PKU GeekGame 3rd writeup

文章发布时间:

最后更新时间:

哎呀混分来了, 启动启动

一眼盯帧

盯帧得字符串

1
synt{jrypbzrarjcynlref}

简单凯撒, 偏移 13

1
flag{welcomenewplayers}

小北问答!!!!!

  1. sbatch https://hpc.pku.edu.cn/_book/guide/quickStart.html
  2. 5.15.78
  3. Watch6,16 https://theapplewiki.com/wiki/N198sAP
  4. 4472 代码在这里, 但是结果好像还要找 linux 跑一跑, Python 版本不一样好像结果也不一样, 好难
  5. 游戏视频,游戏攻略·解说,Mugen,Flash游戏 https://web.archive.org/web/20110118084150/http://bilibili.us/video/game.html
  6. philharmonie.lu

好难啊这个 flag{ken-ding-xiang-zai-wo-shou-zhong!}

麦恩·库拉夫特

前两个 flag 在地图里找找就能找到, 至尊无敌 XaerosWorldmap

最后一个 flag 存在比较器链里, 用 NBT 的读取器 (网上随便找的) 把区块的二进制数据解成 SNBT 然后用简单处理一下得到数据, 可惜发现是图片, 做到一半不想做了

Emoji Wordle

flag1

遍历一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import re
import requests

re_RES = re.compile(r'results.push\("([🟥🟩🟨]{64})')

def check_emoji(char, f):

ans = f
s = requests.get("https://prob14.geekgame.pku.edu.cn/level1", params={ "guess": char * 64 })

if check := re_RES.search(s.text):
res = check[1]
for i in range(64):
if res[i] == "🟩":
ans = ans[:i] + char + ans[i+1:]
return ans

flag2

根据提示看一眼 jwt, payload 里 datatarget 字段

啊? 这就完了?

flag3

不会, 随机数来了

Later: 好像拿着一次的 jwt 一直猜, 猜到答案了就行

第三新XSS

flag1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>

<head>
<title>I'm the admin</title>
<script type="text/javascript">
setTimeout(() => { document.title = document.getElementById("admin").contentDocument.cookie;}, 200);
</script>
</head>

<body>
<h1>flag{xmcp_is_W4TCH1NG_Y0U}</h1>
<iframe src="/admin" id="admin"></iframe>
</body>

</html>

flag2

顺序变了不会做, 本地测试的时候不管怎么测都是不让注册 ServiceWorker, 遂放弃

简单的打字稿

我居然能做这个

看了一眼实现拿正则无视大小写检查输出是否含有 flag 字串, 去掉即可

flag1:

1
2
3
type f<T> = T extends `flag${infer U}` ? U : never;
type f1 = f<flag1>
let a: f1 = '';

错误信息就来咯

error: TS2322 [ERROR]: Type '""' is not assignable to type '"{xxxxxx}"'. let a: f1 = '';

好在这个 flag 括号里面没有 flag 字串了, 没做额外处理

flag2:

好难啊我还没咋写过 TypeScript 这会不会太难了点

非法所得

flag1

发现可以引用外部文件

1
2
3
4
5
6
7
8
9
10
proxy-groups:
-
name: "你们这是违法行为"
type: select
use:
- flag
proxy-providers:
flag:
type: file
path: /app/profiles/flag.yml

flag2

重定向 ys.pku.edu.cn 到我的服务器, 然后塞一个带脚本的简单 html 就行

比较好笑的是不管我怎么调那个密码框就是长度不够显示全部 flag, 最后干脆直接放到 document 里新建一个 <h1>

汉化绿色版免费下载

flag1

用 xp3_upk 解包 .xp3 文件在 scenario/done.ks 中可以找到 flag1

flag2

解包能看到 hash 计算相关逻辑, 用 Cheat Engine 查内存获取到 prev_hash 并根据逻辑反推

看了一眼提示, 用工具把存档压缩关了重新开存档保存发现没有保存历史信息, 只能暴力搜索

哎呀搜了长度小于 12 的咋没一个对的啊

看了提示, 不会, 下一题!

关键词过滤喵,谢谢喵

手写了个转换器, tcg

