2020 信息安全竞赛初赛 Writeup
一人 A 了五题 ... 三题 Web 两题 Misc,还有一题 Web 是早上 8 点多起来再看 A 了 ... 情况如下:

0x01 签到
如题
0x02 the_best_ctf_game
解压后,使用file命令查看flag文件,发现为data。由于文件体积小,所以直接 HEXdump 查看,发现flag{有一定的排布规律。按照以上规律一直找出剩下的字符直到结尾的}为止。

Flag:
1 | |
0x03 电脑被黑
解压后,使用file命令查看disk_dump,发现是 Linux 文件系统镜像,于是再对该文件直接解压,进入.Trash-0文件夹,发现位于misc01/flag.txt曾被删除,因此进入misc01,发现fakeflag.txt,内容不是文本内容。又发现demo文件,使用file命令查看,其为二进制可执行文件。

将demo反编译后发现是一段简单的加密程序,核心部分如下:

编写对应的解密程序:

将解密程序应用于fakeflag.txt,发现flag{flag_is_not_here}。但是上述加密程序只是原位改变字符,因此我们找到fakeflag.txt中flag{五个字符对应的二进制数据\x44\x2A\x03\xE5\x29,再对disk_image文件进行二进制查找,发现了两处。

将上述两处二进制数据再使用刚才的解密程序跑一遍,得到解密结果,其中第二处就是真实的flag:
1 | |
0x04 easytrick
由于对字符串长度进行限制,首先想到 md5 碰撞,并在原始字符中用%00截断。但是该环境的 PHP 版本已经没有strlen的截断。所以不能通过 md5 碰撞实现。
但是,可以利用 PHP 中浮点数的性质:当该浮点数越界时(值为 INF),INF 和其转换后的字符串不相等,这样代码中的$this->trick1 != $this->trick2的条件满足。因此可以在满足长度限制的情况下选足够越界的浮点数,如1E999。
而 PHP 调用md5函数时,会首先将参数转换为string类型,因此两个相同的浮点数转换成字符串之后显然值也相同,其 md5 的值也显然满足md5($trick1) === md5($trick2)。
构造如下的 Payload:
1 | |
即可获得 flag:
1 | |
0x06 littlegame
利用 JavaScript 的原型污染。
解压后发现是 NodeJS 后端,打开app.js,发现其引用了./route/index.js文件,该文件即该服务器的路由配置。浏览了大致逻辑后,发现有定义Admin对象,但其密码都存放在环境变量中,不太可能直接获取。又发现/Privilege接口下有setFn方法,该方法在set-value的npm库中。

进入node_modules文件夹,查阅该库的README.md文件查看使用方法,发现其可以直接递归设置对象的属性。因此想到可以利用该方法修改对象的__proto__原型属性。

由于req.session.knight已经在/SpawnPoint接口中定义,其和Admin都为Object类型,因此可以直接污染Object对象,将setFn的key设置为__proto__.password4,这样在Admin中也可以访问到。
操作步骤如下:首先打开浏览器,访问/SpawnPoint 页面,按 F12 获取Cookie,其中存有当前的 session 信息。然后使用curl命令,构造如下 Payload:
1 | |
将password4设置为100,
最后访问/DeveloperControlPanel接口:
1 | |
即获得 Flag:

1 | |
0x07 easyphp
首先根据题目的文字描述”fork 出的进程能异常退出?“及 PHP 代码可发现,只要构造适当的a和b参数,使得 fork 出的子进程经过call_user_function_array函数调用a时异常退出即可。
因此a需要可以使得子进程异常退出的,带有 3 个参数的函数,且不含pcntl和exec关键词。但我们可以将a设置为call_user_function,这样b就可以是带有pcntl等关键词了。这里需要b为带两个参数的函数。首先就想到了posix_kill,但是这样提交后,服务器直接 502:


因此要想别的方法,接着发现了pcntl-wait函数,尝试了一下,成功进入 PHPInfo 界面。具体产生错误的原理可能是该子进程已经不存在子进程,或者两个进程之间同时 wait 造成的死锁而产生异常退出:
Paylod:
1 | |
在 PHPInfo 界面得到 Flag:

1 | |