Story #0

0.1

点击开始,网页跳转到?level=key

0.2

文字闪烁,最后变成?level=time

0.3

打开控制台,可以看到输出

Hey inspector 🕵️, next level is ?level=classes

0.4

随意填写,提示

你的答案不正确,请再试一次。(哎,你听说过 World of Warcraft 吗?

搜索Vitalik Buterin ETH World of Warcraft,找到一篇文章

从文中我们可以得知被削弱的角色名称为warlock,将答案填入得到?level=cicada3301

Story #1

1.1

检查图片,发现 alt 信息:7 is the magic number

下载图片,使用二进制编辑器(如 010editor、winhex)查看文件末尾得到字符串

CAESAR says Ò?slcls=ihshujl

容易联想到凯撒密码,结合前面的 alt 信息,容易知道凯撒密码的偏移量为 7(实际上凯撒密码偏移量有限,可以枚举)。

得到解密结果:?level=balance

1.2

根据提示:想要财富,你得尝试拥有财富!

尝试点击最上方的 Buy 按钮,再点击 Buy,此时下方会短暂出现彩色字符串:?level=console

1.3

考虑到 url 中的 console,打开控制台,得到提示:

David A. Huffman:
 Smart kid, I've got something for you.
 Use these tools wisely: `frequency`, and `getTree(freq: [string, number][])`.
 Each one is a key to unlocking the secrets of Huffman coding.
 Are you ready to accept the challenge and decrypt the message?

You can try typing `frequency` or `getTree`
> frequency
'[["e",13],[" ",11],["t",10],["l",6],["i",6],["r",5],["s",5],["c",4],["y",4],["a",3],["n",3],["u",3],["o",3],["d",2],["v",2],["m",2],["p",2],["w",1],["z",1],["f",1]]'

> getTree([["e",13],[" ",11],["t",10],["l",6],["i",6],["r",5],["s",5],["c",4],["y",4],["a",3],["n",3],["u",3],["o",3],["d",2],["v",2],["m",2],["p",2],["w",1],["z",1],["f",1]])
{
    weight: 87
    ...
}

这棵树有多重? 🌲的答案自然就是 87。

得到 Story #1 的钱包助记词和下一关的线索:?level=nameless

Story #2

2.1

将图片拖动开,发现图片下方有一行文字:

"/shares/mint" > 取一个好听的名字吧!

根据漫画内容:Johan was such a wonderful name, too.

尝试在 Name 项填写 johan,得到提示:

johan, it's wornderful name!"/shares/1?level=johan"

同时还需要注意到左侧的图片拖动开,下方有下一个环节的提示:

負貳拾柒點壹貳伍捌
負壹佰零玖點叁肆玖柒
puzzle
story#2

2.2

根据上一题得到的线索和这一题框内的格式,填入-27.1258,-109.3497

得到下一关的线索:?level=ffmpeg

2.3

没有名字的怪物下方有对比度较低的链接:>download it<

下载视频后,右键属性,查看详细信息(Windows),在备注中发现:

JOHAN SAY I'M HERE: aHR0cHM6Ly9maWxlLmlvL3dIdlBGUFhaUTEyQg==

base64 解码得到:https://file.io/wHvPFXQaQ12B

由于此文件阅后即焚,后面的部分我没有做。

Story #3

3.1

“回家”可以点击,点击后会跳转到/shares/simulator

不过这道题我没看出来怎么得到下一步 ?level=income的,所以在 js 里面直接看路由!(。

好,这下全出现了 /doge。

case "income":
    return {
        level: "3.1",
        name: "income",
        puzzleId: 3,
        puzzleStep: 1
    };
case "prime":
    return {
        level: "4.0",
        name: "prime",
        puzzleId: 4,
        puzzleStep: 0
    };
case "rsa":
    return {
        level: "4.1",
        name: "rsa",
        puzzleId: 4,
        puzzleStep: 1
    };

3.2

将图片拖动移开,下方有低对比度提示:

5, 2, 3.9, 611, 5, 5, 1.5, 30, 20

将这些数字作为参数输入之前点击回家进入的/shares/simulator(准确来说只用修改 seed)。

将 ETH 的 INCOME 值 3.31 填入,得到下一关线索 ?level=prime

Story #4

4.1

同样 js 逆向,全局搜索这两串二进制字符,发现可疑函数

function r(e, s) {
    let l = e.padStart(s, "0").split("").map(e=>"0" === e ? "1" : "0").join("");
    return BigInt("0b".concat(l))
}

返回出打断点,随意输入并提交,得到两个素数:

p = 5999999999999999572748252001150206112289036460627182991960108783775851893058681618663736251
q = 999999999999999966484112715463900049825186092620125502979674597309179755437379230686901031

提交得到下一关线索 ?level=rsae = 65537

4.2

容易想到是使用 RSA 解密,不过我用 python 不知道为啥解不出来,把私钥导出来去在线网站解密。

import base64

p = 5999999999999999572748252001150206112289036460627182991960108783775851893058681618663736251
q = 999999999999999966484112715463900049825186092620125502979674597309179755437379230686901031
e = 65537
message = "AMVgHu7BK1/aEisp46Fg2y5x28OSM/rofwtQrjT0J/L0TXACdLP1uDMoGoq9C3hozEzDzysV/VTzLwxdMKKHP13IBitX1O6gUTYczg=="

import gmpy2
from Crypto.Util.number import long_to_bytes, bytes_to_long

c = bytes_to_long(base64.b64decode(message))
n = q * p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))


import rsa

private_key = rsa.PrivateKey(n, e, d, p, q)
print(private_key.save_pkcs1("PEM").decode())

print("p=0x%x" % p)
print("q=0x%x" % q)
print("e=0x%x" % e)
print("d=0x%x" % d)

解密得到DM @ashu_mest with pqed

去 Twitter 私信作者 RSA 的参数p、q、e、d即可。