CTFshow SQL注入实战:sqlmap在GET、POST、PUT请求中的高级用法与Tamper脚本绕过 1. 项目概述最近在CTFshow的Web入门系列里SQL注入的实战题目从201题开始画风突然一转从之前的手工注入、脚本盲注直接进入了sqlmap的实战应用环节。特别是从201到213题几乎每一道题都在引导你使用不同的HTTP方法GET、POST、PUT和sqlmap的各种参数来绕过各种限制最终拿到flag。这其实是一个非常贴近实战的练习场景因为在真实的渗透测试或者CTF比赛中你遇到的注入点可能藏在各种奇奇怪怪的HTTP请求里而不仅仅是简单的?id1。很多刚接触sqlmap的朋友可能只会用最基本的-u参数去跑GET请求一旦遇到POST表单、PUT接口或者需要带上特定的Header、Cookie、Referer甚至需要先访问一个页面获取动态Token时就有点手足无措了。CTFshow的这一系列题目正好系统性地覆盖了这些场景。我花了点时间把这十几道题从头到尾捋了一遍把其中涉及到的5种主要HTTP请求方法GET、POST、PUT以及带Cookie和带动态Token的PUT下的sqlmap使用技巧结合我踩过的坑整理成了这篇实战解析。无论你是想系统学习sqlmap还是正在卡在CTFshow的某个关卡相信这篇“避坑指南”都能给你带来直接的帮助。2. 核心思路与场景拆解这十几道题201-213的核心逻辑其实一脉相承目标都是一个存在SQL注入漏洞的API接口你需要使用sqlmap来自动化地探测并利用这个漏洞最终dump出ctfshow_web数据库里某个特定表通常是ctfshow_flax、ctfshow_flaxc等变体中的flag字段。题目的演进主要体现在HTTP请求的复杂程度和附加的绕过需求上。我们可以把整个攻击流程抽象为几个核心环节而每个环节的差异就构成了不同题目的考点请求构造这是最基础的一步。你的sqlmap命令必须能“模拟”出浏览器正常访问该接口时的HTTP请求。这包括了请求方法 (Method)是GET、POST还是PUT请求参数 (Parameters)参数名是什么是放在URL里GET还是请求体里POST/PUT请求头 (Headers)是否需要特定的Content-Type如text/plain是否需要User-Agent、Referer会话维持 (Session)是否需要携带固定的Cookie还是需要先访问另一个接口获取动态的Token/Cookie漏洞探测与利用在正确构造请求的基础上sqlmap才能开始它的本职工作——检测注入点、识别数据库类型、枚举数据。这部分通常使用--dbs,--tables,--columns,--dump等参数。绕过处理 (WAF/Tamper)从207题开始题目引入了简单的过滤如空格这就需要我们使用sqlmap的--tamper参数来对payload进行编码或变形以绕过过滤。整个系列的难度是层层递进的非常像一份精心设计的sqlmap实战教程。下面我们就按照HTTP方法的分类结合具体题目来详细拆解每一种场景下的sqlmap使用技巧和避坑要点。3. 基础GET与POST请求注入这是sqlmap最经典的用法也是201和202题考察的内容。虽然基础但却是理解后续复杂场景的基石。3.1 Web 201: 基础GET请求与常用参数这一题是纯粹的GET请求注入没有任何额外的Header或Cookie要求。但它引入了两个关键参数--user-agent和--referer。题目要点接口URL为http://xxx.challenge.ctf.show/api/?id1后端会检查User-Agent头必须为sqlmap。后端会检查Referer头必须为ctf.show。sqlmap命令解析 我们以获取数据库列表为例完整的命令如下sqlmap -u “http://xxx.challenge.ctf.show/api/?id1” --user-agentsqlmap --refererctf.show --dbs-u “URL”: 指定目标URL这是sqlmap最基础的参数。--user-agentsqlmap: 设置HTTP请求头中的User-Agent字段。这里题目要求必须为sqlmap否则请求会被拒绝。避坑点有些WAF或检查点会拦截默认的sqlmap UA此时需要将其改为常见的浏览器UA来伪装。--refererctf.show: 设置HTTP请求头中的Referer字段。Referer通常用于表示请求的来源页面这里也是题目要求的固定值。--dbs: 枚举数据库。这是信息收集的第一步成功后通常会看到ctfshow_web。后续操作链 获取数据库名后我们需要一步步深入获取表名、列名最后dump数据。这是一套标准的“连招”爆表名指定数据库枚举其中的表。sqlmap -u “http://xxx.challenge.ctf.show/api/?id1” --user-agentsqlmap --refererctf.show -D ctfshow_web --tables-D ctfshow_web: 指定目标数据库。--tables: 枚举该数据库中的所有表。这里通常会找到ctfshow_user和ctfshow_flax或其变体等表。爆列名指定数据库和表枚举表中的列。sqlmap -u “http://xxx.challenge.ctf.show/api/?id1” --user-agentsqlmap --refererctf.show -D ctfshow_web -T ctfshow_flax --columns-T ctfshow_flax: 指定目标表。--columns: 枚举该表的所有列。这里的目标是找到存储flag的列如flagx。爆数据 (Dump)指定数据库、表和列直接导出数据。sqlmap -u “http://xxx.challenge.ctf.show/api/?id1” --user-agentsqlmap --refererctf.show -D ctfshow_web -T ctfshow_flax -C flagx --dump-C flagx: 指定目标列。--dump: 导出该列的所有数据。成功执行后flag就直接显示在终端里了。实操心得--dbs,--tables,--columns,--dump这四个参数是sqlmap信息收集的“四件套”它们的顺序和使用逻辑需要牢记。在实战中如果对目标结构不熟悉最好按这个顺序来避免盲目dump大量无关数据。3.2 Web 202: 处理POST请求 (--data)从202题开始注入点变成了POST请求。这意味着参数不再以?id1的形式出现在URL中而是放在HTTP请求体里。题目要点接口URL变为https://xxx.challenge.ctf.show/api/(注意没有?id1)请求方法为POST参数通过请求体传递格式为id1。同样需要--user-agent和--referer。sqlmap命令解析 关键的变化在于--data参数。sqlmap -u “https://xxx.challenge.ctf.show/api/” --data“id1” --user-agentsqlmap --refererctf.show --dbs--data“id1”: 这是核心。它告诉sqlmap这是一个POST请求并且请求体中的数据是id1。sqlmap会尝试在这个参数id上寻找注入点。此时的-u参数只需要提供基础URL即可无需附带参数。后续操作链 后续的-D,-T,-C,--dump等参数用法完全不变只需要在基础命令后追加即可。例如爆表sqlmap -u “https://xxx.challenge.ctf.show/api/” --data“id1” --user-agentsqlmap --refererctf.show -D ctfshow_web --tables避坑指南很多新手在这里会犯一个错误仍然在URL里写?id1同时又使用了--data。这样sqlmap可能会混淆导致探测失败。正确的做法是对于POST请求-u只写路径参数全部通过--data指定。4. 进阶PUT请求与Header处理203题和204题将难度提升了一个等级引入了PUT方法和Cookie处理这更贴近一些RESTful API或需要认证的接口场景。4.1 Web 203: 处理PUT请求 (--method)PUT方法常用于更新资源在Web API中也很常见。sqlmap默认使用GET和POST对于PUT需要显式指定。题目要点接口URLhttps://xxx.challenge.ctf.show/api/index.php(注意这里需要index.php而前两题直接/api/即可这是一个小坑)。请求方法为PUT。参数仍然在请求体中id1。需要设置Content-Type: text/plain请求头。同样需要--user-agent和--referer。sqlmap命令解析 这里引入了两个新参数--method和--headers。sqlmap -u “https://xxx.challenge.ctf.show/api/index.php” --methodPUT --data“id1” --user-agentsqlmap --refererctf.show --headers“Content-Type:text/plain” --dbs--methodPUT: 强制指定HTTP请求方法为PUT。--headers“Content-Type:text/plain”: 设置额外的HTTP请求头。多个头可以用\n分隔但这里一个就够了。注意Content-Type告诉服务器请求体的格式。虽然id1看起来像表单数据(application/x-www-form-urlencoded)但题目要求text/plain就必须遵守否则服务器可能无法正确解析。后续操作链 同样后续操作只需追加参数。例如爆表sqlmap -u “https://xxx.challenge.ctf.show/api/index.php” --methodPUT --data“id1” --user-agentsqlmap --refererctf.show --headers“Content-Type:text/plain” -D ctfshow_web --tables常见问题为什么203题需要/api/index.php而201、202题只需要/api/这通常是因为服务器的路由配置导致的。/api/可能被重写到/api/index.php但某些检查如PUT方法的处理可能只在具体的脚本文件上生效。经验是如果直接跑/api/不成功可以尝试加上/index.php或常见的入口文件。4.2 Web 204: 处理Cookie (--cookie)204题在203题的基础上增加了对Cookie的校验。这意味着你的请求必须在一个有效的会话上下文中进行。题目要点在203题所有要求的基础上新增需要携带一个特定的Cookie例如PHPSESSID8tabnpij74cu06necss480g05q。sqlmap命令解析 使用--cookie参数来附加Cookie。sqlmap -u “https://xxx.challenge.ctf.show/api/index.php” --methodPUT --data“id1” --user-agentsqlmap --refererctf.show --headers“Content-Type:text/plain” --cookie“PHPSESSID8tabnpij74cu06necss480g05q” --dbs--cookie“NAMEVALUE”: 设置请求的Cookie头。如果有多个Cookie可以用分号分隔如--cookie“PHPSESSIDxxx; ctfshowyyy”。如何获取Cookie题目通常不会直接给你。你需要用浏览器正常访问题目页面。按F12打开开发者工具切换到“网络”(Network)标签页。刷新页面或进行任何操作捕获到对目标接口如/api/index.php的请求。查看该请求的“请求头”(Headers)找到Cookie:那一行复制其值。避坑指南Cookie是有时效性的。在CTF题目中通常一个靶机实例的Cookie是固定的。但在真实环境中Cookie可能会过期或被重置。如果sqlmap跑着跑着突然失败了提示会话无效记得重新获取Cookie并更新命令。另外--cookie和--headers“Cookie: ...”的效果是等价的但--cookie更专用、更方便。5. 动态Token与请求预处理 (--safe-url--safe-freq)205题和206题引入了动态Token机制这是对抗自动化工具的一种常见防御手段。sqlmap提供了--safe-url和--safe-freq参数来优雅地处理这种情况。5.1 Web 205: 处理动态Token题目要点接口和204题类似PUT方法需要Header和Cookie。但是Cookie是动态变化的每次访问/api/getToken.php都会获得一个新的Cookie。直接使用固定的Cookie访问/api/index.php会失败。核心思路 我们需要让sqlmap在每次发送注入探测请求之前先访问一次/api/getToken.php来获取最新的Cookie然后用这个新Cookie去访问目标接口。sqlmap命令解析 使用--safe-url和--safe-freq参数。sqlmap -u “https://xxx.challenge.ctf.show/api/index.php” --methodPUT --data“id1” --user-agentsqlmap --refererctf.show --headers“Content-Type:text/plain” --safe-url“https://xxx.challenge.ctf.show/api/getToken.php” --safe-freq1 --dbs--safe-url“URL”: 指定一个“安全”的URL。sqlmap在探测过程中会定期访问这个URL。这个URL的响应中通常包含了下一步请求所需的Token或Cookie通过Set-Cookie头。--safe-freq1: 指定频率。1表示每发送1次对目标URL(-u)的测试请求后就访问1次安全URL(--safe-url)。这个频率需要根据实际情况调整。如果Token是每次请求都变那就设为1如果是每N次请求变一次可以设为N。sqlmap的工作流程访问--safe-url(即/api/getToken.php)。从这次访问的响应中提取Cookie或其他Session信息。使用这个新的Cookie访问目标URL (-u) 并发送测试payload。由于--safe-freq1在发送下一次测试请求前又会回到步骤1获取最新的Cookie。如此循环确保每次注入尝试都在有效的会话中。后续操作链 同样在基础命令后追加信息收集参数即可。例如这次flag可能在ctfshow_flax表的flagx列sqlmap -u “...” --methodPUT --data“id1” ... --safe-url“...” --safe-freq1 -D ctfshow_web -T ctfshow_flax -C flagx --dump实操心得--safe-url和--safe-freq是处理动态会话的利器。关键点在于观察你需要先手动用浏览器或Burp Suite抓包弄清楚Token/Cookie的获取流程是哪个接口返回的在响应头还是响应体里以及它的更新频率。然后才能正确配置这两个参数。5.2 Web 206: SQL闭合与--suffix206题在205题的基础上增加了一个小变化SQL语句需要闭合。虽然sqlmap通常能自动检测和闭合但有时手动指定更可靠。题目要点所有条件同205题PUT动态Token。SQL语句可能是... id ‘$_GET[‘id’]‘这样的字符型注入需要闭合单引号。sqlmap命令解析 我们可以使用--suffix参数在payload后面添加固定的后缀比如注释符来确保闭合。sqlmap -u “...” --methodPUT --data“id1” ... --safe-url“...” --safe-freq1 --suffix“)#” --dbs--suffix“)#”: 在所有payload的末尾添加字符串)#。)闭合前面的单引号和括号如果存在。#MySQL的注释符注释掉原SQL语句后面的部分。你也可以使用--prefix在payload前面添加字符串或者两者结合使用。为什么需要这个虽然sqlmap的智能检测--level和--risk调高也能处理闭合但在某些过滤严格或结构特殊的场景下手动指定--prefix和--suffix可以大大提高成功率并减少无效payload的测试节省时间。避坑指南不要滥用--suffix。首先让sqlmap自动检测--level 2以上如果发现它无法正确识别注入点例如一直报错“所有参数似乎都不易受SQL注入攻击”再考虑手动分析SQL语句结构使用Burp Suite手动测试一个成功的payload观察其闭合方式然后用--prefix/--suffix来模拟。6. 使用Tamper脚本绕过过滤从207题开始题目引入了简单的WAFWeb应用防火墙规则过滤了空格等字符。这就需要用到sqlmap的另一个强大功能Tamper脚本。6.1 Web 207: 初识Tamper (--tamper)题目要点在205/206题所有要求的基础上新增过滤空格被过滤。sqlmap生成的payload中如果包含空格会被拦截。解决方案使用space2commentTamper脚本。sqlmap -u “...” --methodPUT --data“id1” ... --safe-url“...” --safe-freq1 --tamperspace2comment --dbs--tamperspace2comment: 调用space2comment.py脚本。这个脚本的作用是将payload中的空格( )替换成MySQL的注释/**/。例如UNION SELECT 1,2,3会变成UNION/**/SELECT/**/1,2,3从而绕过对空格的过滤。Tamper脚本是什么Tamper是sqlmap的payload转换脚本位于/usr/share/sqlmap/tamper/目录下Kali Linux默认位置。它们可以在payload发送前对其进行编码、混淆、替换等操作以绕过特定的过滤规则。常用Tamper脚本速查space2comment: 空格 -/**/space2plus: 空格 -space2randomblank: 空格 - 随机空白字符如%09,%0A,%0C,%0Dbetween: 用BETWEEN ... AND ...替换和比较符。charencode: URL编码。randomcase: 随机大小写。equaltolike:-LIKE实操心得--tamper是绕过WAF的利器但跑得慢是它的代价。因为每个payload都要经过脚本处理并且可能产生更多变体。在时间紧张的CTF比赛中如果手工注入可行有时效率更高。但对于自动化或复杂过滤Tamper是不可或缺的。6.2 Web 208-212: 组合Tamper与自定义脚本208题之后过滤规则变得更加复杂单一的Tamper脚本可能不够用需要组合使用甚至自己编写。Web 208: 过滤了select不区分大小写。但sqlmap默认生成的payload中SELECT是大写的而题目可能只过滤了小写select。所以直接用207题的space2comment脚本依然有效因为SELECT没有被过滤。这提醒我们有时过滤规则写得并不严谨。Web 209: 过滤了空格和等号()。space2comment用不了因为注释/**/里包含*可能也被过滤题目实际是过滤了*。这里需要自定义Tamper脚本将空格替换为Tab(%09)将等号()替换为LIKE。自定义Tamper脚本示例 (show-web209.py):#!/usr/bin/env python from lib.core.enums import PRIORITY __priority__ PRIORITY.NORMAL def dependencies(): pass def tamper(payload, **kwargs): # 将等号替换为 LIKE将空格替换为 Tab (0x09) return payload.replace(““, “ like “).replace(“ “, chr(0x09))使用--tampershow-web209.pyWeb 210 211: 题目对输入进行了编码/加密strrev(base64_decode(strrev(base64_decode($id))))。我们需要写一个Tamper脚本对sqlmap生成的payload进行逆向处理使得发送出去的payload被服务器解码后正好是我们想要的SQL语句。自定义编码Tamper脚本示例 (show-web210.py):#!/usr/bin/env python import base64 from lib.core.enums import PRIORITY __priority__ PRIORITY.NORMAL def dependencies(): pass def tamper(payload, **kwargs): # 服务器处理流程解码 - 反转 - 解码 - 反转 # 所以我们的Tamper需要反转 - 编码 - 反转 - 编码 return base64.b64encode(base64.b64encode(payload[::-1].encode()).decode()[::-1].encode()).decode()Web 211: 在210题基础上又过滤了空格。这时需要组合使用多个Tamper脚本先space2comment处理空格再show-web210进行编码。--tamperspace2comment.py,show-web210.py注意顺序sqlmap会按照你提供的顺序依次应用Tamper脚本。这里先处理空格转换再进行整体编码。Web 212: 过滤了空格和*space2comment彻底失效。需要写一个综合脚本将空格替换为Tab(%09)同时进行210题的编码。def tamper(payload, **kwargs): retVal payload if payload: retVal retVal.replace(‘ ‘, chr(0x09)) # 空格替换为Tab retVal base64.b64encode(base64.b64encode(retVal[::-1].encode())[::-1]).decode() # 双重编码 return retVal避坑指南编写Tamper脚本时务必先在本地用Python测试逻辑是否正确。可以模拟服务器的解码过程看你的脚本输出被解码后是否等于原始payload。顺序很重要如果先编码再替换空格被编码后的空格可能无法被识别和替换。7. 实战命令全表与避坑总结为了方便查阅我将CTFshow Web 201-213题的核心sqlmap命令和要点整理成下表。你可以把它当作一个速查手册。题目HTTP方法关键参数/脚本核心命令示例 (以枚举数据库为例)主要考点与避坑点201GET--user-agent,--referersqlmap -u “URL?id1” --user-agentsqlmap --refererctf.show --dbs基础GET注入注意UA和Referer检查。202POST--datasqlmap -u “URL/api/” --data“id1” --user-agentsqlmap --refererctf.show --dbsPOST请求参数在body中-u后不跟?。203PUT--method,--headerssqlmap -u “URL/api/index.php” --methodPUT --data“id1” --headers“Content-Type:text/plain” ... --dbs指定PUT方法及Content-Type头。注意URL可能需要完整路径(index.php)。204PUT--cookie在203基础上加--cookie“PHPSESSIDxxx”处理固定Cookie从浏览器开发者工具获取。205PUT--safe-url,--safe-freq在204基础上加--safe-url“URL/getToken.php” --safe-freq1处理动态Token/Cookie让sqlmap每次请求前先获取新会话。206PUT--suffix(可选)同205可加--suffix“)#”辅助闭合SQL语句闭合sqlmap通常可自动检测手动指定更稳妥。207PUT--tamperspace2comment在205基础上加--tamperspace2comment使用Tamper脚本绕过空格过滤空格-/**/。速度会变慢。208PUT(同207)同207过滤select(小写)但sqlmap用大写SELECT故207脚本仍有效。209PUT自定义Tamper (替换, 空格)--tampershow-web209.py过滤空格和需自定义脚本将换LIKE空格换Tab。210PUT自定义Tamper (编码)--tampershow-web210.py输入被双重base64反转编码Tamper需逆向此过程。211PUT组合Tamper--tamperspace2comment.py,show-web210.py综合210的编码和空格过滤需按顺序应用两个Tamper脚本。212PUT自定义Tamper (编码空格替换)--tampershow-web212.py过滤空格和*需在编码脚本中集成Tab替换空格。213PUT(同212) --os-shell同212最后--os-shell考察sqlmap的--os-shell功能用于在获取权限后执行系统命令。8. 高频问题与排查技巧实录在实际操作这一系列题目时我遇到了不少坑。这里把最常见的问题和解决方法记录下来希望能帮你节省时间。问题1sqlmap跑不出来一直提示“所有参数似乎都不易受SQL注入攻击”。可能原因1请求方法不对。仔细检查题目要求是GET、POST还是PUT。用Burp Suite抓包确认是最可靠的方法。可能原因2Header或Cookie缺失/错误。特别是Content-Type、User-Agent、Referer。用--headers参数设置或用--cookie设置Cookie。对于动态Token务必使用--safe-url和--safe-freq。可能原因3URL路径不对。比如该加index.php的没加。尝试访问/api/和/api/index.php看哪个能正常响应。可能原因4参数位置不对。POST/PUT请求的参数要用--data指定不要写在URL里。排查步骤首先用curl或Burp Suite手动构造一个正常的请求不带注入payload确保能收到正确响应。这能验证你的基础请求方法、头、参数、URL是否正确。然后在这个正常请求的基础上用sqlmap的-r参数。将Burp抓到的完整HTTP请求保存到一个文件如req.txt然后运行sqlmap -r req.txt。这样sqlmap会完全复用你的请求格式成功率极高。问题2使用--tamper后sqlmap跑得非常慢或者直接卡住。原因Tamper脚本会增加payload的复杂度和数量尤其是组合多个脚本时。并且一些编码脚本如base64会使payload长度剧增可能触发服务器其他限制。解决提高效率使用--threads参数增加线程数如--threads 10但注意不要太高以免被封IP。精准打击如果已经知道是字符型注入可以用--techniqueU指定使用联合查询Union这通常比布尔盲注Boolean和时间盲注Time快。减少测试使用--level和--risk控制测试强度。对于已知闭合方式的简单注入--level1 --risk1可能就够了。耐心等待有时候慢是正常的特别是跑--dbs、--tables这类需要枚举大量信息的操作。问题3自定义Tamper脚本不生效或者报错。可能原因1脚本放错了位置。自定义脚本需要放在sqlmap的tamper/目录下或者使用绝对路径--tamper/path/to/your/script.py。可能原因2脚本语法错误。确保Python语法正确特别是dependencies()和tamper()函数定义。可以在命令行用python your_script.py测试或者写个简单的测试用例验证转换逻辑。可能原因3脚本逻辑错误。你的编码/替换逻辑可能和服务器处理逻辑不匹配。用一个小payload如1 AND 11在本地模拟服务器解码流程看最终结果是否正确。排查步骤在sqlmap命令中加入-v 3或-v 4参数查看详细payload。观察发送出去的payload是否经过了你的Tamper脚本处理以及处理后的样子是否符合预期。问题4跑出了数据库和表但找不到flag所在的表或列。原因CTFshow的flag表名和列名经常变化如ctfshow_flax,ctfshow_flaxc,flagx,flagv等。解决跑--tables时仔细查看输出寻找名字中包含flag或看起来非常规的表非ctfshow_user。跑--columns时同样寻找包含flag的列名。如果表太多可以用--exclude-sysdbs排除系统数据库只关注当前数据库(ctfshow_web)。如果还是找不到考虑flag可能不在当前数据库用--dbs看看有没有其他可疑数据库。问题5--os-shell命令执行失败。原因--os-shell功能依赖于几个条件数据库用户有FILE权限、Web目录有写权限、知道Web绝对路径。CTF题目环境通常都满足但有时也会受限。解决如果失败sqlmap通常会给出原因如“Web服务器目录不可写”或“数据库用户无FILE权限”。可以尝试手动写Webshell先通过sql注入查询datadir找到数据库路径推测Web路径然后用SELECT ... INTO OUTFILE写入一句话木马。这需要更深入的手工注入技巧。在CTFshow的上下文中213题使用--os-shell是预期解法一般都能成功。如果失败检查网络或重新启动靶机试试。最后再分享一个我自己的小习惯在运行一长串复杂的sqlmap命令前尤其是包含--tamper、--safe-url等参数时我会先在命令末尾加上--batch --flush-session。--batch让sqlmap自动选择默认选项非交互式运行--flush-session清空之前的会话缓存避免旧数据干扰。这能确保每次测试都是从干净的状态开始。