1. 项目概述为什么我们需要深入理解Tamper脚本如果你用过SQLMap大概率遇到过这种情况对着一个看起来有注入点的URL跑了大半天结果要么是“所有参数似乎都不注入”要么是payload发出去就石沉大海连个错误回显都没有。新手这时候可能就放弃了但老手会心一笑默默加上--tamper参数。Tamper脚本就是SQLMap这套自动化“大杀器”里的“特种兵”专门负责在payload送上前线之前对其进行伪装、变形、编码以绕过各种WAFWeb应用防火墙、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会忽略或错误解析。签名/正则匹配基于已知攻击payload的特征库进行匹配。绕过思路随机化。在关键字中随机插入注释、改变大小写、添加无关参数如version()变成version/*foo*/()使每次请求的payload签名都不同绕过静态规则库。实操心得不要一上来就堆砌Tamper脚本。先用手工测试判断过滤类型。例如在参数后分别添加、and 11、and 12、union select 1,2,3观察响应差异。如果被拦截但and 11和and 12返回相同页面说明可能只过滤了引号但没过滤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( , )或正则匹配\sunion\s。实战场景当你的union select被拦截但union/**/select能正常返回时就应该使用它。注意事项它替换所有空白字符。有时payload里可能有必要的换行符\n也会被替换这可能破坏某些特定上下文极少见。对于更严格的过滤/**/本身也可能被列入黑名单。此时需要尝试其他替代品如space2plus.py用替换或space2hash.py用#加换行。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。id1-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而不是/**/。正确顺序--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?id1” –tamper “my_bypass” -v 3 –flush-session。查看输出中[PAYLOAD]部分确认变形是否符合预期。在安全的测试环境如DVWA、SQLi-Labs中配置相应的过滤规则进行实战测试。高级技巧利用kwargstamper函数接收**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语句是什么。它是否还是一条合法的SQL2.简化Tamper先只用一个最基本的脚本如space2comment测试逐步增加。3.手动验证将-v 3看到的payload手工复制到Burp Suite Repeater中发送观察服务器响应。确认是WAF拦截返回403等还是SQL语法错误返回500或数据库报错。4.检查脚本顺序确保编码类脚本在最后。注入能检测到但无法提取数据如--dbs失败1. 某些关键函数或语句被过滤如select,from,information_schema。2. Tamper脚本在数据提取阶段未生效或生效方式不对。1.分阶段测试分别测试布尔盲注(--techniqueB)、时间盲注(--techniqueT)、报错注入(--techniqueE)。可能某种技术能绕过。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 (--proxyhttp://127.0.0.1:8080)。在Burp中观察所有请求和响应。重点关注被WAF拦截返回403、419等状态码或有特定拦截页面的请求。对比拦截请求和成功请求的payload差异能精准定位触发规则的“坏字符”或“坏模式”。使用--test-filter如果你怀疑某个特定的payload类型如的比较被过滤可以用--test-filter只测试包含该字符的payload快速验证。利用--string或--not-string在盲注中指定一个在真/假条件下会稳定出现在页面中的字符串可以帮助SQLMap更准确地判断减少因WAF干扰导致的误判。离线分析模式使用-l参数从Burp Suite保存的日志文件中读取目标可以避免频繁触发WAF的IP封锁同时仔细分析每个请求。5.3 超越Tamper当脚本也无力时Tamper脚本主要处理payload本身。如果WAF的防御层次更高可能需要结合其他技巧HTTP参数污染HPP?id1id2不同的服务器端语言会以不同方式解析可能绕过参数检查。SQLMap的-prefix和-suffix可以模拟。畸形HTTP请求修改Content-Type、使用GET传递POST数据、分块传输编码等。这需要修改SQLMap的lib/request/相关代码或使用其他工具配合。资源限制绕过如果WAF有请求大小限制可以尝试使用非常长的参数值或者将payload拆分到多个参数如Cookie、Header中。时序攻击对于时间盲注精心设计SLEEP函数使其延迟时间不规则避开基于固定延时阈值的检测。最终SQL注入与WAF的对抗是道高一尺魔高一丈的持续过程。Tamper脚本是自动化工具中的重要一环但真正的突破往往来自于对目标系统、数据库特性、WAF规则乃至HTTP协议的深刻理解。保持学习手动测试分析流量你才能从脚本的使用者成长为绕过规则的创造者。