flag1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
RE [\s]{100000} => s{100000}
RE [\s]{10000} => s{10000}
RE [\s]{1000} => s{1000}
RE [\s]{10} => s{10}
RE [\s] => s
$ => 😡0⭕️😅
count10e5:
if no [^😡]{100000}😡 goto skip10e5
[^😡]{100000}😡 => 😡
add10e5:
if 9⭕️ goto carry
8⭕️ => 9⭕️
7⭕️ => 8⭕️
6⭕️ => 7⭕️
5⭕️ => 6⭕️
4⭕️ => 5⭕️
3⭕️ => 4⭕️
2⭕️ => 3⭕️
1⭕️ => 2⭕️
0⭕️ => 1⭕️
😡⭕️ => 😡1⭕️
if . goto uncarry
carry:
9⭕️ => ⭕️0
if . goto add10e5
uncarry:
RE ⭕️0 => 0⭕️
if . goto count10e5
skip10e5:
rm ⭕️
😅 => 0😅
count10e4:
if no [^😡]{10000}😡 goto skip10e4
[^😡]{10000}😡 => 😡
8😅 => 9😅
7😅 => 8😅
6😅 => 7😅
5😅 => 6😅
4😅 => 5😅
3😅 => 4😅
2😅 => 3😅
1😅 => 2😅
0😅 => 1😅
if . goto count10e4
skip10e4:
😅 => 0😅
count10e3:
if no [^😡]{1000}😡 goto skip10e3
[^😡]{1000}😡 => 😡
8😅 => 9😅
7😅 => 8😅
6😅 => 7😅
5😅 => 6😅
4😅 => 5😅
3😅 => 4😅
2😅 => 3😅
1😅 => 2😅
0😅 => 1😅
if . goto count10e3
skip10e3:
😅 => 0😅
count10e2:
if no [^😡]{100}😡 goto skip10e2
[^😡]{100}😡 => 😡
8😅 => 9😅
7😅 => 8😅
6😅 => 7😅
5😅 => 6😅
4😅 => 5😅
3😅 => 4😅
2😅 => 3😅
1😅 => 2😅
0😅 => 1😅
if . goto count10e2
skip10e2:
😅 => 0😅
count10:
if no [^😡]{10}😡 goto skip10
[^😡]{10}😡 => 😡
8😅 => 9😅
7😅 => 8😅
6😅 => 7😅
5😅 => 6😅
4😅 => 5😅
3😅 => 4😅
2😅 => 3😅
1😅 => 2😅
0😅 => 1😅
if . goto count10
skip10:
😅 => 0😅
count1:
if no [^😡]😡 goto skip1
[^😡]😡 => 😡
8😅 => 9😅
7😅 => 8😅
6😅 => 7😅
5😅 => 6😅
4😅 => 5😅
3😅 => 4😅
2😅 => 3😅
1😅 => 2😅
0😅 => 1😅
if . goto count1
skip1:
end:
rm 😡
rm 😅
RE rm ^0
^$ => 0

这时候对各种 regex 的东西还不是很熟

flag2

哎呀 , 能过就行

1
2
3
4
5
6
7
8
9
10
11
12
13
$ => \n
RE \n\n => \n
RE ([^\n🤣😁😅]+)\n => 🤣\1😁\1😅\n
RE 😁[^🤣😁😅] => 😁😁
RE (🤣[^😁]*)(😁+)😅\n(🤣[^😁]*)\2(😁+)😅\n => \3\2\4😅\n\1\2😅\n
rm 🤣
rm 😁
rm 😅
$ => \n😡
RE ([^\n]*\n)([^😡]*)😡([^😡]*)😡 => \2😡\1\3😡
rm 😡
rm \n$
rm \n$

这个 $ 相关的替换好像有点 对于我来说 unexpected, 还是能跑就行的原则, 拿到 flag 了就行

也算是对 regex 的应用熟练一些了

flag3

哎呀好肝, 放一放先

