Skip to content

Commit c815ec7

Browse files
committed
add monad's writeups
1 parent 88a997f commit c815ec7

26 files changed

+773
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
| [4qwerty7](players/4qwerty7/writeup.md) | 总排名第 2 名 | 签到、进制十六——参上、去吧!追寻自由的电波、猫咪问答 Pro Max、卖瓜、透明的文件、旅行照片、FLAG 助力大红包、Amnesia第一问、轻度失忆、记忆清除、图之上的信息、Easy RSA、加密的 U 盘、赛博厨房、灯,等灯等灯第一问、只读文件系统、一石二鸟、Micro World、卷王与野生的 GPA、阵列恢复大师、链上预言家、助记词、Co-Program、马赛克、minecRaft、密码生成器、外星人的音游掌机、JUST BE FUN、fzuu、p😭q、超 OI 的 Writeup 模拟器前两问 |
7676
| [zhixi](https://zhangjk98.xyz/hackergame-2021-write-up/) | 总排名第 187 名 | 签到、进制十六——参上、去吧!追寻自由的电波、猫咪问答 Pro Max、卖瓜、旅行照片、Amnesia{轻度失忆}、图之上的信息、赛博厨房前两问、p😭q |
7777
| [zxx](players/zxx/README.md) | 总排名第 307 名 | 进制十六 -- 参上, 去吧!追寻自由的电波,FLAG 助力大红包,minecRaft |
78+
| [monad](players/monad/) | 总排名第 14 名 | 签到、进制十六——参上、去吧!追寻自由的电波、猫咪问答 Pro Max、透明的文件、旅行照片、FLAG 助力大红包、Amnesia-轻度失忆、图之上的信息、Easy RSA、加密的 U 盘、赛博厨房(Lv0-2)、Micro World、助记词、密码生成器、p😭q、(还在更新中) |
7879

7980
## 其他资源
8081

players/monad/0-签到.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# 签到
2+
3+
点按钮可以切换 Page,然后不同的 Page 对应不同时间。
4+
5+
并且可以观察到,Page 的 number 就是 Unix 时间戳。
6+
7+
<del>我们不妨大胆猜想</del>,把 Unix 时间戳设置成当前时间,即访问 `http://202.38.93.111:10000/?page=1634968833`,然后发现就可以获得 Flag。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# 进制十六——参上
2+
3+
观察到右边的 flag 被遮住了,但是左边的 hex 却没有被遮住。所以就可以解析左边的 hex 得到右边的 flag:
4+
5+
```python
6+
codecs.decode(
7+
'666c61677b5930555f5348305531445f6b6e30775f4830575f74305f43306e763372745f4845585f746f5f546578547d',
8+
'hex')
9+
```

players/monad/10-Easy RSA.md

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Easy RSA
2+
3+
**公式不渲染警告**,建议移步至[我的博客](https://blog.monadx.com/2021/10/31/Hackergame-2021/#easy-rsa)
4+
5+
观察代码,我们有三个任务:
6+
7+
1. 计算 `p`
8+
2. 通过 `value[-1]` 反推全部 `value`
9+
3. 通过 `value_q` 反推 `q`
10+
11+
## 1. 计算 `p`
12+
13+
查找资料,可以找到一个叫“威尔逊定理”的东西,即当 `p` 为质数的时候,有:
14+
15+
$$
16+
(p - 2)! \equiv 1 \enspace (\operatorname{mod} p)
17+
$$
18+
19+
则:
20+
21+
$$
22+
y! \equiv (x - 2)! \times \frac{y!}{(x - 2)!} \equiv \frac{1}{\prod_{i = y + 1}^{x - 2} i} \equiv \prod_{i = y + 1}^{x - 2} inv(i) \quad (\operatorname{mod} x)
23+
$$
24+
25+
代码:
26+
27+
```python
28+
def get_p(x, y):
29+
mul = 1
30+
for i in range(y + 1, x - 2 + 1):
31+
mul = mul * pow(i, x, x - 2) % x
32+
return mul
33+
```
34+
35+
## 2. 反推 `value`
36+
37+
这个部分涉及到一个最主要的函数就是 `sympy.nextprime`,先看看它的文档:
38+
39+
> `def nextprime(n, ith = 1)`
40+
> Return the ith prime greater than n.
41+
> See Also: prevprime: Return the largest prime smaller than n.
42+
43+
……我感觉官方文档都告诉你应该怎么做了。
44+
45+
```python
46+
def get_first_value(last_value):
47+
val = last_value
48+
for _ in range(9):
49+
val = sympy.prevprime(val)
50+
return val
51+
```
52+
53+
## 3. 反推原始 `q`
54+
55+
这个部分就是给出这个式子(我们把这个原始的 `q` 称为 `x`):
56+
57+
$$
58+
q \equiv x^e \enspace (\operatorname{mod} n)
59+
$$
60+
61+
已知 `e`, `n`, `q`,求 `x`
62+
63+
然而我不会求,查资料,发现了这个:[Finding the k-th root modulo m](https://math.stackexchange.com/questions/2073284/finding-the-kth-root-modulo-m),简直完美。
64+
65+
按照这篇文章(回答)所说,首先求满足这一式子的 $k$, $v$:
66+
67+
$$
68+
u \, e - v \, \varphi(n) = 1
69+
$$
70+
71+
显然 $\gcd(e, \, \varphi(n)) = 1$,这一步可以用 exgcd 做。
72+
73+
求出来了之后,就有
74+
75+
$$
76+
x \equiv (b^u)^e \enspace (\operatorname{mod} n)
77+
$$
78+
79+
写成代码,就是:
80+
81+
```python
82+
# 这里的 n 我们通过 `first_value` 生成
83+
def get_original_q(e, q, first_value):
84+
value = [ first_value ]
85+
n = first_value
86+
for i in range(1, 10):
87+
value.append(sympy.nextprime(value[i - 1]))
88+
n *= value[-1]
89+
90+
# 这样求 phi 可以快不少,该函数要求 factors 中的 keys 都是质数。
91+
factors = { v: 1 for v in value }
92+
pn = sympy.ntheory.factor_.totient._from_factors(factors)
93+
94+
# Ex-GCD, 其中 h = gcd(e, pn);u, v 含义如上所述。
95+
(u, v, h) = sympy.polys.polytools.gcdex(e, pn)
96+
97+
x = pow(q, int(u) * e, n)
98+
return x
99+
```
100+
101+
经过验证,以上三个脚本都可以算出的结果,代入进源程序中,都可以算出源码中对应的结果。
102+
103+
## 4. RSA 解密
104+
105+
RSA 解密还需要一个数 $d$(含义请自行翻看 RSA 文档,也可以看[这篇](https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html))。
106+
107+
算 $d$ 满足以下式子:
108+
109+
$$
110+
ed \equiv 1 \enspace (\operatorname{mod} n)
111+
$$
112+
113+
在先进的 `Python 3.8+` 中,可以直接用 `pow(x, -1, mod)` 来算逆元,省事不少。
114+
115+
于是解密部分的代码:
116+
117+
```python
118+
pn = sympy.ntheory.factor_.totient._from_factors({ p: 1, q: 1 }) # phi(n)
119+
120+
d = pow(e, -1, pn)
121+
122+
c = 110644875422336073...
123+
m = pow(c, d, p * q)
124+
print(m)
125+
print(m.to_bytes(32, "big"))
126+
```
127+
128+
然后就可以拿到 flag 了。

players/monad/11-加密的 U 盘.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# 加密的 U 盘
2+
3+
通过查询 LUKS 的使用文档,可以发现其可以使用密码或 keyfile 加密,于是大胆猜测改密码不会改变 keyfile 一类的东西(注:实际上是 master key 不会变)。
4+
5+
首先使用 `losetup -P /dev/loop1 day1.img` 将镜像挂载至 loopback(day2 同理)。
6+
7+
然后用 `cryptsetup luksDump --dump-master-key /dev/loop1p1` dump 出 master key。`MK dump` 里就是 master key。并用 `xxd` 或其它工具将 master key 写入文件。
8+
9+
![](assets/Luks_masterkey_dump.png)
10+
11+
接着用 master key 打开 `day2.img` 即可:`sudo cryptsetup luksOpen --master-key-file <key file> /dev/loop2p1 day2`
12+
13+
最后把 `/dev/mapper/day2` mount 一下就能读出 `flag.txt` 了。
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# 赛博厨房
2+
3+
## Level 0
4+
5+
写 4 个程序,针对不同菜谱执行不同程序即可。
6+
7+
```
8+
向右 x 步
9+
拿起 1 个物品
10+
向下 1 步
11+
向左 x 步
12+
放下 1 个物品
13+
向上 1 步
14+
向右 y 步
15+
拿起 1 个物品
16+
向下 1 步
17+
向左 y 步
18+
放下 1 个物品
19+
```
20+
21+
自行修改 `x`, `y` = `1` or `2` 来控制不同菜谱即可。
22+
23+
## Level 1
24+
25+
写个简单循环,把物品不断放到锅中即可。
26+
27+
```
28+
向右 1 步
29+
拿起 100 个物品
30+
向下 1 步
31+
向左 1 步
32+
放下 1 个物品
33+
如果手上的物品大于等于 1 向上跳转 1 行
34+
```
35+
36+
## Level 2
37+
38+
写 32 个程序,第 i 个程序分对应 `[0, 0, 0, 0, 0, i]` 这个菜谱。
39+
然后再额外加一个程序,写 `向右 {} 步`,然后枚举数字,撞 hash(实际上是两个 sha256 和一个 arc4 随机?)。
40+
41+
暴力代码见:https://gist.github.com/YanWQ-monad/4febc8f8635514459a981adfefb1b9f1

players/monad/16-Micro World.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Micro World
2+
3+
<del>随便乱瞅一下(我不会告诉你我走了多少弯路的)</del>,能发现这个程序是用 pyinstaller 生成的。
4+
5+
然后就找一个 pyinstaller unpacker(我用的是 [extremecoders-re/pyinstxtractor](https://github.com/extremecoders-re/pyinstxtractor)),然后就可以发现一个 `2.pyc` 文件。用 `pip` 安装 `pygame` 后,发现可以直接用 `python 2.pyc` 运行,能确定提取出了正确的东西。
6+
7+
接着继续尝试反编译,我这里找了 [zrax/pycdc](https://github.com/zrax/pycdc),能够逆向 Python 3.9 的字节码,但不完全能逆向出来(但是至少能看到大致的逻辑,也能看到点的数据)。
8+
9+
这是 `2.pyc``Point` 的定义:
10+
11+
```python
12+
class Point:
13+
def __init__(self, pos, vx, vy):
14+
(self.x, self.y) = pos
15+
self.vx = vx
16+
self.vy = vy
17+
```
18+
19+
不难猜出,`vx``vy` 就是这个点的移动速度(也就是 `list_` 每个元素的后两个值)。显然,只要把这个速度取相反数就可以还原出原 flag 图案了(显然碰撞是可逆的)。
20+
21+
然而我并不想对着 bytecode 翻译一遍代码,所以我决定动态“注入”到 `2.pyc` 里面改变量。下面我们先把 `2.pyc` 重命名成 `w2.pyc`,因为不能直接 `import 2`
22+
23+
但是 `import w2` 后,`import` 没返回,窗口已经运行了,有点难改变量。下面开始乱搞!
24+
25+
我们可以新建一个 `pygamx.py` 来 mock `pygame`,然后修改 `w2.pyc` 的 bytecode:暴力把 `pygame` 改成 `pygamx`(暴力替换二进制数据即可)。
26+
27+
`pygamx.py`
28+
29+
```python
30+
import pygame
31+
32+
class Time:
33+
class Clock:
34+
def init(self):
35+
self.first = True
36+
self.clock = pygame.time.Clock()
37+
38+
def tick(self, fps):
39+
# 手动控制每一帧
40+
input()
41+
# self.clock.tick(fps)
42+
43+
if self.first:
44+
self.first = False
45+
46+
# 经过测试,可以在这里反向 import 并访问 Pointlist 而不抛出 AttributeError
47+
import w2
48+
for i in range(len(w2.Pointlist)):
49+
p = w2.Pointlist[i]
50+
w2.Pointlist[i] = w2.Point((p.x, p.y), -p.vx, -p.vy)
51+
52+
time = Time()
53+
54+
# mock attributes
55+
init = pygame.init
56+
quit = pygame.quit
57+
mixer = pygame.mixer
58+
display = pygame.display
59+
event = pygame.event
60+
draw = pygame.draw
61+
QUIT = pygame.QUIT
62+
```
63+
64+
然后经过测试,可以在上面 `pygame.time.Clock.tick` 里面反向 `import w2`,修改 `list_``Pointlist`,把速度取反,就可以了。
65+
66+
此时直接运行 `python w2.pyc`,并且在控制台窗口用回车控制每一帧,看到 flag 差不多展示出来了就行了:
67+
68+
![](assets/microworld_flag.png)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# 去吧!追寻自由的电波
2+
下载音频,发现音频播放速度非常快,大概是被暴力压缩了。
3+
4+
我们可以随便找点软件(我用了手边的 iMovie),把音频放慢,就可以听了。
5+
6+
题目云「使用了无线电中惯用的方法来区分字符串中读音相近的字母」,即 `e``echo` 表示,等。
7+
8+
虽然英语听力不太行,但是听首字母还是能勉强听得出来的。

0 commit comments

Comments
 (0)