一只菜鸡关于《Project NANO》的解谜记录

Real entry

网页关键内容:

<!--哦,我的老伙计,你怎么误打误撞到这里来了。-->
<!--认真告诉你,这里没东西,去研究下那个二维码吧。-->
<div class="inside-qr">
    <p>哦,来了,我的老伙计。</p>
    <p>都来到这里了,肯定不能太难是吧。</p>
    <div class="hint">
        <p>所以我要跟你打包票,这一关的题目真的<strong>只在这个二维码</strong>里面了。</p>
        <p>要不,拆开看看?</p>
    </div>
    <img "https://cdn.jsdelivr.net/gh/hanlin-studio/Media@master/vigen.png" />
    <div class="hidden">
        <a href="https://cdn.jsdelivr.net/gh/hanlin-studio/Media@master/vigen.png">打开这个图片</a>
    </div>
</div>

一个二维码图片。下载下来之后跑一遍 binwalk ,发现里面藏着一个 ZIP 压缩包,压缩包里面有个 README 文件,内容如下:

This is a 5*5 n-Puzzle. Please recover it.

Scramble STEP: JNPSVWVGMKHGMEPAMEYJJXZ

根据提示,可以看出图片是被当作拼图来打乱过的。

不过我到最后也没想出来这个打乱步骤的提示是什么意思。知道这个图片是被分成 5×5 的小格子,按照 QR 码的格式,能够先手动拼出其中的 17 格。

这里面不能完全确定但是还有些线索的只有右下角的格子:要求第一个像素必须是黑色的。这样在剩余的 8 个格子里面只有 4 个格子是可能在右下角的,其他七个格子肉眼没法看出线索。

有了这些条件,我们可以推测出剩余的可能性有

4\times7!=2\,0160\text{ 种}

于是把上面的图片拆成了 25 个小图片,写了一个简陋的脚本来暴力破解这两万多种可能性。

(至少比 25!=15511210043330985984000000={15\text{\footnotesize秭}5112\text{\footnotesize垓}1004\text{\footnotesize京}3330\text{\footnotesize兆}9859\text{\footnotesize亿}8400\text{\footnotesize万}} 种可能性要好得多。)

from PIL import Image
from itertools import permutations
from pyzbar.pyzbar import decode
p_0_0 = Image.open("0-0.png")
p_0_1 = Image.open("0-1.png")
p_0_3 = Image.open("0-3.png")
p_0_4 = Image.open("0-4.png")
p_1_0 = Image.open("1-0.png")
p_1_1 = Image.open("1-1.png")
p_1_2 = Image.open("1-2.png")
p_1_3 = Image.open("1-3.png")
p_1_4 = Image.open("1-4.png")
p_2_1 = Image.open("2-1.png")
p_3_0 = Image.open("3-0.png")
p_3_1 = Image.open("3-1.png")
p_3_3 = Image.open("3-3.png")
p_3_4 = Image.open("3-4.png")
p_4_0 = Image.open("4-0.png")
p_4_1 = Image.open("4-1.png")
p_4_3 = Image.open("4-3.png")
p_n_0 = Image.open("n-0.png")
p_n_1 = Image.open("n-1.png")
p_n_2 = Image.open("n-2.png")
p_n_3 = Image.open("n-3.png")
p_y_0 = Image.open("y-0.png")
p_y_1 = Image.open("y-1.png")
p_y_2 = Image.open("y-2.png")
p_y_3 = Image.open("y-3.png")
n = [p_n_0, p_n_1, p_n_2, p_n_3]
y = [p_y_0, p_y_1, p_y_2, p_y_3]
fixed = [
    (0, 0, p_0_0),
    (0, 1, p_0_1),
    (0, 3, p_0_3),
    (0, 4, p_0_4),
    (1, 0, p_1_0),
    (1, 1, p_1_1),
    (1, 2, p_1_2),
    (1, 3, p_1_3),
    (1, 4, p_1_4),
    (2, 1, p_2_1),
    (3, 0, p_3_0),
    (3, 1, p_3_1),
    (3, 3, p_3_3),
    (3, 4, p_3_4),
    (4, 0, p_4_0),
    (4, 1, p_4_1),
    (4, 3, p_4_3),
]
left = [(0, 2), (2, 0), (2, 2), (2, 3), (2, 4), (3, 2), (4, 2)]
d = 20
def join_pic(yy, ns):
    dest: Image = Image.new("RGB", (d*7, d*7), (255,255,255))
    for x, y, img in fixed:
        dest.paste(img, (d * (y + 1), d * (x + 1)))
    for coord, img in zip(left, ns):
        x, y = coord
        dest.paste(img, (d * (y + 1), d * (x + 1)))
    dest.paste(yy, (d*5, d*5))
    return dest
