十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Sqlmap实战指南:从SQL注入检测到WAF绕过的完整链路

Sqlmap实战指南:从SQL注入检测到WAF绕过的完整链路 1. 为什么是Sqlmap先给它一个准确的定位先从一个我经常遇到的场景说起。手工注入测了大半天确认了目标存在SQL注入参数也找到了类型也判断出来了Payload在浏览器里验证过能出数据。但真到了要拖库的时候手工一条一条拿数据太慢了盲注更是折磨人——于是想起了Sqlmap。Sqlmap是一款开源的自动化SQL注入检测与利用工具支持从基础的注入点探测到数据库指纹识别、数据提取、文件读写、命令执行等一系列操作。它解决的不是能不能发现注入这一个问题而是把发现注入之后怎么高效利用这件事替你包了。对于刚接触SQL注入的人来说Sqlmap是降低入门门槛的利器对于有经验的测试者来说它是标准化流程里不可缺少的加速器。这篇文章我会按照自己实际使用Sqlmap的习惯来写从环境准备到基础链路再到绕过WAF和参数加密这类进阶场景最后复盘一次跑不出注入时的完整排查过程。适合三类人看一是刚接触Web安全、想在靶场上跑通一遍完整流程的新手二是CTF比赛里被SQL注入题卡住、想把工具用得更灵活的选手三是在实际授权测试中需要对大量目标做快速验证的工程师。把话说在前面所有命令的演示思路都以靶场环境和授权测试为前提。没有授权就去打别人的站这不是能力问题是性质问题。我见过不少新人上来就对着互联网目标一顿乱扫最后自己也说不清在干什么。这条路走不远的。2. 环境准备Sqlmap安装这一步的坑其实不少很多人觉得Sqlmap安装不就是下载解压运行吗实际遇到的坑比你想象得多。我把最常见的几个问题集中说一遍。2.1 Python版本问题最容易被忽略的兼容性陷阱Sqlmap基于Python开发。老版本Sqlmap对Python 2和Python 3都有兼容但新版代码已经全面转向Python 3。问题往往出在环境上很多Linux发行版默认同时装了Python 2和Python 3终端里输入python可能指向的是Python 2这时候直接运行python sqlmap.py就可能因为语法兼容问题直接报错。我的建议是安装前先看一眼环境python --version python3 --version如果你用的是Kali这类渗透测试系统系统自带Sqlmap直接用就好。如果是自己环境安装优先用Python 3。具体操作上装完后用python3 sqlmap.py启动或者写一个别名指向Python 3解释器省得每次手打。2.2 三种安装方式选哪种取决于你的使用习惯Sqlmap的获取方式主要是源码、包管理器和Docker三种我逐个说清楚各自的适用场景。源码方式最推荐因为更新方便。Sqlmap更新非常频繁几乎每天都有新Payload和新绕过脚本这也是它的核心生命力。用Git克隆到本地之后随时拉取更新git clone https://github.com/sqlmapproject/sqlmap.git cd sqlmap python3 sqlmap.py --version如果你想用pip管理也可以直接pip install sqlmap它会装一个sqlmap命令到PATH里之后直接敲sqlmap就能用。这种方式胜在简单缺点是更新节奏不如Git源码方式跟手。Kali自带Sqlmap不用额外装。但我建议还是定期手动更新因为Kali镜像里的Sqlmap版本可能滞后于上游一些新出现的绕过思路和Payload可能没有。2.3 跑通最小验证别在环境上浪费时间装好后最直接的验证方式是python3 sqlmap.py --version能看到版本号说明基础环境没问题。如果要进一步验证工具本身工作正常可以在本地跑一个靶场比如DVWA用它提供的SQL注入关卡做一次完整测试能出数据就说明整条链路都是通的。还有一个小细节Windows下如果双击运行或者直接拖拽窗口没有反应大概率是Python没加到系统PATH里。去环境变量里把Python安装路径加上命令行就能正常识别python命令了。遇到这种问题不用慌本质上和Sqlmap一点关系都没有。3. 第一条完整链路从目标探测到拖出数据的标准操作我写Sqlmap的文章通常会直接给命令但这次想先带你走一遍完整链路。因为只背命令不理解每个参数的作用换个场景就抓瞎。3.1 目标确认先想清楚你要做什么Sqlmap支持的目标形式挺多最常用的就是-u指定URL。但URL不是随便填的这里有个关键点Sqlmap只能测带参数的请求参数通常是URL查询参数、POST数据、Cookie、请求头这些。如果你给的URL本身没有任何参数Sqlmap会告诉你找不到注入点这不是工具不行是目标本身不适合。一个典型的请求长这样sqlmap -u http://192.168.1.10/dvwa/vulnerabilities/sqli/?id1SubmitSubmit --cookiePHPSESSID你的会话ID; securitylow --batch这里带了Cookie是因为DVWA需要登录态才能访问漏洞页面。实际测试中很多应用也需要登录不带Cookie的话Sqlmap连页面都打不开更别说测注入了。还有一点拼URL的时候别把id1随便去掉。我见过有人喜欢把参数值删掉只留id这样很多注入探测脚本反而容易失效。保留一个正常的基线值Sqlmap会在它基础上做各种变形。3.2 自动探测和数据库枚举的标准参数顺序一次标准流程我会按这个顺序来第一步先做个快速验证确认注入点是否存在以及基础信息sqlmap -u http://target.com/item.php?id1 --batch--batch的意思是所有需要交互的地方都用默认选项这样输出不会被一堆提问打断。第二步探测数据库类型和版本这决定了后续Payload的编写方式sqlmap -u http://target.com/item.php?id1 --banner --current-db--banner会返回数据库的版本信息--current-db是当前查询使用的数据库名。拿到这两个信息你就能判断后面是走MySQL的思路还是SQL Server的思路。第三步枚举所有的库和表sqlmap -u http://target.com/item.php?id1 --dbs sqlmap -u http://target.com/item.php?id1 -D 某个库名 --tables第四步拖指定表的数据sqlmap -u http://target.com/item.php?id1 -D 某个库名 -T 某张表 --columns sqlmap -u http://target.com/item.php?id1 -D 某个库名 -T 某张表 -C 字段1,字段2 --dump3.3 拖数据时影响效率的几个关键参数--dump默认会把数据保存下来输出到本地CSV文件里这个设计很贴心万一数据量大也不怕终端刷屏。但单纯用--dump在小带宽和延迟高的目标上会很慢。有这么几个参数可以提速--threads并发线程数一般设为5到10太高可能会把目标打挂。不同数据库引擎对并发处理的响应差异很大MySQL通常在并发较高时报连接数错误需要根据目标反馈调整。--fetch指定数据获取方式。默认是逐行从数据库查询效率一般。如果目标是盲注场景可以试试--fetch配合--technique指定注入类型避免Sqlmap每个候选注入类型都试一遍。--smart让它先对请求做个基础判断初步判定为动态页面的URL才进行深度探测。对大量URL做批量检测时这个参数能省不少时间。用的时候记住一个原则先小范围验证参数效果再对正式目标使用。别上来就开20个线程对着生产环境打容易造成目标服务异常。3.4 数据导出前的信息判断我在实际使用中还会额外看一眼--dbs返回的结果。如果目标是个CMS库名往往直接能告诉你底层框架是什么比如WordPress的库结构通常是wp_users、wp_posts这种前缀开头。看到这种库名后续的表名枚举范围就会小很多。另外一点经验--dump前先用--count统计一下目标表的数据量。如果表里有几百万行数据直接--dump会把带宽占满这时候用--start和--stop限制只导出前十行做验证确认数据有效性后再决定是否全部导出。4. 绕过WAF的实战套路双写、内联注释与编码函数怎么用绕过WAF可能是Sqlmap使用中最容易让人一头雾水的地方很多人背了一堆Payload换个过滤器还是过不去。我觉得问题不在于你背了多少而在于你是否理解每种绕过思路的适用前提。4.1 双写绕过的适用场景和正确配置方式先解释什么是双写。有些WAF的过滤规则是基于正则匹配关键字的比如匹配到SELECT就拦截。双写的思路是把SELECT写成SELSELECTECT如果WAF只做一次简单匹配匹配SELECT时会被SELSELECTECT中间的SELECT拦住但WAF可能不会去递归检测即便检测了替换掉其中一个SELECT剩下的仍然构成合法的SQL语句。这个思路在Sqlmap里不是直接一个参数就能开的。Sqlmap官方没有内置双写这个名字的tamper但你可以借助自定义tamper脚本来实现。我写过一个极简版本核心思路是遍历payload里的关键字然后在中间插入一个自身副本#!/usr/bin/env python from lib.core.enums import PRIORITY __priority__ PRIORITY.NORMAL def tamper(payload, **kwargs): keywords [select, from, where, union] for kw in keywords: payload payload.replace(kw, kw kw.upper()) return payload实际用的时候--tamper参数指定脚本路径即可sqlmap -u http://target.com/item.php?id1 --tamper双写脚本路径.py --batch要注意的是双写虽然能过一部分基于正则的WAF但遇到对SQL语法做完整解析的产品时基本无效因为解析器一开始就能识别出重复关键字是非法语法。所以双写只推荐在确认目标过滤规则是正则替换型的时候用。4.2 内联注释不只是--comment这么简单内联注释是MySQL特色的注释写法形如/*! 50000 SELECT */里面的内容会被MySQL当作真实语句执行而很多正则过滤会先去掉注释再匹配结果就漏了。在Sqlmap中直接指定--comment就能启用内联注释模式这个参数会生成一堆/*! */包裹的Payload。但实战里我更喜欢用tamper脚本精确控制只对必要的位置做内联注释包裹减少Payload体积过大被WAF从长度上拦截的概率。另外热搜词里提到sql注入内联注释经常和版本号一起出现比如/*!50000SELECT*/。版本号的作用是限定MySQL 5.0以上版本才执行。如果目标数据库版本低于这个号语句会直接被当作注释注入就会失败。所以在选用带版本号的注释变体时一定要先确认数据库版本。4.3 replace()这类函数在Sqlmap中的实际用法热搜词里还有个sql注入replace()我要明确一点replace()不是Sqlmap的内置参数它是SQL里的字符串替换函数也可以用来做绕过变形。比如有的WAF严格匹配information_schema这个字符串你可以把它改写成replace(replace(inforschema,s,mation_schema))等数据库执行时函数会先拼出完整的information_schema。在Sqlmap里用这种思路还是要落到自定义tamper上把payload里出现的危险表名替换成replace()包裹的形式。我测试过这种变体对很多基于黑名单匹配的WAF效果不错但对检查函数调用的WAF无效。4.4 tamper脚本的选择思路不是堆得越多越好我看到不少新手喜欢把一个--tamper后面挂十几个脚本总觉得脚本多就稳。实际上每个脚本都会改变Payload的形状叠加过多可能导致两个问题一是Payload本身被改得面目全非数据库无法识别二是特征量暴增反而触发了WAF的异常行为检测。我的建议是按目标情况做减法场景推荐tamper说明目标过滤空格space2comment把空格换成注释符号目标过滤关键字自定义双写仅限正则替换型WAF目标过滤数字between把比较运算换成between来规避目标过滤逗号自定义脚本逗号被过滤时用join等语法替代目标环境是MSSQLcharencode / charunicodeencode把字符转成编码形式先确认目标WAF的拦截点再选最贴合的两三个脚本才是效率最高的路线。我在实战里用到最多的组合往往是一个空格处理脚本加一个关键字变形脚本很少超过三个。另外Sqlmap的--tamper支持参数--tamperspace2comment,between这种逗号分隔多个脚本的写法但上面已经说了谨慎叠加。5. 参数加密场景Sqlmap对看不出参数内容的请求怎么处理热搜词里有一条sql注入漏洞测试(参数加密)这个场景很多文章都没怎么写清楚。实际渗透测试里越来越常见的情况是客户端把参数值做了编码或加密后再发给服务端服务端在业务逻辑里解密再拼接SQL。比如参数值不是id1而是idMQ这种Base64甚至还有做完整AES加密的。如果你直接让Sqlmap去测idMQ它会在Base64串后面拼接Payload服务端解密出来是一串乱码数据库自然执行不了。这不是Sqlmap不行是它不知道请求参数在发给数据库之前还要过一道处理。解决这个问题靠--eval参数。--eval允许你在每次请求发送前执行一段Python代码对Payload做动态变换。思路是把原始Payload先做加密把加密结果填充到请求参数里Sqlmap再发送。拿最常见的Base64场景举例。比如目标接收的参数是enc加密后的值服务端拿到后用Base64解码再拼SQL。那么要让Sqlmap正常工作就需要让它在每轮请求前先对Payload做Base64编码sqlmap -u http://target.com/test.php --dataencMQ --evalimport base64; encbase64.b64encode(enc.encode()).decode() --batch这里解释一下这个命令干了什么。--dataencMQ是告诉Sqlmap请求体里有一个叫enc的参数初始值是MQ这一串Base64文本。--eval里的代码每次发送前执行把enc的值取出、编码、再放回去。Sqlmap内部生成的Payload就带着Base64外壳发送给目标了。热搜词里还有一条sql注入base64函数指的可能是数据库里的TO_BASE64()函数。这两件事的名字类似但不是一个层面的东西。参数加密场景里的Base64是在应用层做的属于业务逻辑而数据库的TO_BASE64()是把查询结果的某个字段转成Base64字符串返回属于结果变形用途完全不同。如果你遇到的不只是Base64而是更复杂的加密算法比如AES思路一样--eval里写对应的加密逻辑即可。比如用pycryptodome库的AES加密函数对Payload处理后填充。核心原理就是让Sqlmap发送的永远是服务端预期接收的格式。还有一个小技巧当加密参数里有时间戳、随机数等动态因素时--eval里可以用time.time()、random等Python模块生成Sqlmap每次请求都会执行这段代码所以变量会自动更新不需要手动同步。6. 一次实战复盘Sqlmap怎么都跑不出注入的时候我在排查什么比命令用错更让人头疼的是URL给了、参数对了、也确认有注入但Sqlmap就是报all tested parameters appear to be not injectable。这类问题我复盘过无数次说一下完整的排查链路你可以照着这个顺序做。6.1 先看网络和会话很多问题出在请求根本没被正确发送第一次遇到跑不出注入时别急着调Payload先确认请求本身有没有问题。Sqlmap发送的请求和你浏览器里的请求是否一致我踩过很多次Cookie失效的坑。有些应用登录态绑定了几分钟后过期Sqlmap第一次探测时登录态还在等跑第二轮时已经掉了整个会话进入登录跳转页面Sqlmap会判断目标不可达。验证方法很简单在命令里加-v 3它会打印详细的请求和响应头。看响应状态码是不是200会不会跳转到login页面。如果发现跳转应该先更新Cookie重新测试。另外目标服务器可能对User-Agent做了过滤很多WAF对非浏览器的User-Agent直接拦截。这时候加一个看起来正常的UAsqlmap -u http://target.com/item.php?id1 --user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) --batch6.2 再调探测深度和注入技术类型默认配置覆盖不到的场景Sqlmap默认的探测等级是1这个等级下只测试最基本的注入类型和Payload组合。如果目标用的是不那么常见的拼接方式比如数字型注入在复杂查询里、或者需要进行两次URL编码才生效默认配置就测不出来。这时候先试--level 3 --risk 2。--level影响的是测试的深度和范围等级越高测试的Header、Cookie里的参数也越多--risk影响的是Payload的破坏性等级越高越可能用到UPDATE、DELETE这类有副作用的语句。在授权测试环境里可以放心开但对生产环境要谨慎--risk 3很可能把数据改了。还有一个经常被忽略的方向指定注入技术类型。Sqlmap支持基于布尔的盲注、基于时间的盲注、报错注入、联合查询、堆叠查询这几种。默认情况下它会自动检测但如果目标的回显特征比较特殊自动检测可能误判。比如目标页面不管正确还是错误都不区别显示内容但执行SQL报错时页面上有500状态码这类场景使用报错注入往往比盲注更可靠sqlmap -u http://target.com/item.php?id1 --techniqueE --batch同理如果目标只是响应时间表现出差异内容上完全无差别用--techniqueT让它专注时间盲注能省下大量的无效探测请求。6.3 仔细看Sqlmap给出的判断依据很多人忽略Sqlmap的输出日志里那几行关键信息。它会明确告诉你当前用了哪个Payload进行测试响应状态是什么比如HTTP/1.1 200 OK。多留意这些细节能更快定位问题。有一次我在测一个目标时Sqlmap对每个Payload都返回200但页面内容千奇百怪有的是报错页有的是正常页。最终看日志发现目标对所有非法SQL都返回了200状态码但错误信息被后台吞掉了页面完全无差异。这时候单纯依赖状态码判断注入是失效的必须回归到响应内容的对比用--string或--regexp指定一个页面中与注入结果绑定的特征字符串Sqlmap就能从内容差异里识别出注入效果。6.4 另一个让我卡了很久的场景分号被过滤还有一次实际测试中目标对参数值做了很强的过滤分号被直接替换成空。常规Payload全挂Sqlmap也测不出来。最后手动测试发现问题不是注入不存在而是注入点拼接SQL时用的是分号分隔的堆叠查询语境但分号被过滤了。解决方式不是在Sqlmap里调参数而是换一个思路找到另一个允许双引号包裹下构造字符串字面量的SQL上下文用SELECT本身来做布尔判断。这个场景提醒我Sqlmap是探测工具不是银弹有些时候你的SQL功底比工具参数更有用。6.5 排查顺序总结我把排查顺序整理成了一个小清单你可以直接照着执行-v 3确认请求和响应是否正常有没有跳转和Cookie失效问题。确认目标参数是GET还是POSTCookie和Header是否完整。调--level 3 --risk 2扩大探测面但注意先和授权方确认风险。按响应特征指定--technique不要盲目依赖自动检测。如果页面内容无差异用--string或--regexp指定特征绑定。如果目标带WAF按第4节的方法配置tamper后再测。最后一步检查自己输入的URL是否符合目标应用的参数规则必要时手动用Burp Suite抓包验证注入点存在后再回过来用Sqlmap。这套流程走下来90%的跑不出注入问题都能定位到原因。7. 配合靶场练习与CTF场景Sqlmap在具体题目里怎么用很多读者问练SQL注入用什么靶场。我看过太多人一上来就去互联网上找真实目标测试既不安全也不合规。推荐的路线是先把靶场练熟再回到实战场景中去验证。7.1 几个高频出现的靶场对应的练习重点DVWA适合入门SQL Injection模块分四个安全级别从低级到中级再到高级逐步增加防护强度。低级和中级可以直接用Sqlmap默认配置跑通高级需要结合登录态和防御绕过思路。建议把四个级别全部打通而不是停留在最简单的low级别。Pikachu形式更活泼覆盖了SQL注入的多种子类型包括POST注入、搜索型注入、HTTP头注入、宽字节注入等。它的价值在于让你理解不同输入位置对注入Payload的影响这对后续手动搭测试场景很有帮助。CTFHub技能树把SQL注入拆得很细整数型、字符型、报错注入、布尔盲注、时间盲注、堆叠注入还有Cookie注入、UA注入、Referer注入等冷门点。每一个小关卡都对应一种注入类型适合用来排查自己对哪种类型还不熟悉。CTFShow的web入门SQL注入系列的题目偏实战需要根据目标返回情况灵活判断注入类型Sqlmap结合手动验证都能用上。N1BOOKNu1L战队的题集题目难度分层适合进阶练习里面SQL注入和代码审计经常结合你需要先看懂源码逻辑再决定Sqlmap参数。7.2 在CTF比赛中Sqlmap的正确打开方式CTF里用Sqlmap和实战测试有个很大的不同题目环境一般规模很小、数据量少而且设计者往往会在数据库层面设置了特定的返回值要求。比如有的题目要求你读取服务器上的某个文件比如CISP-PTE考题里通过SQL注入读取/tmp/360/key文件。这种场景用Sqlmap的--file-read参数就可以直接读sqlmap -u http://target.com/index.php?id1 --file-read/tmp/360/key --batch执行成功后Sqlmap会把读到的文件保存到本地~/.local/share/sqlmap/output/目标地址/files/目录下。这个参数适用于MySQL的LOAD_FILE()函数和MSSQL的相关特性。前提是数据库账号有对应权限没有的话工具也无能为力。比赛里经常要快速枚举库表我通常会把--dbs、-D 库 --tables、-T 表 --columns --dump三步连着跑但每次只测一个方向不把命令复杂度堆太高。7.3 Sqlmap命令速查与自查清单为了帮你快速回顾我把本文出现过的核心命令整理一下建议收藏目的命令示例基础探测sqlmap -u http://t.com/id1 --batch获取数据库类型和版本sqlmap -u ... --banner --current-db枚举数据库sqlmap -u ... --dbs枚举指定库的表sqlmap -u ... -D 库名 --tables枚举表的字段sqlmap -u ... -D 库名 -T 表名 --columns导出数据sqlmap -u ... -D 库名 -T 表名 -C 列名 --dump读取服务器文件sqlmap -u ... --file-read/tmp/钥匙文件提高探测深度sqlmap -u ... --level 3 --risk 2指定注入技术sqlmap -u ... --techniqueB/E/T/U/S加载tamper变形sqlmap -u ... --tamperspace2comment带Cookie测试sqlmap -u ... --cookiea1; b2处理参数加密sqlmap -u ... --dataencxxx --eval...最后再分享一个我自己的使用习惯不管多急新建一个目标开始测试前我都会先手动发一个干净请求到Burp Suite把完整请求包复制出来再决定Sqlmap的参数怎么写。这一步看似费时间实际上能帮你避开大量因为请求头、Cookie、参数位置不对导致的无效探测。Sqlmap本身确实是个自动化工兵但真正的效率提升永远来自你对目标的理解——工具只是把这种理解落实到每一次请求里而已。
返回列表