HGAME 2022 第一周部分题的 Writeup。
小插曲,作者在打比赛的时候忘记截止时间少交了一个 flag,血亏 150 分 QAQ。
CRYPTO
Dancing Line
拿到图片,发现是一条折线,一开始没啥思路。
但仔细思考发现线横向只会向右,纵向只会向下,想到了什么?二进制!其实是受到校内比赛的启发
使用 PS 打开,放大后发现相邻两个黑方格之间正好相差 7 个蓝方格,想到了什么?ASCII 码!
再结合 HGAME 的 flag 格式:hgame{xxx}
,我们先拿到 h
的 ASCII 码,并将其转为二进制 1101000
,再去对应找规律。
猜测是在折角处改变 0/1 状态,进行验证,发现前两段正好是 hg
,写了个 Python 脚本:
1 | from PIL import Image |
flag:hgame{Danc1ng_L1ne_15_fun,_15n't_1t?}
Easy RSA
送分题,没啥好说的,RSA 的常规解密过程。
Python 脚本:
1 | import gmpy2 |
flag:hgame{L00ks_l1ke_y0u've_mastered_RS4!}
English Novel
下载下来的文件夹里给了加密方法 encrypt.py
,算法非常简单,就是用 key
对 data
进行了一个移位,而移位是模 26 意义下的,这就意味着如果知道原文和密文,我们可以暴力枚举密钥,每一位的复杂度是 。
接下来的问题是找原文对应的密文是什么了,而这些文件分别在 original
和 encrypt
文件夹下,但是被打乱了顺序。注意到加密对于非字符是跳过的,且不改变大小写,所以我们可以根据这两个条件来写脚本判断是否对应。
然后值得一提的是加密过程和密钥长度无关,所以只需要枚举 flag.enc
文件长度的密钥就行,时间复杂度 。脚本如下:
1 | encrypted = "klsyf{W0_j0v_ca0z_'Ks0ao-bln1qstxp_juqfqy'?}" |
flag:hgame{D0_y0u_kn0w_'Kn0wn-pla1ntext_attack'?}
Iot
饭卡的uno
硬件啥也不会,但是把 .hex 文件扔进 IDA 能看见这个:
1 | seg000:000005BE db 68h ; h |
flag:hgame{F1rst_5tep_0F_IOT}
正解就等官方 wp 吧。。
MISC
欢迎欢迎!热烈欢迎!
送分题,照着题目做就行,flag 都没记录下来(
这个压缩包有点麻烦
下载下来的文件注释告诉我们 Pure numeric passwords within 6 digits are not safe!。
意思就是让我们爆破密码,用 fcrackzip 暴力破解出密码是 483279
。
解压后里面给了我们一个字典文件 password-note.txt
,有个 README.txt
提示 I don’t know if it’s a good idea to write down all the passwords.
然后发现 fcrackzip 的字典模式不能用,好像是把自己目录下的 README 当成字典了,也不会修,尝试编译了半天没成功,希望有大佬能教教我。
用 ARCHPR 跑字典后得到密码是 &-`;qpCKliw2yTR\
。
解压后又有一个压缩包,提示 If you don’t like to spend time compressing files, just stores them.。
一开始以为这段话意思是没压缩藏在文件里了,尝试 binwalk,发现啥也没有。
仔细观察发现这段话刚好 68 字节,而压缩包里的 README.txt
原始大小也是 68 字节!更关键的是,CRC32 也相同!
于是考虑明文攻击。一开始试了好久一直不行,然后 SU 的师傅推荐了 rbkcrack,论工具的重要性(
1 | ./rbkcrack.exe -C flag.zip -P README.zip -a |
拿到 keys: 060fd5e1 d1f696b7 12655d8d
再用 keys 去解压压缩包:
1 | ./rbkcrack.exe -C flag.zip -c flag.jpg -k 060fd5e1 d1f696b7 12655d8d -d flag.jpg |
拿到 flag.jpg,但是还没结束,图片内容不是 flag,使用 binwalk 发现里面藏着一个压缩包,把压缩包提取出来。
发现加密了,但是又没提示了。。思考了半天,想到压缩包的考点也就这么几个,文件内容决定了显然不是 CRC 爆破,那么只能是伪加密了。
把压缩包的两个压缩源文件数据区和压缩源文件目录区的加密标识 09 00
改成 00 00
即可,拿到最终的 flag.jpg。
flag:hgame{W0w!_y0U_Kn0w_z1p_3ncrYpt!}
好康的流量
给了一个流量包,用 Wireshark 打开看看,发现是一次邮件过程,里面放了一个图片附件。
流量包看上去没啥东西,去把图片还原出来吧。
用 binwalk 扫到了一个 .zlib
文件出来,被误导了半天。后来想到 binwalk 只检测文件头,文件里可能有数据类似 zlib 文件头被错误地检测出来了。把前面的数据提取出来,果然发现图片失效了,排除附加文件的可能。
看来只能从文件本身入手了,用 PS 放大,发现图片左上角有一个类似条形码的东西,但是我啥也不会,就拿 PS 先加滤镜,使用锐化,参数怎么清楚怎么来,然后手绘,大概半夜画到凌晨 3:40,得到这么一张图:
还是老办法,先生成了 hgame{
的二维码,发现前面对上了!但是扫出来是 hgame{ez_1mg_
,好家伙只有一半,又没啥思路了。
去搜索了图片隐写的方式,尝试搜索原图来看是否存在数字水印,但是只找到了完整的图片,而这张只是那张图的稿子。但我知道了这张图是游戏王,牌佬出题人夹带私货属于是
没啥思路就拿眼睛瞪呗,用 PS 放大用取色器一个像素一个像素仔细观察,感觉最左边竖列颜色有问题,猜测也是有隐写,得知了 Stegsolve 这个工具,用它选择 RGB 的最低位,LSB,按竖列解析,获得这么一个文件:
1 | 53746567346e3067 72617068797d5374 Steg4n0g raphy}St |
后面都是无意义乱码了,再与之前的到的前半部分拼接得到 flag:hgame{ez_1mg_Steg4n0graphy}
P.S. 也是得知这个工具后才知道左上角的条形码可以通过 Stegsolve 的通道来看,直接秒出,选择 Green Plane 2
即可。可恶,白画了 3 个多小时 PS,TAT!论工具的重要性。
群青(其实是幽灵东京)
下载下来的音频先试听一会,发现低音区好像被截了,绝对有隐写(废话)。
拿 Audacity 看看频谱,发现字符串 Yoasobi,但是不是 flag。
然后再用 Silenteye 对文件进行解密,使用 AES256,密钥就用频谱得到的字符串 Yoasobi。
解出来一个 URL:https://potat0-1308188104.cos.ap-shanghai.myqcloud.com/Week1/S_S_T_V.wav
下载下来是一个非常刺耳的音频,拿文件名去网上搜索得知还有 SSTV 这东西,介绍就自己去看百科吧。
P.S. 国际空间站也传 SSTV 格式的视频,大佬可以自制天线接收解析(
但这格式也太小众了,网上还没啥软件能解析的,一搜全是怎么编码的。我终于在中国业余无线电论坛上找到有人推荐了 Black Cat SSTV。
但是直接外放麦克风的环境噪声比较大,只能看见里面有个二维码,并不能识别出来,好巧不巧的是,“Decode Audio File…” 选项会导致程序崩溃(已邮件反馈 bug)。
只能使用虚拟设备了,安装 VBCABLE,然后 Sound Input 选择 CABL Output,播放音频即可。
就是不知道为什么是歪的,不过不影响结果
扫码出来 flag:hgame{1_c4n_5ee_the_wav}
P.S. 图片右边好像是异格斯卡蒂,看来 MISC 出题人成分复杂啊(
PWN
test_your_nc
PWN 啥也不会,就只会个 1 分的送分题。
连接以后直接 ls
发现有个 flag 文件,直接 cat
就行。
环境太卡了,flag 也忘记保存了。
REVERSE
easyasm
这个文件不一般啊,是 DOS 的可执行文件,直接以文本格式打开发现有个 hgame{Fill_in_your_flag}
,但这个显然不是 flag。
把文件扔进 IDA 里去,直接反汇编发现不兼容 16 位的代码。只能自己看了,发现两个不认识的寄存器 AL 和 AH,搜了一下资料。
AL,AH 合并起来是 AX,其中,AX 是一个 16 位寄存器,AH 就是 AX 的高字节(高 8 位),AL 是 AX 的低字节(低 8 位)。
大致看了一下,代码是把读入的数据放到 AL,把 AL 左移 4 位存到一个地方,再把 AL 右移 4 位存到一个地方,再把它们相加的结果异或 0x17,再与一个数组的数据进行比较。
前面说到 AL 是一个八位的寄存器,所以前面的操作相当于是把 AL 的高 4 位和低 4 位对调之后再异或 0x17。
直接通过逆向得到的数组进行反推,代码如下:
1 |
|
拿到 flag:hgame{welc0me_to_4sm_w0rld}
WEB
easy_auth
题目要我们拿到管理员的 to_do,先发现 admin
无法注册,得知管理员账号,然后注册了一个用户名是 2333
,密码是 2333
的用户,登录后随便试了一会,了解一下 API。
一开始以为是什么 SQL 注入,但是毫无头绪,翻看 JS 的时候发现每次操作会传一个 token,而且格式非常特殊,就上午搜了一下,得知 JWT 这玩意。老规矩,不知道的请移步百科。还知道了一个好用的工具网站: https://jwt.io/
发现代码中会把这个 token 储存在 localStorage 里,就直接 localStorage.getItem('token');
拿到 token(为了清晰一点,我按照 JWT 的格式分为 3 段):
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. |
用工具网站解析得到:
1 | { |
网上搜索到方法有加密算法替换为 None 和密钥爆破,我直接对目标 API http://whatadminisdoingwhat.mjclouds.com/v1/todo/list
进行操作。
使用 ewogICJhbGciOiAiTm9uZSIKfQ.eyJJRCI6MCwiVXNlck5hbWUiOiJhZG1pbiIsIlBob25lIjoiIiwiRW1haWwiOiIiLCJleHAiOjk2NDI5MTM3OTIsImlzcyI6Ik1KY2xvdWRzIn0.
发现服务器不支持 None 算法:
1 | { |
而 Signature 那么长也爆破不了 secret。注意到有个 ID 字段,怀疑是根据 ID 查询密码作为密钥的,尝试注入。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6W10sIlVzZXJOYW1lIjoiYWRtaW4iLCJQaG9uZSI6IiIsIkVtYWlsIjoiIiwiZXhwIjo5NjQyOTEzNzkyLCJpc3MiOiJNSmNsb3VkcyJ9.
然后返回了这个:
1 | { |
好了,现在我们得知后端是 Go 了,但是事实上并没有什么用。
P.S. 作者当时还去搜索了 Go 的 JWTClaims,发现确实有 CVE,但是这题里面毫无关系,反而思路被带偏了。
机缘巧合的是在我把 secret 置空以后发现生成的和最开始的那个居然一模一样,真是吐血,居然 secret 是空。这我一开始是真没想到。
然后我把 ID 改为 0,UserName 改为 admin,
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MCwiVXNlck5hbWUiOiJhZG1pbiIsIlBob25lIjoiIiwiRW1haWwiOiIiLCJleHAiOjE2NDI5OTMwNTcsImlzcyI6Ik1KY2xvdWRzIn0.DFdaY3gfvJ-9g7bz9wDSWCIGqlROXKo-_O4l4gTvToQ
服务器返回:
1 | { |
当时吓我一大跳,还以为又白做了。但我把我自己 token 的 ID 改了发现也是这个结果,才知道这个返回的意思是 ID 和 Username 不匹配。
于是把 ID 改成 1,
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiVXNlck5hbWUiOiJhZG1pbiIsIlBob25lIjoiIiwiRW1haWwiOiIiLCJleHAiOjE2NDI5OTMwNTcsImlzcyI6Ik1KY2xvdWRzIn0.HKmz9ithj9bnb6c2pKDGiHUSPxSR6b9x5gho5vTmyKc
服务器返回:
1 | { |
拿到 flag:hgame{S0_y0u_K1n0w_hOw_~JwT_Works~1l1lL}
蛛蛛…嘿嘿♥我的蛛蛛
进去就让你点按钮,点了几次试了一下,后来受不了直接写了一个功能弱一点的脚本(按照题目来说就是让你写爬虫的):
1 | list = document.querySelectorAll('a'); |
在控制台不停执行就行,最后到了 https://hgame-spider.vidar.club/636defa854?key=MmhhqguSUpelDSTyNsBjinkUo458MpiGW72R6HiydqseM8lZ3zEn%2BQT0a7vs09NjqB9KtkAYWtKYf4HVifIN1A%3D%3D
。
网页中间一个大大的标题 我好像在就是把flag落在这里了欸~ 快帮我找找x,联系到 URL 的 key,一开始怀疑这个 key 是加密的。
先 URL 解码得到 MmhhqguSUpelDSTyNsBjinkUo458MpiGW72R6HiydqseM8lZ3zEn+QT0a7vs09NjqB9KtkAYWtKYf4HVifIN1A==
,一开始看着结尾的 ==
以为是 Base64,然而失败了。
后来意识到可能不在这,在响应头里发现了 fi4g: hgame{202418360e93093582ff7358f3b3829d3f733935bef5686eeb568e9848b779c1}
拿到 flag:hgame{202418360e93093582ff7358f3b3829d3f733935bef5686eeb568e9848b779c1}
Tetris plus
打开网页,是个小游戏,题目描述说“据说没人能超过 3000 分”,着重寻找代码里和这个有关的。
首先我们要知道,作者一般不会自己去写这个小游戏的,所以小游戏的代码可以先不看。
然后我们找到了 js/checking.js
,有这么一段代码:
1 | if (score >= 3000 && !window.winned) { |
执行一下那行 alert 再说,得到的是“flag 貌似被藏起来了,再找找吧!”。
然后发现下面那一长串可疑的注释,做 Hackergame 的经验告诉我,这是可以解码的,得到 alert("hgame{jsfuck_1s_S0_fUu1n}")
,然后 flag 就出来了。。
flag:hgame{jsfuck_1s_S0_fUu1n}
P.S. 菜鸡作者这时候才知道 jsfuck 这玩意,以前只知道 BrainFuck。
Fujiwara Tofu Shop
每一层题目都给了提示,说实在的这和校内的比赛第一题简直一模一样。。
想成为车神,你需要先去一趟秋名山(qiumingshan.net)
这个 URL 是无法解析的,我们只需在请求头里加上 Referer: qiumingshan.net
即可。
只有借助AE86才能拿到车神通行证(Hachi-Roku)
借助一个东西?立马想到 User-Agent,在请求头里加上 User-Agent: Hachi-Roku
即可。
86的副驾上应该放一盒树莓(Raspberry)味的曲奇
曲奇?Cookie!但是发现直接 Cookie: Raspberry
不行,查看响应头发现 Set-Cookie: flavor=Strawberry
,于是得到正确的格式 Cookie: flavor=Raspberry
。
汽油都不加,还想去秋名山?请加满至100
同样是在响应头发现 Gasoline
字段,在请求头里加上 Gasoline: 100
即可。
哪怕成了车神,也得让请求从本地发出来才能拿到 flag !
最有趣的地方来了,首先要知道 localhost
是 127.0.0.1
的别名。
然后常规思路是用 X-Forwarded-For,但是当我请求带上 X-Forwarded-For: 127.0.0.1
,服务器返回“大黑阔也想当车神?”。啊,这熟悉的口音,令人难绷。然后一时没有办法了
这时候想起信息搜集,仔细观察响应头,发现 Server: gin-gonic/gin v1.7.7
,仿佛找到了新大陆。
然后发现了这个 https://security.snyk.io/vuln/SNYK-GOLANG-GITHUBCOMGINGONICGIN-1041736
欸,这可不就是和 X-Forwarded-For 有关吗?
再去 Github 上找是咋修的,然后发现了 X-Real-Ip 这个字段。请求头里加上 X-Real-Ip: 127.0.0.1
就拿到 flag 了。
拿到 flag:hgame{I_b0ught_4_S3xy_sw1mSu1t}
坐等晚上官方 wp。
To be continued…