def gen_perm():
    for i in y:
        ii = [j for j in y if j is not i]
        ii.extend(n)
        for j in permutations(ii):
            yield (i, j)
        
for y, ns in gen_perm():
    p = join_pic(y, ns)
    v = decode(p)
    if v:
        print(v)

尝试出来的结果……

python3 test.py
[Decoded(data=b'https://clang.pp.ua/c1An9', type='QRCODE', rect=Rect(left=20, top=20, width=100, height=100), polygon=[Point(x=20, y=119), Point(x=120, y=120), Point(x=119, y=20), Point(x=21, y=21)])]

设计解法:文件名的 vigen.png 提示的是 Vigenère Cipher。用 STEP 当作密钥,JNPSVWVGMKHGMEPAMEYJJXZ 当做密文,可得明文:

RULDDDRRURDRULLLULUUREV

其中:

  • R: right = 右
  • U: up = 上
  • D: down = 下
  • L: left = 左
  • REV 提示「Reverse」,就是反转的意思。具体怎么反转的我也没太仔细研究。(毕竟是暴破出来的。)

按照步骤还原拼图应该就可以了。

(感谢群友 @MiaQAQ 的指点)

Clang

网页主要内容:

<title>./.../.-./---/--</title>
<!-- ... -->
<div id="not-here">
    <p class="what-is-this">这里 和那 里或 许没 什么 关系</p>
    <p><code>1T1023298N757Q13142N10271Q1T1029</code></p>
    <p>或许你需要先看看,嗯,标题。</p>
    <p class="what-is-this">9432bc3b.log</p>
</div>

「日志」内容:

ZeroMe Logs | Mar 27, 2020.
undefined
谜题的出口通向哪里?
来到出口,你抬头看了看门。
忽又想起了 “门上插刀” 这个经典的梗。
刀砸在地上,碎屑溅起来了。
你看了看碎屑,从其中找到了那打开出口的钥匙。
[时序碎片:31 31]
[解锁 丢失的密钥:whereisthefilter]

能够找到的线索:

  • 标题 ./.../.-./---/-- 是摩尔斯电码的 ESROM(Morse 反过来写)
  • 「这里 和那 里或 许没 什么 关系」→两字一组?
  • 1T1023298N757Q13142N10271Q1T1029:不明。
  • whereisthefilter:不是答案,用途不明

「门上插刀」似乎是出题者杜撰出来的一个「梗」。

来自出题团队成员「门上插刀,天上平板,直字拐弯」

看截图来说基本上就是描述日文字形的特点了吧。(是的,同样是汉字,但是不同地方的汉字长得不总是一样的。)

思源黑体中「门上插刀 天上平板 直字拐弯」这十二个字在日本、中国大陆、台湾、香港和韩国的书写习惯的区别对比。「插」、「平」、「板」、「字」、「拐」、「弯」等字的区别没有在此标注。

我说泥萌出题能不能加混淆提示了啦。

于是乎,俺到这里……
……就卡住了。

翌日,得到了出题者给出的提示:

