ARTICLE DETAIL

资讯详情

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

sqlmap参数详解与批量扫描实战:从入门到落地

sqlmap参数详解与批量扫描实战:从入门到落地 我不是来讲理论的一上来就摆原理的这篇直接聊工具。sqlmap 对于一个做 Web 安全测试、漏洞排查、CTF 解题的人来说属于绕不开的自动化注入检测工具配合“参数”理解到位之后“批量扫描方案”这事才真正落地。刚开始用 sqlmap 的时候我也犯过只拿一个 URL 就跑命令、扫不出来就换靶场反复试的傻事后来才发现问题往往不在工具本身而在请求参数、检测级别、目标上下文这些细节上。这篇文章会把 sqlmap 的常用命令、参数含义、批量扫描的完整思路拆开讲一遍结合我在本地靶场和授权测试环境里的实际操作经验来说明适合刚学注入测试的初级安全工程师、CTF 参赛者以及想快速验证内部系统是否存在 SQL 注入风险的研发同学参考。需要先交代一句安全边界文中的所有示例都是基于本地自建靶场、CTF 题目环境或已获得书面授权的测试目标请勿对未授权的第三方系统使用 sqlmap 或其他扫描工具。批量扫描能力越强越要清楚它应该在什么范围内被使用。1. 从一个真实需求说起为什么 99% 的注入测试卡在参数理解上1.1 先搞清楚 sqlmap 解决的是哪个环节手工测试 SQL 注入的时候核心流程通常是找到可能存在数据库交互的参数、构造特殊输入观察页面响应差异、判断注入类型、再一步步枚举数据。这个过程重复性很高尤其当目标页面参数多、请求需要携带登录态或者 CSRF Token 的时候手工效率会非常低。sqlmap 的价值就是把这几个环节自动化探测参数、识别注入类型、推断数据库类型、利用注入点获取数据。但这里有个很容易被忽略的前提sqlmap 能自动化的部分是“检测与利用”而“目标请求长什么样、哪些参数值得测、如何让请求真实到达后端”仍然需要人来准备。我见过很多扫描失败的案例最后排查下来不是目标没有漏洞而是漏了 Cookie、POST 数据没带上、URL 特殊字符被 shell 转义了、或者目标压根不是标准 GET 传参方式工具根本拿不到有效的响应。所以“参数理解”才是真正拉开差距的地方。这个“参数”既包括 sqlmap 命令行参数也包括 HTTP 请求中的业务参数。两边得合在一起看才能让工具真正干活。1.2 不同请求场景下分别该怎么传参根据目标页面的请求方式第一件事是决定用哪个请求参数组合去喂给 sqlmap。最常规的是 GET 请求URL 后面直接带查询参数。这种直接把完整 URL 用英文双引号包住传给-u就行。但要注意 URL 里如果有这种字符在 Linux shell 下不加引号会被解释成后台执行符命令直接跑飞这是新手最容易踩的第一个坑。如果是登录后的页面、搜索框、表单提交这类 POST 请求则需要把 POST 数据放在--data参数里同时把登录态 Cookie 用--cookie带进去。比如 DVWA 靶场里登录后进入 SQL Injection 页面如果不带 Cookie会重定向到 login.phpsqlmap 扫到的就不是真正的注入页而是登录页面本身。如果目标页面的接口是 Ajax 异步请求返回 JSON 数据这类请求除了--data之外往往还需要用--headers指定Content-Type: application/json否则后端可能直接拒收。还有一类场景是页面带 CSRF Token如果 Token 每次都变就不能硬编码sqlmap 提供了--csrf-token来自动从响应页面里提取 token 并更新到下一次请求中比每次手动拷贝要稳定得多。我用一个表格把常见的请求场景和对应参数列出来方便对照查阅场景传参方式例子GET 显式参数-u 完整URLsqlmap -u http://127.0.0.1/dvwa/vulnerabilities/sqli/?id1SubmitSubmitPOST 表单-u URL --data参数值参数2值2sqlmap -u ... --datauseradminpass123submit1登录后扫描-u URL --cookiePHPSESSIDabc123sqlmap -u ... --cookiePHPSESSIDabc123JSON 接口-u URL --data{id:1} --headersContent-Type: application/jsonsqlmap -u ... --data{id:1} --headersContent-Type: application/jsonCSRF Token 变化--csrf-token参数名sqlmap -u ... --dataid1tokenxx --csrf-tokentoken说实话把这些场景理清楚之后sqlmap 的入门就已经完成一半了。因为工具本身怎么拼命令是固定的难的是判断当前目标属于哪一种请求形态这是经验问题。2. 实操前准备环境搭建和基础命令框架2.1 工具安装与靶场环境选择sqlmap 是 Python 写的官方推荐直接通过 git clone 拉取最新源码因为更新频率比较快发行版自带的版本往往落后几个大版本。如果设备上有 Python 3也可以直接用 pip 安装方式但更建议 clone 方式为了方便自己跟进更新我平时用git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git拉一个精简版然后通过python sqlmap.py运行或者建立软链到 PATH。Kali Linux 默认自带 sqlmap但系统自带的版本不保证是最新的。验证方式很简单命令行进入 sqlmap 目录后执行python sqlmap.py --version正常会输出版本号。如果连 Python 环境都没有可能需要先解决 Python 基础环境问题这一步卡住后边什么都做不了。靶场环境方面我强烈建议本机用 Docker 搭一套 DVWA 或者 SQLi-Labs。这两个是练习注入检测最好的起步环境一个侧重真实业务页面场景一个侧重注入点语法花样。选 Docker 的原因是可以快速起停、不污染宿主机而且能随时重置数据库状态。我自己常用的命令是docker run -d -p 8080:80 vulnerables/web-dvwa启动后浏览器打开http://127.0.0.1:8080初始化数据库并登录之后就有本地合法目标可以测试了。2.2 一上来就要明白的合规边界这个句话可能有点老生常谈但确实有必要讲清楚再继续。sqlmap 的批量扫描能力非常强一条命令扫一个网段都不是什么难事但能不能扫、可以对谁扫这道红线跟技术无关是每个从业者的基本底线。我在带新人的时候经常说一个原则本地靶场随便折腾线上目标必须有授权。授权可以是渗透测试项目合同、漏洞众测平台的测试范围说明、或者客户书面确认。如果没有这些哪怕对方服务器防护再弱也不能用 sqlmap 去试。技术能力的意义在于让被授权范围内的测试更高效而不是让工具变成危险品。2.3 一条基础命令拆开看假设已经登录 DVWA进入 SQL Injection 页面此时 URL 是http://127.0.0.1:8080/vulnerabilities/sqli/?id1SubmitSubmit在浏览器开发者工具里能看到 Cookie 里有 PHPSESSID 字段简单粗暴的办法是把整个 Cookie 字符串复制出来跑这样一条命令python sqlmap.py -u http://127.0.0.1:8080/vulnerabilities/sqli/?id1SubmitSubmit --cookiePHPSESSID你的会话值; securitylow --batch逐项说明-u指定目标 URL注意整段加了双引号防止和?被 shell 截断。--cookie传入登录态保证请求到的是登录后的页面。--batch表示使用默认选项不对 sqlmap 的每个交互问题逐一确认。执行后 sqlmap 会先做连通性测试然后对 URL 中的参数发起检测。输出里如果出现Parameter: id (GET)和Type: boolean-based blind之类的信息就说明发现了注入点。我第一次跑通的时候感觉挺兴奋但其实这里边包含了很多自动化的检测逻辑要读懂这些输出就得理解后续要讲的各类参数。3. sqlmap 参数详解这些参数才是读懂输出的关键3.1 请求侧参数不只是 -u 和 --data请求侧参数决定“工具以什么样的身份和格式把请求发给目标”。--cookie的作用前面已经提过但这里还想多说一个点除了登录态Cookie 里可能包含一些业务侧的身份信息、偏好设置、会话标记。如果目标是一个大型业务系统缺失 Cookie 很可能导致请求被重定向到登录页或风控页sqlmap 就会基于一个完全错误的响应做判断。所以批量扫描前用浏览器的复制请求功能把完整 Cookie 带出是性价比最高的准备动作。--headers是用来补充自定义头部的。有的接口要求必须带上某个自定义 Header 才会返回正常业务数据这一条在测试内部系统时尤其常见。比如一个网关会检查X-Forwarded-For或Authorization头没带就直接拒绝。写法是--headersX-Forwarded-For: 127.0.0.1\nAuthorization: Bearer eyJxxx--method一般用不到因为工具会根据--data存在与否自动选择 GET/POST但如果目标是 PUT、DELETE 这类方法就需要显式指定。--data里如果参数值本身包含 URL 编码字符可以直接填原始字符工具会自动处理编码。但如果参数值里含引号、空格最好整体用单引号包裹避免与 shell 解析冲突。3.2 检测深度参数--level 与 --risk 到底改了什么很多新手不理解为什么有时候-u xxx?id1扫不出东西把--level提到 5 之后就能扫到。原因是 level 控制的是检测的“广度”默认 level1 时sqlmap 只测 URL 中的 GET 参数level2 时加入 Cookie 参数level3 时会测 User-Agent、Referer 等 HTTP 头level4、5 则会尝试更多注入位置和更复杂的 payload 变形。--risk控制的是“危险程度”risk1 是相对安全的常规注入测试risk2 会增加一些基于时间盲注的 payloadrisk3 可能使用基于堆叠查询的 payload存在修改数据或触发异常的风险。在只读数据、非破坏性测试场景下默认 risk1 就够了如果目标明确允许深度测试可以适度提升。两者的区别可以用一张表说明参数默认范围影响--level11-5测试参数范围扩大GET参数 - Cookie - Header - 更多 payload--risk11-3从安全注入 payload 到高副作用 payload 递进实际测试时我很少一上来就 level5因为会非常慢而且会产生大量无意义请求。常规做法是先用默认 level1 快速过一遍没有发现再提到 level3如果确实存在藏在 Header 里的注入点再上 level5。时间盲注场景下level 越高扫描时间会指数级上升需要权衡。3.3 数据库指纹与注入技术参数--dbms用于指定后端数据库类型比如--dbmsmysql或--dbmsmssql。这个参数表面上是约束实际上是加速。因为不同数据库的注入语法和测试 payload 差异很大明确指定类型后工具就不再发大量跨数据库的试探请求扫描时间明显缩短。但前提是你对目标技术栈有基本判断不能乱猜。--technique用于指定使用的注入技术。sqlmap 支持六种技术缩写分别是BBoolean-based blind布尔盲注EError-based报错注入UUnion query-based联合查询注入SStacked queries堆叠查询注入TTime-based blind时间盲注QInline queries内联查询默认是 BEUST 全开。如果只想用某一种技术可以写成--techniqueU比如目标响应里联合查询的效果比较好就只跑这一种速度会快很多。--current-db是快速了解当前数据库名的参数对于 MySQL 场景非常实用。一条命令python sqlmap.py -u http://127.0.0.1:8080/vulnerabilities/sqli/?id1SubmitSubmit --cookiePHPSESSID你的会话值; securitylow --current-db --batch执行完能看到current database: dvwa。这一步虽然简单却能为后续枚举表名、字段名提供基础。3.4 数据获取完整链路从库名到字段值确认了注入点和当前数据库之后拿数据基本就是三步走。第一步查所有数据库名python sqlmap.py -u ... --cookie... --dbs --batch第二步根据库名查表python sqlmap.py -u ... --cookie... -D dvwa --tables --batch第三步根据表名查字段并导出python sqlmap.py -u ... --cookie... -D dvwa -T users --dump --batch这里加-D指定数据库名-T指定表名完整拼写分别是--dbmsmysql -D dvwa -T users。--dump会先枚举列名再自动取数据并保存到 CSV 文件。有个容易踩的坑如果一张表很大--dump可能会耗费大量时间。可以先--columns看字段再通过--sql-querySELECT user,password FROM users LIMIT 5;指定查询减少传输量。速度方面可以在命令里加--threads 3提高并发默认是 1不要设置太高否则容易把目标打挂或触发防护。--o参数相当于开启所有优化项对常规 MySQL 目标有一定加速效果但遇到过滤严格的目标时反而可能适得其反建议按需使用。3.5 那些帮你“省人力”的参数--batch、--answers、--output-dir交互式操作在一两条命令时还能接受批量跑十几个 URL 的时候每个目标都弹交互问题会让人崩溃。--batch表示接受所有默认选项这是一个保底方案但它的缺点是有时默认选项不是最优解。比如 sqlmap 在检测到 MySQL 后会问是否要继续测试其他数据库类型默认只测 MySQL这对于明确目标是 MySQL 的场景是好的。但如果后端是 PostgreSQL、前端用了 MySQL 兼容协议默认可能就不完整了。所以更精细的控制是用--answers提前回答常见交互问题。例如--answerscrackN, dictionaryN, installN这个命令告诉工具不进行密码字典破解、不安装扩展把更安全的选项固定下来。--output-dir指定结果输出目录建议批量为每批任务单独建文件夹。sqlmap 默认把结果写到用户目录下的.local/share/sqlmap或类似路径久了之后文件会非常分散。我自己习惯把输出目录统一成--output-dir/root/sqlmap-logs/20240115方便后续回看每个目标的检测结果和日志。4. 批量扫描方案的思路与落地不要上来就扫一堆 URL4.1 先把目标清单整理成干净输入批量扫描最容易犯的错误是把几百个 URL 直接丢给脚本并发跑。这样既容易触发目标风控也会让结果混乱到没法分析。我建议批量之前先做三件事。第一确认目标范围。每一次批量扫描的主机、域名、URL 路径必须来自授权范围清单而不是搜索引擎或主动资产测绘结果。第二去重和清洗。很多 URL 看起来不同实际指向同一个后端接口带了不同的统计参数而已这些需要去掉。第三补齐请求上下文。如果目标都在同一个登录系统下先把公共 Cookie、公共 Header 准备好如果每个 URL 需要不同的认证信息则要提前分组。一个可参考的 URL 清单文件targets.txt格式如下http://127.0.0.1:8080/vulnerabilities/sqli/?id1SubmitSubmit http://127.0.0.1:8080/vulnerabilities/sqli_blind/?id1SubmitSubmit http://127.0.0.1:8080/vulnerabilities/sqli_session/?id1SubmitSubmit每一行一个完整 URL保证用 Python 或 Shell 遍历时可以直接取用。4.2 三种批量跑法for 循环、Burp 导出请求、调用 sqlmapapi方式一是简单的 Shell for 循环适合目标数量少、可控性要求高的场景while read url; do python sqlmap.py -u $url --cookiePHPSESSID你的会话值; securitylow --batch --output-dir/root/sqlmap-logs/targets done targets.txt这个方式的优点是逻辑清晰、可控性强每个目标顺序执行不会并发失控缺点是目标多的时候耗时较长。方式二是配合 Burp Suite。把批量抓到的 HTTP 请求从 Burp 中导出每条请求保存为一个 .req 文件然后通过-r参数交给 sqlmappython sqlmap.py -r request1.req --batch-r的好处是完整保留了请求行、Headers、Body 内容不会因为手工拼接 URL 而丢掉关键上下文尤其适合 POST 表单和带复杂 Cookie 的接口。方式三是调用 sqlmapapi这是更工程化的方案。启动一个 API server 后可以用 Python 或其他语言调用接口创建扫描任务、查看任务状态、拉取结果。适合团队做自动化流程。我个人的观点是如果只是自己用Script 方式足够如果要平台化再考虑 sqlmapapi。不要为了炫技而增加不必要的复杂度。4.3 登录态、CSRF 与动态参数在批量场景的处理批量扫描时最容易翻车的是登录态。很多目标是需要登录才能看到数据页面的如果所有请求都复用同一个 Cookie有可能因为短时间访问量过大触发风控导致会话失效。这种情况建议把目标按账号维度拆批次每批次限速而不是几百个并发共享一个会话。CSRF Token 是一个更难缠的问题。如果每个表单都需要动态 token 才能提交那简单复制 Cookie 就不够。sqlmap 的--csrf-token参数能解决一部分问题它会自动从目标响应中提取指定参数值并更新后续请求。这个参数在本地靶场比较有效因为 token 生成逻辑相对简单。真实系统中如果 token 是通过 JavaScript 异步生成、存放在内存中的那么这类自动化工具基本无解只能人工辅助处理。动态参数除了 token还有时间戳、签名等。这类参数如果在请求中固定不变后端可能会拒绝。实际测试时我会先用浏览器多抓几个请求对比哪些参数值是固定的、哪些是变化的。变化的部分优先看前端响应里是否可获取再考虑用 sqlmap 的--csrf-token或自定义脚本去解决。如果确实无法自动化就标记为“需人工测试”不要硬跑浪费时间。4.4 批量结果归档与快速研判批量跑完不能只看终端输出。sqlmap 每个目标的详细日志、注入 payload、session 数据都会写到--output-dir下对应的文件夹中。归档的意义在于后续复现和审计报告。常用的快速研判思路是先用 grep 在所有日志里找出包含Parameter:或Type:的行这是目标存在注入的最直观标志然后根据注入类型分布优先排查联合查询和报错注入因为这两类的利用价值通常更高最后人工复测确认 payload 是否可以稳定返回数据。可以做一个简单的索引表目标文件发现时间参数注入类型是否需要复测request1.req10:24idboolean-based blind是request2.req10:26无未发现否request3.req10:31userunion-based是这种表格在写渗透测试报告或者给研发同事反馈漏洞时非常有用能让所有人快速对齐目标。4.5 批量扫描的速率控制与访问压力控制批量扫描不等于高并发扫描。sqlmap 本身是单线程检测通过--threads才能并发而且每个并发都会向目标发送大量请求。如果目标是一个对外的在线业务系统无节制的请求很可能影响正常用户访问也可能触发风控、WAF最终导致测试中断甚至 IP 被拉黑。我常用的限速方案是--delay 1让每次请求之间至少间隔 1 秒--time-sec 5指定时间盲注中用于判断真假的延迟基准--threads 2最多开两个并发线程。这三个参数组合下来整体扫描速度稳定但不会造成明显压力。还有一个小技巧先用小样本验证命令可行性。比如在完整目标清单里先挑 2-3 个 URL 跑一遍确认既不报错、也不触发风控再放开到全量。这个思路跟先冒烟再回归的测试理念是一样的。5. 常见问题排查与避坑实录5.1 URL 带特殊字符被 shell 吞掉这是最常见的一类问题而且报错信息并不直观。有人在命令行里直接写python sqlmap.py -u http://127.0.0.1:8080/vulnerabilities/sqli/?id1SubmitSubmit --batch这里的在 Linux shell 里是后台执行符命令会在python sqlmap.py -u http://127.0.0.1...这里截断真正扫到的 URL 只是?id1后面那部分完全没传进去而且整个命令的背景行为会让人困惑。解决办法就是给 URL 加英文双引号python sqlmap.py -u http://127.0.0.1:8080/vulnerabilities/sqli/?id1SubmitSubmit --batch如果 URL 中还有空格尤其是一些参数值里有特殊字符也可以用单引号包裹。统一建议所有 URL 和 --data 的值都要用引号包裹并且先 echo 出来确认一下再跑真正命令。5.2 扫描报错 unable to connect 或 timeouts如果 sqlmap 报 unable to connect第一步不是调参数而是用浏览器或 curl 先手动访问目标地址确认目标服务是活的、网络是通的。很多时候是测试机到目标网络不通、目标服务临时宕机、或者请求被防火墙拦截。如果服务正常但 sqlmap 仍报超时可以加--timeout30拉长连接超时时间加--retries2提高重试次数。但如果目标本身响应就是很慢那可能要检查是不是请求链路经过了多层网关。还有一个容易被忽略的点目标只接受 HTTPS而 URL 写成了 HTTP这里会直接连接失败。5.3 扫描结果提示 parameter appears to be dynamic当 sqlmap 输出类似parameter id appears to be dynamic时意思是该参数每次请求返回的内容都不一样工具无法通过静态响应对比来判断注入是否存在。这种情况有几种可能一参数确实不参与数据库查询只是前端标识id二页面包含随机元素、时间戳、广告位导致响应动态变化三后端有过滤或参数校验导致注入 payload 没有进入数据库查询逻辑。我的排查顺序是先用浏览器多刷新几次目标页面看哪些部分是变化的再手工构造?id1和?id2对比页面差异如果明确参数参与数据查询但仍提示 dynamic可以尝试用--string或--text-only参数指定稳定参考标识帮助 sqlmap 在动态页面中识别真伪响应。--text-only会让工具只关注去除 HTML 标签后的文本内容很多时候能绕过模板引擎造成的干扰。5.4 注入过滤严格双写、大小写绕过与 tamper 脚本的正确用法很多目标带了基础防护比如把select、union这些关键词直接过滤或替换为空。此时 sqlmap 默认的 payload 可能全部失效输出里就会是 clean 或者 all tested parameters are not injectable。针对这类场景sqlmap 提供了--tamper参数指定一个或多个脚本对 payload 做变形。比较有代表性的几个space2comment把空格替换成注释符/**/可用于绕过简单空格过滤。between把等比较符替换成BETWEEN ... AND ...可以绕过部分比较符过滤。concat2ws把字符串拼接函数替换成空白符适合部分过滤concat的目标。双写绕过的思路在 MySQL 场景比较有效虽然 sqlmap 没有完全等价的现成脚本但可以通过自定义 tamper 脚本实现。简单版本是把select替换成selselectect、union替换成ununionion用于应对只过滤一次关键词的规则。我实际用下来的感受是tamper 是最后一道手段不是第一选择。先确认目标确实存在注入点但 payload 被过滤再考虑用 tamper。而且不同过滤规则差异很大没有万能脚本写自定义 tamper 时要先看过滤逻辑而不是盲目套用网上的脚本。CTF 题里经常遇到这种情况比如题目过滤了空格你直接--tamperspace2comment可能就过了但如果题目在多处做了多次过滤那还是得手工分析。5.5 时间盲注极慢的优化思路时间盲注扫描慢是常态因为每个判断都要等 sleep 延时结束。优化方向有三条一是收窄范围用--techniqueT只测时间盲注用--level 1避免额外 payload二是减少判断次数用--string指定稳定页面标识帮助工具更快区分真伪三是合理调整--time-sec本地靶场默认 5 秒够了如果目标网络延迟大可以适当调高但如果调的太大整体耗时也会上升。还可以用--union-cols在某些场景下跳过逐列猜测直接指定联合查询的列数但这个需要手工先探测出准确的列数否则会出错。写在最后的经验我刚接触 sqlmap 的时候最喜欢做的事是复制网上的大段命令然后对着靶场跑一遍跑不通就换命令。后来慢慢意识到真正有价值的是理解每条参数背后的检测逻辑和请求场景。比如 level 3 才会测 Header 里的注入点比如 CSRF Token 和动态签名在很多真实系统里靠工具是解不了的又比如批量扫描的真正难点不是并发而是如何控制访问压力不被打断。现在每次批量扫描之前我都会先保存命令日志和输出目录因为几天之后回看结果时你会非常需要知道当时到底用了哪些参数。希望这篇内容能帮你少走一些弯路。
返回列表