Later: 写好了, 倒觉得没啥难度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
rm [^><\+-\.,\[\]]
rm \+-
rm -=
rm <>
rm ><
rm \[\]
$ => \n\n
RE \n\n => \n
\n$ => 🐸👁️😃⛔️
^ => 🐵
parse:
if 🐵> goto >
if 🐵< goto <
if 🐵\+ goto +
if 🐵- goto -
if 🐵\. goto .
if 🐵\[ goto [
if 🐵\] goto ]
if 🐵🐸 goto out
rm [^><\+-\.,\[\]🐵🐸]
if no 🏳️‍⚧️ goto parse
>:
🐵> => >🐵
👁️([^👁️]) => \1👁️
👁️⛔️ => 👁️😃⛔️
if no 🏳️‍⚧️ goto parse
<:
🐵< => <🐵
if no ([^👁️])👁️ goto error
([^👁️])👁️ => 👁️\1
if no 🏳️‍⚧️ goto parse
+:
🐵\+ => +🐵
...
👁️\* => 👁️+
👁️\) => 👁️*
👁️\( => 👁️)
👁️' => 👁️(
👁️& => 👁️'
👁️% => 👁️&
👁️\$ => 👁️%
👁️# => 👁️$
👁️" => 👁️#
👁️! => 👁️"
👁️ => 👁️!
👁️🤮 => 👁️
👁️🤢 => 👁️🤮
👁️😵 => 👁️🤢
...
👁️😁 => 👁️😆
👁️😄 => 👁️😁
👁️😃 => 👁️😄
if no 🏳️‍⚧️ goto parse
-:
🐵- => -🐵
if 👁️😃 goto error
👁️😄 => 👁️😃
👁️😁 => 👁️😄
👁️😆 => 👁️😁
...
👁️🤢 => 👁️😵
👁️🤮 => 👁️🤢
👁️ => 👁️🤮
👁️! => 👁️
👁️" => 👁️!
👁️# => 👁️"
👁️\$ => 👁️#
👁️% => 👁️$
👁️& => 👁️%
👁️' => 👁️&
👁️\( => 👁️'
👁️\) => 👁️(
👁️\* => 👁️)
👁️\+ => 👁️*
...
if no 🏳️‍⚧️ goto parse
.:
🐵. => .🐵
👁️(.)(.*⛔️.*) => 👁️\1\2\1
if no 🏳️‍⚧️ goto parse
[:
🐵\[ => [🐵
if 👁️[^😃] goto nojump]
jump]:
RE 🐵([^\]]*)\[([^\[\]]*)\] => 🐵\1🦈\2♿️
if no 🐵([^\]]*)\] goto error
🐵([^\]]*)\] => \1]🐵
RE 🦈 => [
RE ♿️ => ]
nojump]:
if no 🏳️‍⚧️ goto parse
]:
🐵\] => ]🐵
if 👁️😃 goto nojump]
jump[:
RE \[([^\[\]]*)\]([^\]]*)\]🐵 => 🦈\1♿️\2]🐵
if no \[([^\[]*)\]🐵 goto error
\[([^\[]*)\]🐵 => [🐵\1]
RE 🦈 => [
RE ♿️ => ]
nojump[:
if no 🏳️‍⚧️ goto parse
out:
rm [^🐸]*🐸
rm 👁️
rm [^⛔️]*⛔️
if no 🏳️‍⚧️ goto end
error:
RE [^❔]* => ❔
if no 🏳️‍⚧️ goto end
end:
🤓 => \n

小章鱼的曲奇

Smol

唉根本没学过随机数, 折腾半天找了个 cracker, 跑了一下发现不对 (625 正好多一个整数给我检查用, 果然就是这么做的)

做不出来查了一个钟发现是小端序惹的祸, 以紫砂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from randcrack import RandCrack

rc = RandCrack()

rs = "f892c06a..." # 总之是拿下来的东西

rb = rs[:5000] # 看源码有 2500 位空白

data = rs[5000:]

crack_int = [int.from_bytes(bytes.fromhex(rb[i * 8:i * 8 + 8]), byteorder='little') for i in range(625)]

for i in range(624):
rc.submit(crack_int[i])

# Check 一下

print(crack_int[-1], rc.predict_randrange(0, 4294967295))

# 接下来是解 data

mask = ""
for i in range(8):
mask += rc.predict_randrange(0, 4294967295).to_bytes(byteorder='little', length=4).hex()

mask = mask[:58]

# 从源码里搬出来这个

def xor_arrays(a, b, *args):
if args:
return xor_arrays(a, xor_arrays(b, *args))
return bytes([x ^ y for x, y in zip(a, b)])

data = xor_arrays(bytes.fromhex(data), bytes.fromhex(mask))

print(data)

Big

猜测是输入一个特殊整数能让 seed 相等, 没来得及测试

SUPA BIG

没判等???, 直接输入啥输出啥, 应该是非预期解吧

扫雷

观察到规律:

  • 生成的地图为网状, 固定位置可以保证无雷, 可以先开
  • 纵向固定 18 单元 (18 列), 右侧有三个一组的单元, 有固定格式, 但可能有多种具体情形

本题网格交叉处理论不可解, 推测出题人意图可知如下结构应当是横纵独立的

0 0 1 1 1 0 0
0 0 1 A 1 0 0
1 1 2 B 2 1 1
a b 2 a b 1 a
1 1 2 A 2 1 1
0 0 1 B 1 0 0
0 0 1 1 1 0 0

即 AB 中一处为雷, ab 中一处为雷

由于行数约为列数的 4 倍, 已知的一行的左端点处可以决定该列的雷的具体分布, 而已知的一列可以决定该列上所有以该列尾左端点的行的分布, 右侧三个一组的单元会有已知 1 得 2 和已知 2 得 1 的各种情况, 故猜测少量右侧单元即可按顺序解出所有行列


这人写的什么 wp 根本就是胡言乱语

你说得对, 我是来混分的, 混了个三等奖已经满足了, 继续启动别的博客去了