NanoApe「参考解谜之履第三期解析(跑」

网上搜索「解谜之履第三期解析」,得到了一篇微博

当中第九关有着这样的谜题:

战役 Ending with codes

06068N86858488818187858389

Are you seeking the clue which is quite important?
FIRST… YOU MAY TRY ‘ESROM’.

题目原文

观察发现那句英文的空格是不对的,把单词前空了两格的首字母提出了得到【ASCII】,提示使用ASCII码表。把“ESROM”反过来得到【Morse】,提示使用摩斯电码,以及把密文“反过来”。
根据提示操作,把上面超长的字符串转成摩斯电码,再将点线互换,再次转成字符串再对照ASClI码16进制那栏,得到密码【qq:1093662084】
【另外一种解法会认为将摩斯电码逆过来看。但是这种情况下,频繁出现的8会变成2。但是ASC码中,2字打头的除了16进制那栏,都无法对应符号,16进制那栏对应的都是标点符号,所以这种考虑被排除了。至于为什么是先摩斯再ASCII,是因为英语中提到了FIRST。】

引用自题解

所以基本上这道题是同样的意思。(谁能想到 ESROM 代表的反转是把「嘀」和「嗒」换过来了啦摔)所以……

  1. 1T1023298N757Q13142N10271Q1T1029
  2. 转换成摩尔斯电码
  3. 嘀嗒对调
  4. 转换回字母
  5. 按照两字一字节用 ASCII 解码,得到
next: /hizeronet

Hi Zeronet

从本题目以降,感谢群友 Lawson(@N5cA934A)的协作。

网页关键内容:

<div id="not-here">
    <p class="what-is-this" v-bind:title="hint?'真的没有吗…':'哎?不是这个意思?'">No hint</p>
    <p>No hint</p>
    <p class="what-is-this" v-bind:title="'Wcc_b)||UbZh&{R^\|R[P]V|P]^cWTa{iX_'">...</p>
</div>

能看出来的是:

  • URL 里面的提示:Zeronet,一个去中心化的网站托管技术。
  • v-bind:title:这应该是 Vue.js 了。(不过这跟解谜有啥关系?)
  • 'Wcc_b)||UbZh&{R^|R[P]V|P]^cWTa{iX_':一串密码。直觉判断应该是能解出一个 Zeronet 地址的。(Zeronet 地址长得是像一串 Base64 一样的 35 位字符,而这串字符刚好也是 35 位。)

然后就是出题组成员的各种玄妙的提示……

Piggy「这一关不解那个(那 35 个字符的地址)也能做」
Piggy「就是得足够莽(」
Piggy「no hint
Piggy「『真的没有 hint 嘛』」

Piggy 这样强调「hint」这个词我也只能想到那个二分逻辑式(hint?'真的没有吗…':'哎?不是这个意思?')里面把 hint 取伪。但是仍然是毫无头绪。(果然俺还是不适合这种解谜的么 orz)

于是乎,俺到这里……
……刚解出来一关……
……就又卡住了。

翌日,在一番苦思冥想之后,发现了这个是要用脑筋急转弯的思路来解的。就是那种「把冰变成水怎么最快」的思路。所以……

\text{hizeronet}-\text{hint}=\text{zeroe}

然后那串代码什么的基本上就是绕远路解法了(虽然我也不知道怎么才能把那远路绕出来)。

Zeroe

页面主要内容

<div id="here-is">
    <p><small>ZEROMAIL_</small></p>
    <div class="subject">E</div>
    <p><small>From: <a href="mailto:[email protected]">霂</a></small></p>
    <hr>
    <p>告诉你个秘密,其实我挺喜欢对着些温馨些的场景看来看去。</p>
    <p>有时候对着张桌面的照片都能看上半天,那种温暖的感觉便自心底发出。</p>
    <p>要不我给你一段录像,你来看看其中的奥秘?</p>
    <p><a href="https://cdn1.ihcr.top/clang-game/record.rar" target="_blank">[Video File]</a></p>
    <p class="hint">答案就在其中,请坐和放宽。</p>
    <p class="hint">哦对,看的时间别太长了,会出现幻觉的。</p>
    <p class="what-is-this">ffa04ddc.log</p>
</div>

「日志」内容:

ZeroMe Logs | Apr 1, 2020.
abc 19:23:01
所以我们又来到了这个题,你有什么思绪吗?
Piggy 19:23:35
思考
abc 19:24:00
哇,这视频这么长,真要我一天25小时高强度温馨看桌面嘛???
Piggy 19:24:35
一定会有更高效的方法来着,你看这压缩包的体积并不大。
abc 19:26:00
我记得有款现代视频处理工具来着,好像是格式工厂都在用的内核?
先不管了,让我来分析一下这个视频……
Piggy 19:26:53
去搜索引擎找一找吧……
一定不需要一帧一帧出来看的。
[解锁提示: iwhvgqetai]
abc 19:28:00
等等,这是啥?
[时序碎片: 39 33]

那个「现代视频处理工具」、「格式工厂都在用的内核」指的是 FFMpeg1Wikipedia。经过一番搜索,发现 CTF 常用的一个手法就是在长视频里面隐藏 flag。本来视频是有 80 多万帧的,我开始是着每秒只提取一帧,然后逐帧比对,没有比对出什么。但是每一帧都提取我的硬盘会吃不消的。光是这三万多帧的逐帧比对就花了电脑两个多小时,这个数字乘以 25 真的搞不来。

经过群友一只废喵@NebulaNeko 的抓帧,得到以下两帧内容:

Wow_Awes0me_
ViDeo_Proc355ing

事后谈

  1. FFMpeg 有一个 mpdecimate 的 filter 专门用来解去除重复画面的。当时不知怎的没搜到。
  2. 群友 Suzunari Shizuku@GPLv3 提示说「给了一串神秘字符,用之前关卡出现的 key 解密可以得到 hint」。拿着之前日志里面的「iwhvgqetai」和 Clang 的日志里面给的「whereisthefilter」,塞进他们喜欢的Vigenère Cipher,就真的给出了 mpdecimate 这个提示。

Wow awesome video processing

页面主要内容:

ZeroMe Logs | Apr 2, 2020.
[部分讯息经过加密]
泠 17:32:13
看到这个「Project NANO」,你有啥想法没?
霂 17:32:59
有啊有啊,看到这名字我想到了[Uvebfuvzn]的一家餐馆,是意大利风味的。
泠 17:33:55
......?那这家餐馆在哪呢?
霂 17:34:25
知道知道,我这就去给你搜搜......
霂 17:36:58
你要不先联系一下他们的老板吧?
泠 17:37:55
行,你把电话给我吧。
答案为纯数字电话号码,或许是国际长途。
请不要因此拨通任何号码,给他人带来不必要的麻烦。
答案验证框还在老地方,别看我。
似乎答案不对?多找找或者换换格式。
> |

没有任何提示,经过各种试错,发现加密方式是 ROT13。

ROT13(Uvebfuvzn) = Hiroshima

Hiroshima = 广岛县,搜索广岛县叫做 NANO 的意大利餐馆,会找到一个这个:

带着国家代码把电话号码输进去,就是答案。

页: 1 2 3


Comments

《 “一只菜鸡关于《Project NANO》的解谜记录” 》 有 2 条评论

  1. 大学学的信息安全,就像是其他专业有acm,每年学长都会给我们准备ctf的题。
    回想起来当时和同学通宵做题抢分,真是有意思。
    一转眼都毕业三年了。

  2. […] 之前写过了一篇选手视角的题解,亦可以作为参考:一只菜鸡关于《Project NANO》的解谜记录 […]

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

To respond on your own website, enter the URL of your response which should contain a link to this post’s permalink URL. Your response will then appear (possibly after moderation) on this page. Want to update or remove your response? Update or delete your post and re-enter your post’s URL again. (Find out more about Webmentions.)