题面
这么多年,总算是把新年红包的题弄出来了。值此除旧迎新之际,希望大家新年快乐,玩得开心(x
地址:https://triangle-2022.mmf.moe/
解密平台基于 Triangle 创建,不过估计明年还得重构如果明年还有
如果提示红包码不存在,请在本文下留言,并附上题目的编号(如 #3
)。为了延长红包的存活时间(24小时),后面题的红包我会在前面题有人做出来之后再发(
如果网址打不开,请检查自身与 Cloudflare IP 的连通性情况,可以更换网络条件重试(如流量);也可以 尝试等待一段时间(可能是 DNS 没有生效)。
赛后题面将会公布于 https://github.com/Yesterday17/triangle-2022,WriteUp 则会在本文更新。
Hints
- #2 中1指的是0x1,表示的是加密时的顺序。工具建议使用 010Editor 和 CyberChef
- #4 中 UUID(不含前后缀)的 3、4和5、6位,19和20位、29和30位的位置需要交换(不含横杠,从1开始),希望 jxw 赶快重构
- 追记:19:50 群友已经 AK 了(
- #3 不涉及条件竞争
Writeup
红包生效时间过了,也该发 WriteUp 了(笑)
#1 サヨナラから始まる物語
作为签到题,flag
是通过藏头的方式隐藏起来的。简单拼接一下,顺利拿到 32 位的 hex。提示里说到 UUID 可能是 32 位无横线的形式,其实就是这题用到了。
得到的结果是 2fb4552d826a4e288ef4-911a719b271a
,可以直接作为地址使用,也可以自行补上横线。
「サヨナラから始まる物語」是 「IDOLY PRIDE Collection Album [約束]」 的第一首,怀揣着种种思绪唉告别旧的一年,希望新的一年大家能够好好活着。
#2 MOLE
这题的起源是 MORA
上下载的音频里总是有一些意味不明的 UUID,再加上 Project Anni 对 FLAC 的研究,于是就出现了这一题。
Hint 里提示了可以使用 010Editor,但实际上我推荐的是 anni
。输入:
anni flac export -t=all mole.flac
结果如下:
METADATA block #0
type: 0 (STREAMINFO)
is last: false
length: 34
minimum blocksize: 4608 samples
maximum blocksize: 4608 samples
minimum framesize: 3392 bytes
maximum framesize: 23824 bytes
sample_rate: 48000 Hz
channels: 2
bits-per-sample: 24
total samples: 5916672
MD5 signature: c420c52e09e69781bd746c70177bb07d
METADATA block #1
type: 4 (VORBIS_COMMENT)
is last: false
length: 139
vendor string: rot13(xor(1))
comments: 3
comment[0]: title=Rick Astley - Never Gonna Give You Up [HQ]
comment[1]: encoder=Lavf58.76.100
comment[2]: q5415ps8,853q,5703,n453,q84227or26n8=
METADATA block #2
type: 1 (PADDING)
is last: true
length: 8151
可以看到,METADATA block
中的 vendor string
是 rot13(xor(1))
,而 comment[2]
是一个奇怪的字符串。于是按照 vendor string
中的编码方式反推回去:
得到下一题的 UUID
:c4504de9-942c-4612-b542-c95336af37b9
。
#3 Touch The Flag
本题的源码在:https://github.com/Yesterday17/triangle-2022/tree/master/layered-flag-toucher
之前在公司有听到 OverlayFS 相关的内容,印象中 Rust 的 VFS 好像也有相关的内容,于是就去具体了解了一下。结果不看不知道,一看发现这实现还是有不少问题的。
OverlayFS
简单来说就是一个分层的文件系统。在这个文件系统中,只有最顶层是可读可写的,除此之外的各层均为只读。在读取时,指定的文件名如果在上层存在,就返回上层的文件内容,否则就依次在下层寻找,直到最底层也不存在对应文件时表示文件不存在。
概念很明确,但 Rust VFS 中的实现却非常 buggy,如:
所有和路径相关的操作都会用到 read_path
,但却通过 [1..]
截掉了第一个字符,表示限定了只能使用绝对路径。
另一个就是本题利用的核心:
为了表示文件的删除,VFS 的实现使用了 whiteout
的概念。在 .whiteout
目录下如果存在对应的文件,则表示该文件实际已被删除。通过这种方式,实现了文件的“隐藏”。
这种实现出现是因为 Overlay 的下层是只读的,只有顶层可写。于是 Rust VFS 就选择在可写的 write_layer
上通过 .whiteout
目录的方式写入一个用于隐藏文件的目录。但!是!这个 .whiteout
目录也是可以正常写入的。于是就造成一个问题,在特定条件下,用户可以通过在 .whiteout
目录中「创建」文件,达到形式上「删除」文件的效果。
分析
题目给出了两个接口,一个是读取 flag
的 /flag
,一个是创建文件的 /touch
。于是根据上图中的命名规则,我们只需要创建 flag.lock
对应的 flag.lock_wo
就可以了:
然后就可以读到 flag
了:
#4 Minyami
艺术来源于生活
对 Minyami
的介绍可以去看网商的这篇博客。网商到底有哪些人.jpg
简而言之,Minyami
就是一个多线程下载 m3u8
的工具。m3u8
是播放列表,指向实际的播放视频分块(ts
)。在直播情况下,通过对 m3u8
的不断请求就可以得到一份持续更新的播放列表,其中的 ts
就对应 了直播流。m3u8
中 ts
的顺序就代表了实际拼接时的块顺序。
如果下载的过程没有任何问题倒还好,但如果遇到了错误,该怎么办呢?Minyam
i 有错误重试的功能,在错误情况下可以通过重试。但如果一直失败呢?在我当时使用的版本,某一个块的错误就会导致不断的重试,进而使得 Minyami
无法进入下一步的合并阶段,而合并所需要的块顺序又只在内存中存在。如果你下的源对应的 ts
又正好不是顺序数字命名,那么恭喜——你这个源就下废了(
当时我也试图通过日志分析的形式对视频进行复原,但最终还是失败了,于是就有了这次的这道题。题目对应的源码在 https://github.com/Yesterday17/triangle-2022/tree/master/minyami,是一个简单的 http 服务器,接口有返回 m3u8
的 /
和返回 ts
的 /flag_{id}.ts
,flag
的每一位对应一个块文件。对块文件的请求有 2/3
的概率会返回 403 Forbidden
,只有 1/3
的概率会正常返回文件。
我们知道,Minyami
是顺序尝试下载块的,因此下载和失败的顺序一定程度上可以反映实际的块顺序。因此我们将日志文件中所有出现的块文件名称题取出来,并进行 unique
操作,只保留第一次出现。
将得到的块顺序保存在一个文件中(如 r.txt
),并使用命令输出:
cat r.txt | xargs cat
得到一个看上去非常像是正确答案的东西:mmf{bd77014d-f4a1-475f-ae3c-8f5019c498ee}
。但它不对(逃
查看 Hint,我们把 flag
中的某些位调换一下位置,就可以拿到真正的 UUID
了。
结语
整场的领取情况是:14/30,11/15,3/8,2/5;四个红包的面额分别是 1.14,5.14,8.10,19.19(哼,哼,啊啊啊啊啊啊啊啊啊
红包的题面有一些陈述不清的地方,不过在群友们的帮助下在 Hint
里补了不少澄清和提示。毕竟是图一乐的玩耍场,#1 和 #2 本来是想着大家开心一下的,没想到没领完,明年还要弄简单点
我的本意是把红包码放在 HTML
的注释里的,这样既不影响截图也不影响领取。结果吃完年夜饭一看没人领,后来才发现是 Cloudflare
的压缩把注释都干掉了,于是又加急放回了题面里,麻了。
第三题的环境一开始没有重置,导致在 KAAAsS
AK 了之后后续的解题者就算有了正确答案也会显示 flag is stolen
。后来才补上了脚本。
总而言之,这次的题面我个人还是比较满意的,想到的脑洞基本上都弄出来了#4 应该是比 #3 要简单上一些,但不知道为什么有人只做了 #3(。这次题面里没有逆向,整体也偏 MISC 一些,希望明年能百花齐放两开花。或者也可以跟群友一起出?(
总之,虎年也如期而至。在这篇咕到初二才出来的 WP 里,给各位拜个晚年,新年快乐!
平台择日下线,想玩的这几天还能复现一下ww
多线程太神奇了.jpg