更新: 359 个文件 - 2026-03-16 23:30:01

这个提交包含在:
hao
2026-03-16 23:30:01 -07:00
父节点 527990f535
当前提交 2974cd9ad9
修改 359 个文件,包含 6332 行新增673 行删除

查看文件

@@ -26,6 +26,23 @@ import re
import urllib.parse
from typing import List, Dict, Tuple, Optional
import time
import sys
from pathlib import Path
SCRIPTS_DIR = Path(__file__).resolve().parents[2] / "scripts"
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
from tool_contract import ( # noqa: E402
add_common_args,
emit_report,
ensure_authorized,
make_report,
parse_cookie_string,
parse_headers,
write_evidence,
)
class Colors:
@@ -345,16 +362,19 @@ def main():
"--all-categories", action="store_true", help="测试所有Payload类别"
)
parser.add_argument("--timeout", type=int, default=10, help="超时时间")
add_common_args(parser)
args = parser.parse_args()
ensure_authorized(args, parser)
requests.packages.urllib3.disable_warnings()
fuzzer = XSSFuzzer(timeout=args.timeout)
print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}")
print(f"{Colors.BOLD}XSS Fuzzer{Colors.END}")
print(f"{Colors.BOLD}{'=' * 60}{Colors.END}\n")
fuzzer.session.headers.update(parse_headers(args.header))
if args.proxy:
fuzzer.session.proxies.update({"http": args.proxy, "https": args.proxy})
if args.format != "text":
fuzzer.print_result = lambda *_args, **_kwargs: None # type: ignore[assignment]
data = {}
if args.data:
@@ -363,13 +383,9 @@ def main():
k, v = pair.split("=", 1)
data[k] = v
cookies = {}
if args.cookie:
for pair in args.cookie.split(";"):
if "=" in pair:
k, v = pair.strip().split("=", 1)
cookies[k] = v
cookies = parse_cookie_string(args.cookie)
csp_result = {"has_csp": False, "weaknesses": []}
if args.check_csp:
fuzzer.print_result("INFO", "检查 CSP 策略...")
csp_result = fuzzer.check_csp(args.url, cookies)
@@ -384,6 +400,7 @@ def main():
for w in csp_result["weaknesses"]:
fuzzer.print_result("WARNING", f" - {w}")
dom_results = []
if args.dom_scan:
fuzzer.print_result("INFO", "扫描 DOM XSS...")
dom_results = fuzzer.scan_dom_xss(args.url, cookies)
@@ -396,18 +413,58 @@ def main():
fuzzer.print_result("INFO", "上下文分析:")
for ctx, status in context.items():
color = Colors.YELLOW if status == "未过滤" else Colors.GREEN
print(f" {color}{ctx}: {status}{Colors.END}")
if args.format == "text":
print(f" {color}{ctx}: {status}{Colors.END}")
results = fuzzer.test_reflected(args.url, args.param, args.method, data, cookies)
print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}")
if results:
fuzzer.print_result("SUCCESS", f"发现 {len(results)} 个 XSS 漏洞!")
for r in results:
print(f" - [{r['category']}] {r['param']}: {r['payload'][:60]}...")
else:
fuzzer.print_result("INFO", "未发现反射型 XSS 漏洞")
print(f"{Colors.BOLD}{'=' * 60}{Colors.END}\n")
evidence_refs = []
for name, payload in [
("xss-context.json", context),
("xss-reflected.json", results),
("xss-dom.json", dom_results),
("xss-csp.json", csp_result),
]:
ref = write_evidence(args, name, payload)
if ref:
evidence_refs.append(ref)
status = "verified" if results else "suspected" if dom_results or csp_result.get("weaknesses") else "needs-review"
severity = "high" if results else "medium" if dom_results else "low" if csp_result.get("weaknesses") else "info"
report = make_report(
tool="xss-fuzzer",
mode="dom-and-reflected-xss",
target=args.url,
status=status,
severity=severity,
payload_or_probe={
"reflected_hits": results,
"dom_hits": dom_results,
"context": context,
"csp": csp_result,
},
request_summary={
"method": args.method,
"param": args.param,
"has_body_template": bool(args.data),
"header_names": sorted(parse_headers(args.header).keys()),
},
evidence_refs=evidence_refs,
destructive_risk="low",
args=args,
)
text_lines = [
"=" * 60,
"XSS Fuzzer",
"=" * 60,
f"Target: {args.url}",
f"Method: {args.method}",
f"Param: {args.param}",
f"Reflected Hits: {len(results)}",
f"DOM Findings: {len(dom_results)}",
f"CSP Weaknesses: {len(csp_result.get('weaknesses', []))}",
f"Status: {status}",
]
emit_report(args, report, text_lines)
if __name__ == "__main__":

查看文件

