0%

GeekGame 2021 Writeup

前些天参加了第一届 PKU GeekGame,拿了第 21 名。这是我第一次参加 CTF,感觉还挺有趣的,分享一下 Writeup。

签到

使用可以打开 PDF 的软件(如 Chrome 等)打开,复制最下面两行到其他文本软件中,得到

1
2
fa{aeAGetTm@ekaev!
lgHv__ra_ieGeGm_1}
得到 flag。

一开始用的是 Acrobat,谁知道只能复制 {} 内部的内容,得到的是 aeAGetTm@ekaev! lgHv__ra_ieGeGm 然后就对着这个东西搞了好几天,一度怀疑人生,最后试了一下 Chrome,直接吐血......

小北问答

  • 北京大学燕园校区有理科 1 号楼到理科 X 号楼,但没有理科 (X+1) 号及之后的楼。X 是?

显然为 5。也可以查百度地图

  • 上一届(第零届)比赛的总注册人数有多少?

查相关微信 推送 可知答案为 407

  • geekgame.pku.edu.cn 的 HTTPS 证书曾有一次忘记续期了,发生过期的时间是?

给一个 网址

  • 2020 年 DEFCON CTF 资格赛签到题的 flag 是?

再给一个 网址

  • 在大小为 672328094 * 386900246 的方形棋盘上放 3 枚(相同的)皇后且它们互不攻击,有几种方法?

根据这个 网页 上面给出的 m*n 棋盘上的 3 皇后问题的公式,编程计算即可。

这个问题卡了几天,一看这两个数字分别是狗妈和嘉然的 UID,还以为和她们有什么关系……

  • 上一届(第零届)比赛的 “小北问答 1202” 题目会把所有选手提交的答案存到 SQLite 数据库的一个表中,这个表名叫?

去上一届的 Github 存档 可以找到,答案为 submits

  • 国际互联网由许多个自治系统(AS)组成。北京大学有一个自己的自治系统,它的编号是?

直接在 相关网站 搜索即可

  • 截止到 2021 年 6 月 1 日,完全由北京大学信息科学技术学院下属的中文名称最长的实验室叫?

在相关 网页 可以查到,答案为区域光纤通信网与新型光通信系统国家重点实验室

翻车的谜语人

  • 用 wireshark 打开文件,过滤 HTTP 包,可以获得几个 jupyter notebook 文件和一个 flag1.txt 和一个 flag2.7z
  • 分析 jupyter notebook 文件,可以发现把 flag 与一个 key 做了一下异或,注意到 flag1flag2 使用的 key 是不同的,不过都可以从文件中直接得到。
  • 此时就可以求出 flag1 了。直接再异或一次就行。
  • 打开 flag2.7z,可以发现压缩包加密了,且里面是一个 wav 文件。
  • 为了求 flag2,还需要过滤 websocket 包,看到先是用 stego-lsb 工具把 flag2 隐写到一个 wav 文件中,然后再 7z 压缩,密码为 Wakarimasu!`date``uname -nom` `nproc`
  • 从 websocket 的内容可以发现这个计算机叫做 you-kali-vm,从 7z 压缩输出可以看到 CPU 是一个 8 核的 i7-10510u,操作系统是 Linux,从包到达的时间可以知道调用命令的具体时间,于是密码为 Wakarimasu! Sat 06 Nov 2021 03:44:15 PM CST you-kali-vm x86_64 GNU/Linux 8
  • 解压出 wav 文件,然后再用 stego-lsb 提取出 flag2,再和对应的 key 异或一次就能解密出来了。

这题栽在了 PM 上,我所有时区都试过了……

叶子的新歌

  • 观察 MP3 元数据,看到一个可疑内容:aHR0cDovL2xhYi5tYXh4c29mdC5uZXQvY3RmL2xlZ2FjeS50Ynoy
  • base64 解码之,得 http://lab.maxxsoft.net/ctf/legacy.tbz2
  • 下载,得到一个 img 软盘文件
  • 用虚拟机挂载之,得到 flag2 和一个不知是啥的密码

  • 解压缩 img,得到一个压缩包 MEMORY.zip 和一个 NOTE.txt,txt 里提到密码为 宾驭令诠怀驭榕喆艺艺宾庚艺怀喆晾令喆晾怀,搜索相关内容知道这是冠号暗语,解密后解压 MEMORY.zip,得两个 bin 文件和一个含有提示的 txt
  • 根据提示,要找不同。把两个 bin 不同的数据按顺序拼到一块,得到一个文件,文件头有 NES 字样,再根据之前的提示使用 NES 模拟器,发现是个魔改超级玛丽
  • 游玩之(指使用金手指锁命 + 随时存档 + 跳关),通关后获得新提示

  • 打开网站 http://lab.maxxsoft.net/lab/ctf/leafs/,利用之前虚拟机里的密码打开,可获得 flag3

