1. 项目概述:为什么我们需要深入理解Tamper脚本?
如果你用过SQLMap,大概率遇到过这种情况:对着一个看起来有注入点的URL跑了大半天,结果要么是“所有参数似乎都不注入”,要么是payload发出去就石沉大海,连个错误回显都没有。新手这时候可能就放弃了,但老手会心一笑,默默加上--tamper参数。Tamper脚本,就是SQLMap这套自动化“大杀器”里的“特种兵”,专门负责在payload送上前线之前,对其进行伪装、变形、编码,以绕过各种WAF(Web应用防火墙)、IDS(入侵检测系统)甚至是应用层自己写的蹩脚过滤逻辑。
很多人把Tamper脚本当成一个黑盒魔法,只知道“这个站用space2comment能过”,却不知道背后的原理。这就像你只会按配方做菜,却不懂火候和调味,一旦遇到新奇的防御手段,立马抓瞎。这篇指南的目的,就是带你从“会用”到“懂原理”,再到能根据实际情况“自己改”甚至“自己写”。我们将从最基础的绕过场景开始,拆解经典Tamper脚本的每一行代码,理解其设计思路,最后探讨如何组合使用乃至定制开发高级绕过脚本。无论你是安全测试人员、渗透测试学习者,还是对Web安全防御机制感兴趣的研究者,掌握Tamper脚本的实战精髓,都能让你对SQL注入攻防的理解提升一个维度。
2. Tamper脚本核心原理与工作机制拆解
2.1 SQLMap的Payload生成与处理流水线
要理解Tamper,首先得明白SQLMap是如何工作的。它不是简单地把一堆预定义的payload扔出去。其内部有一个完整的引擎,工作流程大致如下:
- 逻辑判断与指纹识别:SQLMap首先会发送一些无害的探测请求,判断参数是否动态、数据库类型(通过报错信息、特有函数等),并尝试识别WAF。
- Payload库选取:根据数据库类型(MySQL, PostgreSQL, SQL Server, Oracle等)和注入类型(布尔盲注、时间盲注、报错注入、联合查询等),从庞大的内置payload库中选取基础模板。
- Payload渲染:将模板中的占位符(如
[INFERENCE],[SLEEP],[COLUMN])替换为具体的逻辑(如比较子句、延时函数、列名)。 - Tamper处理:这是关键一步。渲染后的原始payload会依次通过用户指定的Tamper脚本(如
--tamper “space2comment,randomcase”)。每个脚本都是一个Python函数,接收原始payload,返回修改后的payload。多个脚本按顺序执行,前一个的输出是后一个的输入。 - 请求发送与结果分析:处理后的payload被嵌入到HTTP请求中发送,SQLMap再根据响应内容(如页面差异、时间延迟、报错信息)来判断注入是否成功,并提取数据。
所以,Tamper脚本的本质是一个Payload变形过滤器。它不关心注入逻辑,只负责对payload的“外观”进行手术,使其能够逃逸防御系统的检测规则。
2.2 常见过滤与绕过思路分类
WAF和过滤逻辑千变万化,但核心思路无非几种,对应的Tamper脚本也围绕这些思路展开:
- 关键字过滤:直接拦截
union,select,from,where,or,and等SQL关键字。- 绕过思路:大小写变形(
UnIoN)、内联注释分割(uni/**/on)、编码(URL编码、HTML实体、十六进制)、等价替换(||替代or)、注释混淆(union/*random*/select)。
- 绕过思路:大小写变形(
- 特殊字符过滤:过滤空格、单引号
'、双引号"、等号=、注释符--、#等。- 绕过思路:
- 空格:用
/**/(多行注释)、+、%0a(换行)、%0d(回车)、%09(制表符)甚至多个空格替代。 - 单引号:用
\'(转义)、%bf%27(宽字节)、chr(39)(字符函数)、0x27(十六进制)替代。 - 等号:用
like、between...and...、>0 and <2等逻辑替代。
- 空格:用
- 绕过思路:
- 语义分析:高级WAF会尝试解析SQL语句结构,拦截拼接后形成威胁的语句,而不仅仅是匹配关键字。
- 绕过思路:通过注释、换行、无效括号等方式破坏WAF的语法解析,例如
union(select(1),2)或union/*!50000select*/ 1,2。MySQL特有的内联注释/*!...*/在这里是神器,因为MySQL会执行其中的内容,而许多WAF会忽略或错误解析。
- 绕过思路:通过注释、换行、无效括号等方式破坏WAF的语法解析,例如
- 签名/正则匹配:基于已知攻击payload的特征库进行匹配。
- 绕过思路:随机化。在关键字中随机插入注释、改变大小写、添加无关参数(如
version()变成version/*foo*/()),使每次请求的payload签名都不同,绕过静态规则库。
- 绕过思路:随机化。在关键字中随机插入注释、改变大小写、添加无关参数(如
实操心得:不要一上来就堆砌Tamper脚本。先用手工测试判断过滤类型。例如,在参数后分别添加
'、and 1=1、and 1=2、union select 1,2,3,观察响应差异。如果'被拦截但and 1=1和and 1=2返回相同页面,说明可能只过滤了引号但没过滤and和=。这个初步判断能极大缩小Tamper脚本的选择范围。
3. 基础Tamper脚本深度解析与实战应用
这一部分,我们挑选几个最常用、最具代表性的基础脚本,逐行分析其代码,理解其适用场景和局限性。
3.1 space2comment.py:空格的经典替身
这是使用频率最高的Tamper脚本之一。它的逻辑非常简单:将payload中的空格字符( )替换为多行注释/**/。
#!/usr/bin/env python # 简化后的核心逻辑 def tamper(payload, **kwargs): retVal = payload if payload: retVal = re.sub(r"\s+", "/**/", payload) # 正则匹配所有空白字符(包括空格、制表符、换行等) return retVal- 作用:绕过对空格的过滤。许多简单的WAF或代码层过滤会直接
str_replace(' ', '')或正则匹配\s+union\s+。 - 实战场景:当你的
union select被拦截,但union/**/select能正常返回时,就应该使用它。 - 注意事项:
- 它替换所有空白字符。有时payload里可能有必要的换行符
\n,也会被替换,这可能破坏某些特定上下文(极少见)。 - 对于更严格的过滤,
/**/本身也可能被列入黑名单。此时需要尝试其他替代品,如space2plus.py(用+替换)或space2hash.py(用#加换行)。
- 它替换所有空白字符。有时payload里可能有必要的换行符
3.2 randomcase.py:随机大小写的迷惑术
这个脚本通过随机化关键字的大小写,来绕过基于大小写敏感匹配的过滤规则。
#!/usr/bin/env python import random def tamper(payload, **kwargs): def process(match): word = match.group('word') return ''.join(random.choice((c.upper, c.lower))() for c in word) # 对每个字符随机选择大写或小写 retVal = payload if payload: retVal = re.sub(r"(?i)\b[A-Z_]+\b", process, retVal) # (?i)忽略大小写,匹配单词边界内的纯字母/下划线串 return retVal- 作用:将
SELECT可能变为SeLeCt、sELecT等,使SELECT这个静态签名失效。 - 实战场景:适用于那些采用简单正则(如
/select/i)进行过滤,但没有进行规范化(统一转小写再比较)的防御系统。 - 注意事项:
- 它只影响“单词”,即被
\b边界包围的字母串。所以version()中的version会被变形,但括号不会。 - 过度随机化可能导致payload长度显著增加(虽然不明显),且每次请求的payload都不同,这在某些需要稳定payload的盲注场景下可能不是最优选择。
- 重要:如果目标系统(如Oracle)对SQL关键字大小写敏感(通常不,但自定义解析器可能),使用此脚本会导致语法错误。
- 它只影响“单词”,即被
3.3 charencode.py 与 charunicodeencode.py:编码的艺术
这两个脚本都涉及编码转换,但目的不同。
charencode.py:对payload中未编码的字符进行一次URL编码(
%xx格式)。union select 1,2->%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%31%2c%32- 适用场景:某些WAF只解码一次URL,或者对原始参数进行关键字检查,但对解码后的内容检查不严。通过手动编码,可以让关键字“隐形”。
- 注意:它使用
urllib.quote,并设置safe="%&",意味着%和&不会被二次编码,避免破坏已编码部分或参数分隔符。
charunicodeencode.py:将字符转换为Unicode编码形式(
%u0075%u006e...或\u0075\u006e...)。union->%u0075%u006e%u0069%u006f%u006e- 适用场景:主要针对一些ASP/ASP.NET环境,它们可能支持这种Unicode表示法,而WAF规则库可能遗漏对此格式的检查。在现代Web应用中效果有限。
避坑指南:使用编码类Tamper时,务必使用
-v 3(或更高)参数运行SQLMap,观察最终发出的payload。你需要确认编码后的payload在目标服务器的上下文中能被正确解码。例如,如果参数放在JSON body里,URL编码可能无效;如果放在application/x-www-form-urlencoded的POST数据中,URL编码是标准操作,通常有效。
3.4 between.py 与 equaltolike.py:操作符的“马甲”
这两个脚本针对的是对比较操作符(>,=,<)的过滤。
between.py:用
BETWEEN ... AND ...语法替换>和=。id > 1->id NOT BETWEEN 0 AND 1(逻辑等价:id > 1)id = 1->id BETWEEN 1 AND 1- 原理:
BETWEEN是范围查询,在语义上可以等价于比较操作,但字符串形式完全不同,能绕过简单的操作符过滤。 - 限制:生成的SQL语句更复杂,可能在某些非常简化的SQL解析场景(如某些ORM框架拼接的查询)中引发问题。
equaltolike.py:将等号
=替换为LIKE。id=1->id LIKE 1- 原理:在绝大多数数据库中,当比较数值或简单字符串时,
=和LIKE(在没有通配符的情况下)功能是等价的。但LIKE这个关键字很少出现在注入攻击的规则集中。 - 注意:如果比较的是字符串,
LIKE是大小写不敏感的(取决于数据库配置),而=可能是大小写敏感的,这可能导致逻辑差异。但在注入探测中,我们通常只关心“真”和“假”的页面差异,这种细微差别通常不影响布尔盲注。
基础脚本组合策略:面对未知过滤,一个安全的起步组合是--tamper “space2comment,randomcase”。先用space2comment解决空格问题,再用randomcase扰乱关键字签名。如果不行,再考虑加入编码类(charencode)或操作符替换类(between)脚本。记住,--tamper参数的脚本顺序就是处理顺序。
4. 高级绕过技巧:脚本组合、定制与WAF对抗实战
当你用遍了内置脚本还是绕不过时,就需要更高级的策略了。这包括理解WAF的工作原理、巧妙组合现有脚本,以及最终手段——自己编写Tamper脚本。
4.1 针对特定WAF的脚本解析
SQLMap内置了一些针对历史版本或特定品牌WAF的脚本,理解它们有助于举一反三。
modsecurityversioned.py / modsecurityzeroversioned.py:这两个是针对ModSecurity(一个开源的WAF模块)的。核心技巧是利用MySQL内联注释。
# modsecurityversioned.py 核心代码片段 def tamper(payload, **kwargs): retVal = payload if payload: postfix = '' for comment in ('#', '--', '/*'): if comment in payload: postfix = payload[payload.find(comment):] # 保留原注释 payload = payload[:payload.find(comment)] # 去掉原注释 break # 在关键字前添加 /*!50000*/ retVal = re.sub(r"(?i)\b(union|select|insert|update|delete|drop|from|where)\b", r"/*!50000\1*/", payload) retVal += postfix return retVal/*!50000*/是MySQL的特性,表示如果数据库版本大于等于5.00.00,就执行注释内的代码。对于WAF,它可能被视为普通注释而忽略,但MySQL会正常解析其中的union、select等关键字。modsecurityzeroversioned.py使用/*!00000*/,原理类似。- 实战意义:这教会我们,利用数据库特性(尤其是MySQL的内联注释)是绕过WAF语义分析的利器。
bluecoat.py:这个脚本做了两件事:1) 在SQL语句后添加随机空白符;2) 将
=替换为LIKE。它模拟了某些特定代理或WAF(如BlueCoat SGOS)的绕过模式。其价值在于展示了尾部填充的思路,添加无关内容可以干扰某些基于固定长度或位置签名的检测。
4.2 脚本组合的进阶策略与顺序考量
Tamper脚本的顺序至关重要,因为前一个脚本的输出是后一个脚本的输入。
- 错误示例:
--tamper “charencode,space2comment”- 你先对整个payload进行了URL编码,空格变成了
%20。然后space2comment脚本寻找的是空格字符 ,找不到%20,所以替换无效。最终payload里依然是%20而不是/**/。
- 你先对整个payload进行了URL编码,空格变成了
- 正确顺序:
--tamper “space2comment,charencode”- 先把空格变成
/**/,然后再对整个字符串进行URL编码,/**/被编码为%2f%2a%2a%2f。这样既绕过了空格过滤,又通过了可能的关键字检查。
- 先把空格变成
一个针对较强过滤的通用组合思路:
- 结构破坏:
randomcomments.py- 在关键字之间插入随机注释,破坏union select这样的连续签名。 - 关键字变形:
randomcase.py或versionedkeywords.py- 改变关键字本身的形式。 - 空格处理:
space2comment.py或space2plus.py- 处理剩余的空格。 - 最终编码:
charencode.py- 对整个payload进行一次“化妆”。
你可以这样使用:--tamper “randomcomments,randomcase,space2comment,charencode”。使用-v 3观察最终payload,看是否符合预期。
4.3 手把手编写自定义Tamper脚本
当内置脚本全部失效,或者遇到非常独特的过滤规则时,自己写Tamper脚本是终极解决方案。一个Tamper脚本就是一个Python文件,放在SQLMap的tamper/目录下即可被识别。
编写步骤与模板:
- 创建文件:在
tamper目录下新建一个.py文件,例如my_bypass.py。 - 编写函数:文件必须包含一个名为
tamper的函数。 - 实现逻辑:在函数内对
payload字符串进行操作。 - 添加依赖:如果需要,可以导入
re(正则)、random等库。
下面是一个实战案例:假设目标网站过滤了select关键字,但不过滤selselectect(即采用简单的str_replace(‘select’, ‘’)过滤,我们可以用双写绕过)。
#!/usr/bin/env python """ Copyright (c) 2006-2024 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ import re from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL # 定义脚本优先级,NORMAL即可 def dependencies(): pass # 这里可以声明依赖关系,通常为空 def tamper(payload, **kwargs): """ 双写绕过简单字符串替换过滤 例如:select -> selselectect,替换后变成 select """ retVal = payload if payload: # 定义要双写的关键字列表 keywords = ['select', 'union', 'from', 'where', 'or', 'and', 'insert', 'update', 'delete', 'drop', 'table'] for keyword in keywords: # 使用正则忽略大小写进行替换 # 注意:替换模式是 ‘s’ -> ‘selsect’,‘e’->‘seleect’... 我们采用简单方法:在关键字中间插入自身 # 更健壮的做法:将 ‘select’ 替换为 ‘sel’ + ‘select’ + ‘ect’ if keyword.lower() in payload.lower(): # 粗略检查是否存在 # 构造双写后的字符串:将 ‘select’ 变成 ‘sel’ + ‘select’ + ‘ect’ doubled = keyword[:len(keyword)//2] + keyword + keyword[len(keyword)//2:] # 使用正则进行不区分大小写的替换 retVal = re.sub(r'(?i)\b' + re.escape(keyword) + r'\b', doubled, retVal) return retVal代码解析:
__priority__:脚本执行的优先级。当多个脚本对同一部分进行修改时,优先级高的后执行(覆盖前面的)。一般保持NORMAL。dependencies():用于声明此脚本运行所需的其他脚本,通常不需要。tamper(payload, **kwargs):核心函数。payload是SQLMap传来的原始payload字符串。我们返回修改后的字符串。- 逻辑:遍历预定义的关键字列表,检查payload中是否存在(忽略大小写)。如果存在,则将其替换为“双写”版本。例如,
select被替换为selselectect。当目标系统执行str_replace(‘select’, ‘’, $input)后,中间的select被移除,两边的sel和ect又拼接成了select,从而绕过过滤。
测试你的脚本:
- 将脚本放入
tamper/目录。 - 使用SQLMap的
-t参数指定一个临时文件来观察payload变化:sqlmap -u “http://test.com?id=1” –tamper “my_bypass” -v 3 –flush-session。查看输出中[PAYLOAD]部分,确认变形是否符合预期。 - 在安全的测试环境(如DVWA、SQLi-Labs)中配置相应的过滤规则,进行实战测试。
高级技巧:利用kwargs:
tamper函数接收**kwargs,其中包含一些有用的上下文信息,例如kwargs.get(‘headers’, {})可以获取HTTP头。你可以编写根据Content-Type或User-Agent动态调整变形策略的智能脚本。例如,当Content-Type为application/json时,可能需要对payload进行JSON字符串转义。
5. 实战问题排查与Tamper脚本调试技巧
即使掌握了所有脚本,实战中依然会碰壁。本章节汇总了常见问题及其排查思路。
5.1 常见失败场景分析与解决思路
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 使用Tamper后,SQLMap报告“所有参数似乎都不注入” | 1. Tamper脚本破坏了payload语法。 2. 变形后的payload触发了更严格的WAF规则。 3. 目标参数本身不存在注入点。 | 1.使用-v 3或-v 4:这是最重要的步骤。仔细观察[PAYLOAD]和[HTTP请求]部分,看最终发出的SQL语句是什么。它是否还是一条合法的SQL?2.简化Tamper:先只用一个最基本的脚本(如 space2comment)测试,逐步增加。3.手动验证:将 -v 3看到的payload手工复制到Burp Suite Repeater中发送,观察服务器响应。确认是WAF拦截(返回403等)还是SQL语法错误(返回500或数据库报错)。4.检查脚本顺序:确保编码类脚本在最后。 |
注入能检测到,但无法提取数据(如--dbs失败) | 1. 某些关键函数或语句被过滤(如select,from,information_schema)。2. Tamper脚本在数据提取阶段未生效或生效方式不对。 | 1.分阶段测试:分别测试布尔盲注(--technique=B)、时间盲注(--technique=T)、报错注入(--technique=E)。可能某种技术能绕过。2.使用 --prefix和--suffix:手动指定注入点前后缀,有时可以绕过对特定上下文语法的过滤。3.检查WAF识别:使用 --identify-waf更准确地识别WAF,然后查找针对该WAF的特定Tamper或技巧。4.尝试 --level和--risk:提高检测等级(--level 3)和风险等级(--risk 3),SQLMap会使用更多、更“冒险”的payload和Tamper组合。 |
| 同一个Tamper脚本,有时成功有时失败 | 1. WAF可能有动态规则或学习模式。 2. 脚本中使用了随机化(如 randomcase),导致某些变形恰好被规则命中。3. 服务器有速率限制或IP临时封锁。 | 1.降低请求频率:使用--delay 1(每秒1请求)和--timeout 30。2.使用代理池:通过 --proxy或--proxy-file轮换IP。3.禁用随机化:如果怀疑是 randomcase导致,换用确定性的变形脚本,如versionedkeywords。4.观察WAF日志模式:如果可能,在测试环境分析WAF日志,看它到底拦截了哪些特征。 |
| 自定义脚本不生效 | 1. 脚本未放在正确的tamper/目录。2. 脚本语法错误。 3. 函数名不是 tamper,或优先级__priority__设置有问题。4. SQLMap缓存了之前的测试结果。 | 1.检查路径:确保脚本在SQLMap根目录的tamper/文件夹内。2.Python语法检查:在命令行运行 python -m py_compile your_script.py检查错误。3.使用 --flush-session:清除缓存,强制SQLMap重新开始测试。4.在脚本开头加 print语句调试(临时):在tamper函数第一行添加print(“My script is called with:”, payload),运行SQLMap时观察控制台输出。 |
5.2 高效调试与信息收集方法论
- 必用参数
-v 3:这是调试Tamper的“眼睛”。它显示每一个测试payload的变形前和变形后状态。 - 结合Burp Suite:
- 将SQLMap的代理设置为Burp (
--proxy=http://127.0.0.1:8080)。 - 在Burp中观察所有请求和响应。重点关注被WAF拦截(返回403、419等状态码或有特定拦截页面)的请求。
- 对比拦截请求和成功请求的payload差异,能精准定位触发规则的“坏字符”或“坏模式”。
- 将SQLMap的代理设置为Burp (
- 使用
--test-filter:如果你怀疑某个特定的payload类型(如>的比较)被过滤,可以用--test-filter只测试包含该字符的payload,快速验证。 - 利用
--string或--not-string:在盲注中,指定一个在真/假条件下会稳定出现在页面中的字符串,可以帮助SQLMap更准确地判断,减少因WAF干扰导致的误判。 - 离线分析模式:使用
-l参数从Burp Suite保存的日志文件中读取目标,可以避免频繁触发WAF的IP封锁,同时仔细分析每个请求。
5.3 超越Tamper:当脚本也无力时
Tamper脚本主要处理payload本身。如果WAF的防御层次更高,可能需要结合其他技巧:
- HTTP参数污染(HPP):
?id=1&id=2,不同的服务器端语言会以不同方式解析,可能绕过参数检查。SQLMap的-prefix和-suffix可以模拟。 - 畸形HTTP请求:修改
Content-Type、使用GET传递POST数据、分块传输编码等。这需要修改SQLMap的lib/request/相关代码或使用其他工具配合。 - 资源限制绕过:如果WAF有请求大小限制,可以尝试使用非常长的参数值,或者将payload拆分到多个参数(如Cookie、Header)中。
- 时序攻击:对于时间盲注,精心设计
SLEEP函数,使其延迟时间不规则,避开基于固定延时阈值的检测。
最终,SQL注入与WAF的对抗是道高一尺魔高一丈的持续过程。Tamper脚本是自动化工具中的重要一环,但真正的突破往往来自于对目标系统、数据库特性、WAF规则乃至HTTP协议的深刻理解。保持学习,手动测试,分析流量,你才能从脚本的使用者,成长为绕过规则的创造者。