Hackergame 2019 Writeup
比赛结束后忙了若干天才有空挤出这篇writeup……
先放一下自己的成绩吧(
签到题
送分题,打开开发者工具,把按钮的disabled属性去掉即可:
输入token后提交即可得到flag:
白与夜
目测是一道图片隐写题。Google搜出来若干篇文章之后知道了StegSolve这个工具。
用其打开这张图片,在Normal Image页面就可以看到若隐若现的flag:
切换至Colour Inversion(Xor)页面后,看到了清晰的flag:
信息安全2077
直接打开题目给出的链接会看到这样的提示:
在开发者工具的Source页面,页面中的脚本如下:
可以看到,在请求flag.txt时,在header处添加了一个If-Unmodified-Since
的字段,值为当前时间。如果请求没有成功(返回HTTP 200),则计算当前时间与最后修改时间Last-Modified
的差值,将其显示出来。
由此可以推测,flag.txt的修改时间在某个时间点,如果当前时间早于该时间点,那么If-Unmodified-Since
就不会为真,请求不会成功。此处返回的是HTTP 412 (Precondition Failed):
那么问题就容易解决了,为了让请求成功,把If-Unmodified-Since
的值改在flag.txt的修改时间之后就可以了。在得到lastModified
的后一行打个断点,这就是我们需要的修改时间,格林威治标准时间的2077年10月1日0时0分0秒:
(emmmm,果然是2077……)
然后在发送请求前打一个断点,将If-Unmodified-Since
的值改为在其之后的时间点:
就可以得到flag:
宇宙终极问题
42
给出\(x,y,z\),使得\(x^3+y^3+z^3=42\)。
之前正好看到这个问题已经被解决了: \[ x = -80538738812075974 , y = 80435758145817515 , z = 12602123297335631 \]
网页读取器
先看源代码:
1 | whitelist_hostname = ["example.com", |
其过滤网址的方法可以分为以下几步:
- 检查输入的URL是否以
http://
开头; - 从输入的URL中取出
http://
之后的字符,以去除协议名; - 在上一步的结果中寻找
@
符号,若有,则取出其后面的字符,以去除用户名信息; - 在上一步的结果中寻找
/
符号,若有,则取出其前面的字符,以去除域名以外的信息; - 在上一步的结果中寻找
:
符号,若有,则取出其前面的字符,以去除端口号; - 判断上面的结果是否在白名单(
example.com
,www.example.com
)中。
参考Server-side request forgery (SSRF) 里面的"SSRF with whitelist-based input filters"一节,构造了这样的一个URL:http://web1/flag#@example.com
#
这个符号在URL规范里是一个Fragment Identifier,该符号前面的部分是一个URL查询,后面的部分将被视为额外的Fragment,不会发送给服务器,而是由客户端进行处理。亦即,访问http://web1/flag#@example.com
与访问http://web1/flag
,对服务器而言是一样的;- 我们来看看这个URL是否能通过上述过滤步骤:
- 它以
http://
开头,没有问题; - 取出
@
符号后面的字符,得到example.com
; - 没有
/
和:
符号,跳过这两步; example.com
在白名单中,检查通过。
- 它以
检查出的结果是example.com
,实际访问的却是http://web1/flag
,我们从而绕过了URL检查,得到了flag:
达拉崩吧大冒险
这题显然需要我们将Attack值溢出。抄起开发者工具,在买童子鸡的时候把数量改掉:
但是……改成多少呢?
一开始试的是C语言里面的INT_MAX,并没有溢出……后来用了比较蠢的方法,用了一个比较大的数字(大概是十几个9),提交了几次,终于让Attack值溢出了(流下了不学无术的泪水)
再后来用了Java的Long型的最大值(Long.MAX_VALUE=9223372036854775807, \(2^{63} - 1\)),一发入魂(再次流下了不学无术的泪水)
溢出后是这么个效果:
拿到flag:
小巧玲珑的 ELF
拿起神器IDA,打开下载下来的文件,按下F5看一下反编译后的代码。
首先映入眼帘的是这么一串东西:
然后是一些代码逻辑:
由此可以猜测:
- 一开始看到的那串东西是一个字符数组;
- 使用系统调用得到的输入存放在buf数组,并对其每一个字符进行处理。对于
buf
数组的第i
个元素buf[i]
,有如下操作:- 将
buf[i]
加上i*2
- 将
buf[i]
与i
异或 - 将
buf[i]
减去i
- 将
- 用处理后的buf数组与上述字符数组进行比对,如果一致,则使用系统调用输出flag。
很容易写一个demo进行逆操作,来得到期望的输入:
1 |
|
最后的输出:
三教奇妙夜
Part 1
一个很自然的想法是,黑底白字的部分应该是视频的关键帧。那么把关键帧提取出来,在其中进行筛选就可以了。
使用ffmpeg来导出关键帧,存放在预先建立的pics目录:
.\ffmpeg -i output.mp4 -vf "select=eq(pict_type\,I)" -vsync vfr pics\%04d.jpg
输出了4319张关键帧。工作量已经小很多了,一张一张筛选就是了
发现关键帧的md5值似乎是一样的,用Python写个小脚本筛选一下md5不一样的文件:
1 | #!/bin/python |
运行:
感觉……flag少了最后一块(扑通)
Part 2
我开始怀疑,flag片段并不是都在关键帧里的。那就得想个办法把非关键帧里的片段找出来。
刚才找到的flag片段中,最后一个片段大约在视频的9:00:00之后。经过一番参考,得知可以用下面的命令,输出9:00:00之后,画面变化幅度超过0.1的帧:
.\ffmpeg -ss 9:00:00.0000 -i output.mp4 -vf "select=gt(scene\,0.1), scale=640:360" -vsync vfr pics\%04d.png
得到了下列图片:
拼凑所有片段,就可以得到完整的flag:flag{ViDe0_prOcE55_with_program_1s_eaSy}
Reference:
Extract images frame by frame from a video file using FFMPEG
THUMBNAILS (IFRAME / SCENE CHANGE) - 2018
后记
- Shell骇客本来应该不难,但不是很熟悉msfvenom工具,生成的shellcode并没有起作用
- 我想有个家那道题,机智地
sudo chmod o-x /
通过了删除检测,没想到后面还有要求,却没法把权限改回去了,宣告gg,只好重置系统(x