在线解压网站

这个网站可以让我们上传一个压缩文件,网站会帮我们解压。那么可以利用软连接来获得任意文件。在本地做一个软连接文件,连接到根目录下的 flag 文件 (ln -s /flag test),压缩并提交到网站,下载解压后的文件并打开,就可以获得 flag。(甚至没有看源码)

Flag 即服务

/api/ 涉及到访问文件,可以利用。因为 demo.json 位于 /data/ 文件夹,我们需要访问上一级文件夹,可以用 URI 编码 ..%2f 这个漏洞。访问 /api/..%2fpackage.json,根据 json 里的链接下载源码,打开源码看到 flag1 为 flag {${0.1+0.2}},计算一下就得到 flag1(注意 0.3 无法精确表示)

诡异的网关

  • 下载程序,观察发现程序里存储了一个叫 flag 的用户及其密码。显然密码就是我们需要的 flag
  • 添加一个用户,可以发现密码保存在 config 文件中。
  • 对比下 config 文件和 config.xml 文件,发现 config 里的部分 ASCII 字符做了加 1 或减 1 处理。定位到 flag 用户(文件中为 gmaf),发现其密码为 gmafzh1w^did_xNt^f2t_ti3^parrv0Se?|,猜测密码为英文有意义内容,又因为加密后的 ASCII 码与加密前的 ASCII 码最多相差 1,推测 flag 为 flag {h0w_did_yOu_g3t_ti3_passw0Rd?},提交发现正确。(我估计标准答案绝对不是这么做的)

最强大脑

根据源码发现 flag1 保存在 bf 开的数组的最后位置,使用代码 +[>.+](无限循环打印当前指针指向的内容)就可打印出 flag

密码学实践

观察源码,Richard 首先发了两条消息,使用 MESenc 函数加密。观察该函数,发现其是个分块加密,将 key 分为 32 份,循环对一块进行加密,关键代码为

1
2
for key in keys:
a, b, c, d = b, c, d, a ^ c ^ key

显然解密时只要将代码改为

1
2
for key in reversed(keys):
a, b, c, d = d ^ b ^ key, a, b, c

即可。但问题是我们并不知道 key 具体是啥。使用如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
keys = [1<<i for i in range(32)]
a = 1<<32
b = 1<<33
c = 1<<34
d = 1<<35
for key in keys:
a, b, c, d = d ^ b ^ key, a, b, c
print(bin(a),bin(b),bin(c),bin(d))
a = 1<<32
b = 1<<33
c = 1<<34
d = 1<<35
for key in reversed(keys):
a, b, c, d = d ^ b ^ key, a, b, c
print(bin(a),bin(b),bin(c),bin(d))

可以分析出加解密时每个小块的 pattern。可以得到明文和密文的关系 a,b,c,d = a_cip^c_cip^k1^k3, b_cip^d_cip^k2^k4, a_cip^k1, b_cip^k2

其中

1
2
3
4
k1 = a_cip^c
k2 = b_cip^d
k3 = c_cip^a^c
k4 = d_cip^b^d

这样如果我们获得了一个消息的明文和密文,我们就可以对任意密文进行解密了。而 Richard 发来的第二条消息是已知的,那么就可以推出第一条消息的内容,进而获得 flag1

对于 flag2,因为 God 不会给我们名字是 Alice 的人的 cert,可以进行 选择密文攻击。构造一个名字叫做 Alice,密钥是 0 的用户,分别 packmess 后连起来获得 cert(未加密)。然后选择一个整数 \(X\),其与 \(N\) 互质,计算 cert * pow (X,e,N),解压出其 namekey,将 namekey 传给 God,让他帮我们加密,获得加密后的 new_cert。找到 \(X\) 在模 \(N\) 下的逆元,乘以 new_cert,就获得了加密后的 cert。将其传给 Richard,获得了新的消息。由于密钥为 0,所以 MESenc 函数使用的 key 不变,那么利用同样的方法就可以获得 flag2 了。

扫雷

困难难度实际更简单。因为困难模式不会重新生成 board,而 board 是用密码学不安全的 getrandbits 函数生成的。参考 这个链接,我们记录下前 78 局生成的随机数(一局 16*16=256bit,78 局就能获得 624 块的数据了),然后使用 MT19937 随机数预测器(如 这个)就可以推测出后面生成的 board 了。