Hackergame 2023 Writeup
作者: RiK (3050/223)
第一次参加 HG,明显感受到出题质量很高,做起来整体感觉很爽!(后面好难,一个都不会呃呃)
取部分解出题目写一篇 WP。新人实力有限,有误望指正。
1. 赛博井字棋
打开题目玩了几局后发现不可能以正常方式解题。而后尝试伪造 POST 请求,发现比较复杂(作为备选方案)。打开浏览器开发人员工具 (F12) 注意到以下代码:
static/script.js
async function setMove(x, y) {
if (board\[x\]\[y\] != 0) {
return;
}
if (frozen) {
return;
}
let url = window.location.href; // 获取当前 URL
let data = { x: x, y: y }; // 设置要发送的数据
return fetch(url, {
method: "POST", // 设置方法为 POST
headers: {
"Content-Type": "application/json", // 设置内容类型为 JSON
},
body: JSON.stringify(data), // 将数据转换为 JSON 格式
}).catch(errorHandler);
}于是通过 Console 重新定义(覆盖)该函数并将前两个 if 语句删除,Restart,强制占用对手棋子位置,获胜得到 flag:
flag{I_can_eat_your_pieces_53f95d1546}
2. 组委会模拟器
打开题目尝试手动撤回,不出意料消息发送过快,人工撤回不可能,于是编写 Python脚本自动撤回。(其实用 JS 直接监听更方便)
import requests, re, json, asyncio
async def send_delete(delay: float, id: int):
await asyncio.sleep(delay)
res = requests.post(
"http://202.38.93.111:10021/api/deleteMessage",
json={"id": id},
cookies={
> "session": "\[token\]"
},
headers={"Content-Type": "application/json"},
)
print(res.text)
res = requests.post(
"http://202.38.93.111:10021/api/getMessages",
cookies={
> "session": "\[token\]"
},
headers={"Content-Type": "application/json"},
)
data = json.loads(res.text)
illegal_req = \[\]
id = 0
for msg in data\["messages"\]:
if re.findall(r"hack\\\[a-z\]+\\", msg\["text"\]):
illegal_req.append(send_delete(msg\["delay"\], id))
id += 1
await asyncio.gather(\*illegal_req)
res = requests.post(
"http://202.38.93.111:10021/api/getflag",
cookies={
> "session": "\[token\]"
},
headers={"Content-Type": "application/json"},
)
print(res.text)由于单纯地 sleep() 后 requests.get() 的执行时间会因网络延时不断偏移预定值,可通过测量请求时间相减抵消,此处选用 asyncio 库并发解决。最后得到 flag:
flag{Web_pr0gra_mm1ng_36a712733a_15fun}
3. 虫
由于以前玩过业余无限电,一看到这题当场乐了,使用安卓手机软件 Robot36,外录音频后自动解码 SSTV 图像结果如下:

于是得到 flag:
flag{SSssTV_y0u_W4NNa_HaV3_4_trY}
4. Git? Git!
一开始使用 git log 找了个遍发现根本没有包含 flag 的 commit,后来索性对所有文件暴力搜索关键词 "flag" 没有结果,后来意识到是它可能是以二进制形式存储。本想放弃,网上搜索发现 git reflog 可以显示存储在本地的更详细的操作历史:
git reflog
ea49f0c (HEAD -\> main) HEAD@{0}: commit: Trim trailing spaces
15fd0a1 (origin/main, origin/HEAD) HEAD@{1}: reset: moving to HEAD~
505e1a3 HEAD@{2}: commit: Trim trailing spaces
15fd0a1 (origin/main, origin/HEAD) HEAD@{3}: clone: from https://github.com/dair-ai/ML-Course-Notes.git。执行命令
git reset 15fd0a1 --hard
后在 README.md 中第 19 行找到 flag:
flag{TheRe5\_@lwAy5_a_R3GreT_pi1l_1n_G1t}
5. LD_PRELOAD, love!
LD_PRELOAD 是一个环境变量,可以实现在运行实际程序前更改程序的动态链接库。但是它并不能更改静态链接库。于是编写程序如下:
capture_flag.c
\#include \<stdio.h\>
\#include \<stdlib.h\>
int main(void) {
char \*flag_str;
size_t n;
FILE \*flag_file;
flag_file = fopen("/flag", "r");
if (flag_file == NULL) {
perror("Cannot open flag file");
exit(EXIT_FAILURE);
}
open_memstream(&flag_str, &n);
getline(&flag_str, &n, flag_file);
puts(flag_str);
fclose(flag_file);
return 0;
}。执行命令
gcc capture_flag.c -o capture_flag -static
以静态链接编译程序,上传执行程序后输出 flag:
flag{nande_ld_preload_yattano_6e6cacbe72}
6. 流式星球
被下面几个细节坑了好久(最难绷的一集):
- 使用
numpy.fromfile()时一定要指定dtype=numpy.uint8啊啊啊啊。 - 使用
np.hstack()后同样要将数组转换为uint8型否则 opencv 报错。 - assert 语句的意思是条件不成立时异常,所以宽高不是 10 的倍数。
- 视频宽高不用强行枚举凑,用 Hex 编辑器打开源文件发现周期大致为 1281 字节,宽高取其因数即可。
最后得到 flag:
flag{it-could-be-eazy-to-restore-video-with-haruhikage-even-without-metadata-0F7968CC}
7. 奶奶的睡前 flag 故事
用 pngcheck 发现有两个 IEND 后想方设法拼接、拆分;读 PNG Specification 用 Hex 编辑器手写 IHDR、IDAT、IEND 块,配合 pngcheck 算 CRC ……整了两天最后还是没做出来,libpng 一直报错(zlib 相关),下半部分就是加载不出来。结果赛后得知有现成工具???(我的评价是多读题干)