@@ -7,11 +7,13 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"sync"
@@ -30,6 +32,9 @@ type XSSScanner struct {
Threads int
Timeout time.Duration
Payloads map[string][]string
Headers map[string]string
Cookie string
Quiet bool
}
var (
@@ -52,6 +57,7 @@ func NewXSSScanner(threads int, timeout time.Duration) *XSSScanner {
},
Threads: threads,
Timeout: timeout,
Headers: map[string]string{},
Payloads: map[string][]string{
"basic": {
"<script>alert(1)</script>",
@@ -110,6 +116,12 @@ func (s *XSSScanner) SendRequest(targetURL, method, param, payload string) (stri
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
for k, v := range s.Headers {
req.Header.Set(k, v)
}
if s.Cookie != "" {
req.Header.Set("Cookie", s.Cookie)
}
resp, err := s.Client.Do(req)
if err != nil {
@@ -150,8 +162,10 @@ func (s *XSSScanner) ScanURL(targetURL, method, param string) []XSSResult {
Category: cat,
})
mu.Unlock()
fmt.Printf("%s[VULN]%s [%s] %s - %s\n",
colorRed+colorBold, colorEnd, cat, param, p[:min(50, len(p))])
if !s.Quiet {
fmt.Printf("%s[VULN]%s [%s] %s - %s\n",
colorRed+colorBold, colorEnd, cat, param, p[:min(50, len(p))])
}
}
}(category, payload)
}
@@ -234,6 +248,21 @@ func min(a, b int) int {
return b
}
func parseHeaders(raw string) map[string]string {
headers := map[string]string{}
if raw == "" {
return headers
}
for _, part := range strings.Split(raw, ",") {
pair := strings.SplitN(part, ":", 2)
if len(pair) != 2 {
continue
}
headers[strings.TrimSpace(pair[0])] = strings.TrimSpace(pair[1])
}
return headers
}
func main() {
target := flag.String("u", "", "Target URL")
method := flag.String("m", "GET", "HTTP Method (GET/POST)")
@@ -242,53 +271,105 @@ func main() {
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")
header := flag.String("header", "", "Extra headers in Name:Value,Name2:Value2 format")
cookie := flag.String("cookie", "", "Cookie header value")
format := flag.String("format", "text", "Output format: text or json")
output := flag.String("output", "", "Write output to file")
evidenceDir := flag.String("evidence-dir", "", "Optional evidence directory")
runID := flag.String("run-id", "", "Associated run ID")
caseID := flag.String("case-id", "", "Associated case ID")
ackAuthorized := flag.Bool("ack-authorized", false, "Confirm the target is owned or authorized")
flag.Parse()
if *target == "" {
if *target == "" || !*ackAuthorized {
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)
scanner.Headers = parseHeaders(*header)
scanner.Cookie = *cookie
scanner.Quiet = *format != "text"
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)
cspResult := map[string]interface{}{"has_csp": false, "weaknesses": []string{}}
if *checkCSP {
fmt.Printf("\n%s[*]%s Checking CSP...\n", colorCyan, colorEnd)
cspResult := scanner.CheckCSP(*target)
if cspResult["has_csp"].(bool) {
cspResult = scanner.CheckCSP(*target)
if *format == "text" && 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 {
} else if *format == "text" {
fmt.Printf("%s[-]%s No CSP configured!\n", colorYellow, colorEnd)
}
}
domResults := []map[string]string{}
if *domScan {
fmt.Printf("\n%s[*]%s Scanning for DOM XSS...\n", colorCyan, colorEnd)
domResults := scanner.ScanDOMXSS(*target)
domResults = scanner.ScanDOMXSS(*target)
if *format == "text" {
fmt.Printf("\n%s[*]%s Scanning for DOM XSS...\n", colorCyan, colorEnd)
}
for _, r := range domResults {
if *format != "text" {
continue
}
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))])
report := map[string]interface{}{
"tool": "xss-scanner-go",
"mode": "bulk-reflected-xss",
"target": *target,
"status": "needs-review",
"severity": "info",
"timestamp": time.Now().UTC().Format(time.RFC3339),
"request_summary": map[string]interface{}{"method": *method, "param": *param, "threads": *threads},
"payload_or_probe": map[string]interface{}{"reflected_hits": results, "dom_hits": domResults, "csp": cspResult},
"evidence_refs": []string{},
"minimal_validation": "只读探测、最小化注入、可审计回显、可回滚验证。",
"authorization_scope": "lab-local, lab-public, authorized-third-party",
"destructive_risk": "low",
"run_id": *runID,
"case_id": *caseID,
}
fmt.Printf("%s%s%s\n\n", colorBold, strings.Repeat("=", 60), colorEnd)
if len(results) > 0 {
report["status"] = "verified"
report["severity"] = "high"
} else if len(domResults) > 0 {
report["status"] = "suspected"
report["severity"] = "medium"
}
if *evidenceDir != "" {
_ = os.MkdirAll(*evidenceDir, 0o755)
evidencePath := *evidenceDir + "/xss-scanner-go.json"
if raw, err := json.MarshalIndent(report, "", " "); err == nil {
_ = os.WriteFile(evidencePath, append(raw, '\n'), 0o644)
report["evidence_refs"] = append(report["evidence_refs"].([]string), evidencePath)
}
}
var content []byte
if *format == "json" {
content, _ = json.MarshalIndent(report, "", " ")
} else {
text := []string{
strings.Repeat("=", 60),
"XSS Scanner (Go)",
strings.Repeat("=", 60),
"Target: " + *target,
"Method: " + *method,
fmt.Sprintf("Reflected Hits: %d", len(results)),
fmt.Sprintf("DOM Findings: %d", len(domResults)),
"Status: " + report["status"].(string),
}
content = []byte(strings.Join(text, "\n"))
}
if *output != "" {
_ = os.WriteFile(*output, append(content, '\n'), 0o644)
}
fmt.Println(string(content))
}