HackThisSite - Extended Basic Mission 02
Challenge
You have this function, provide the value which must be POST-ed as filename to obtain the desired results: Get the source code of hackthissite.org/index.php
给出一个把 POST 参数
filename拼上.php后读取文件的函数,要求给出filename的值,读到站点根index.php的源码。
关卡页给出的函数:
1 |
|
状态:live 已通关(partially verified:当时提交的字符串未被留存)。处理函数和目录深度都在本地按同形目录树复现过,唯一推断出来的一步是处理脚本距站点根的层数。
Solution
file_get_contents
把一个路径当文件读成字符串,返回的是原始字节,不经过
PHP 解释器,所以读 .php 文件拿到的是源码。这正是题面要的
source code:读 index.php
得到它磁盘上的内容,而不是它渲染出来的页面。
路径完全由 $_POST['filename'] 决定,程序只做两件事:拼上
.php、然后读。没有任何目录白名单,也没有过滤
..。
函数固定追加 .php,乍看像扩展名黑名单。但
file_get_contents 不执行 PHP,读 .php
文件本来就是拿源码;而目标 index.php 的文件名里正好带
.php:追加的后缀恰好对得上,不构成障碍。真正要解决的是路径:提交处理脚本在
/missions/extbasic/ 下,目标是站点根的
index.php。
处理脚本位于站点根的下面两级,往上爬两级再指名
index:
1 | /missions/extbasic/ -> .. -> /missions/ -> .. -> / |
按同形目录树搭一个沙箱(站点根放 index.php,处理脚本放
missions/extbasic/template.php),把题面函数照抄为
template.php:
1 |
|
驱动脚本从处理脚本自己的目录(即它在正常部署下的 cwd)发起调用:
1 |
|
1 | $ cd replica/missions/extbasic && php probe.php |
第一行 index 失败,因为处理脚本所在目录里没有
index.php;../../index 把站点根
index.php
的源码原样吐了出来(没有被执行)。php://filter
变体返回同一份字节的 base64,说明固定的 .php
后缀并不妨碍读源码,关键只在路径。
filename 字段填
../../index。formkey
每次加载关卡页都会变,取页面和提交要在同一次运行里完成;提交必须带
Referer: <关卡页>,否则模板页丢弃该次尝试。会话
cookie 从环境变量 HTS_COOKIE 读入,不落盘。
Key points
$_POST['filename']未经校验进入file_get_contents,构成路径遍历;用户控制的路径要先规范化再校验是否落在允许目录内(realpath后做前缀比对)。file_get_contents读的是源码而不是执行结果,所以给.php目标补.php后缀不构成防护:这里的扩展名过滤是错觉。../的层数取决于处理脚本相对目标的深度:本关处理脚本在/missions/extbasic/,距站点根两级,故用../../。- 现代 PHP 里
%00空字节截断早已失效(PHP 5.3.4 起字符串不再被 NUL 截断),本关也不需要它:目标本身就以.php结尾。