PortSwigger SQL注入LAB7 & LAB8 & LAB9
今天我把这三道LAB一起来分享,主要是因为他们之间有着特别紧密的关联,至于有那些关联就让我们来看一下吧。
【本篇目标】
- 理解并掌握通过
UNION来确定查询的列数及数据类型 - 通过LAB7、LAB8的知识来获取LAB9所需的用户名及密码,并完成登录
一、LAB 7
我们先来看LAB7的题目,页面上的注入点是/filter?category=...,LAB的目标是要让我们确认原查询的列数,我们这里可以直接使用ORDER BY *的递增来确认原查询返回的列数
但是我们看原题目,题目要我们返回包含空值(NULL)的额外行,来确定查询返回的列数
其实很好理解,就是在进行UNION联合查询的时候把要查询的值给换成NULL,说可能说不太清楚,但是一看就明白什么意思了
我们可以发现服务器返回了ERROR 500错误,这是因为我们的UNION查询语句返回的列数与原查询列数不符,所以我们可以通过添加NULL的个数直到服务器不返回ERROR 500,这样就可以确定原查询的列数了
就像这样一样,当我们把NULL的个数增加到3的时候,服务器返回了200 OK,所以我们可以得出原查询的列数为3列,LAB7实验目标达成。
【LAB7 Payload】
' UNION SELECT NULL,NULL,NULL FROM information_schema.tables --
二、LAB 8
先看LAB8这道题,页面上的注入点仍然是/filter?category=...,LAB的目标是要让我们发现一个可以返回文本的列,并且返回对应的随机值,这里我们需要返回的值为Wo1mBK
接下来我们先来通过上一个LAB的方法来获得原查询的列数,然后再到他的基础上进行下一步操作
通过尝试可知原查询的列数为3,那么接下来我们只需要知道那一列的数据类型支持返回字符串即可
在这里我们只需要把其中的NULL改为我们所需要返回的字符串Wo1mBK来对每一个列数进行尝试即可。(注意加引号,否则非字符串类型!)
成功定位并返回随机字符串值,LAB8实验目标达成。
【LAB8 Payload】
' UNION SELECT NULL,'Wo1mBK',NULL FROM information_schema.tables --
三、LAB 9
最后我们来看LAB9这道题,页面上的注入点还是/filter?category=...,最后一个LAB的目标是要我们获取数据库中存储的用户名和密码,并且使用administrator账户来进行登录
这道LAB相较于上一篇的LAB5和LAB6,题目中已经告诉了我们目标用户名密码被存放在了users表的username和password列中,这下倒是省去了我们遍历寻找的麻烦
3.1 确认列数
还是老样子,既然我们要用到UNION,那就必然要先来确定原查询的列数,我们可以使用以下两种办法,但是此处我选的是后者:
ORDER BY *
SELECT NULL,NULL,...FROM information_schema.tables
通过尝试,我们得出原查询的列数为2
3.2 确认列数返回的数据类型
题目中我们要返回的是用户名和密码,他们的数据类型为字符串,那么现在我们要做的就是要确定这两行是否可以返回字符串
看来这两列并不是像之前一样都支持返回字符串数据类型的,那我们就要确定是哪一行可以返回字符串了
通过尝试,我们可以发现只有第二列是支持返回字符串的,那现在我们就可以构建最终的Payload了
3.3 构建最终Payload
由于我们一次只能返回一个字符串,所以我们需要先返回所有用户名从而来确定我们所需要的administrator用户位于的行数
通过查询可得,我们所需要的administrator用户位于第2行(以第一行为表头来算),那么接下来我们就可以查询他的密码了
至此我们可得administrator用户的密码为:smevzxynefrus8eviear
现在我们去网页中尝试登录
成功登录并显示LAB已完成。
【LAB 9 Payload】
' UNION SELECT NULL,username FROM users --
' UNION SELECT NULL,password FROM users --
四、两者的区别
通过这三个LAB我们知道可以用两个方法来获取原查询的列数,但这两个方法在触发方式、可获取的信息以及使用场景上各有侧重,具体对比如下:
| 对比项 | ORDER BY | SELECT NULL (UNION) |
|---|---|---|
| 触发方式 | 通过逐步增加 ORDER BY 的列索引,直到出现错误来判断列数 | 通过加入带有 NULL 占位的 UNION SELECT,如果列数或类型不匹配会产生错误 |
| 可得到的信息 | 主要用于快速确认列数 | 既能确认列数,又能进一步测试列的数据类型和哪列能返回字符串 |
| 风险与适用性 | 相对隐蔽,通常不会返回额外数据,适合快速测试列数 | 更灵活但更具侵入性,常用于构造有效载荷以读取数据,可能触发明显的错误或日志 |
| 推荐场景 | 仅需确认列数时优先使用 | 需要继续探测数据类型和返回具体字段时使用 |
所以本质上它们都是用来确定目标查询的列数,但 SELECT NULL 更适合用于后续探测列的数据类型与返回能力,而 ORDER BY 更适合快速且低侵入地确认列数。
五、总结与防御建议
总结:
- 本篇通过LAB7、LAB8、LAB9演示了如何使用
ORDER BY与UNION SELECT确认列数、判断可返回字符串的列,并最终读取目标用户与密码。 - 在渗透测试中,先用低侵入的方法确认列数,再用 UNION 确认列的数据类型,可以更稳妥地构造有效载荷。
防御建议:
- 使用参数化查询或预处理语句,避免直接拼接用户输入到 SQL 中。
- 对输入实行严格的白名单校验与长度限制,尽量不要把未过滤的输入用于查询构造。
- 最小化数据库账户权限,仅授予应用运行所需的最低权限,避免使用高权限账户执行应用查询。
- 关闭或屏蔽详细错误信息,避免把数据库错误信息直接返回给用户,以减少信息泄露。
- 部署 WAF 与入侵检测,监控异常查询模式与频繁的错误返回,并记录审计日志以便追溯。
- 使用 ORM 或安全库替代手写 SQL,并对第三方库保持更新,修补已知漏洞。
博客园技术分享 · 请勿用于非法测试