今天奇安信ATEAM团队分享一篇深度好文这是一篇“不一样”的真实渗透测试案例分析文章引爆朋友圈多次转发分享,Phith0n 师傅也专门写了一篇经典写配置漏洞与几种变形的总结,看了P神师傅文章想起我之前也挖到过一个类似漏洞,这里我把中间思路和部分代码贴出来分享一份。
漏洞代码还是出在一个写配置文件的函数当中,代码如下(大部分代码删去,只留核心部分):
public function input($c)
{
///...........省略大部分代码..........
if ($_POST['name_list']) {
$name_list = explode("/", $_POST['name_list']);
foreach ($name_list as $v) {
if (substr($v, 0, 6) == "_scfg_") {
$name = substr($v, 6);
} else {
$name = $v;
}
if ($name) {
{ if (is_array($_POST[$v])) {
$input[$name] = implode("|@|", $_POST[$v]);
} else {
$input[$name] = $_POST[$v];
}}
}
}
}
$filename = $newsid.".php";
$res = write_config_file($this->lib_path."../data/".$dir."/", $filename, "_scfg_var", $input, false, false);
if ($all_input) {
$is_one_fail = false;
if ($ares = mysql_query("select * from ".$this->table_name." where cate1='$vars[cate1]' and cate2='$vars[cate2]' and cate3 ='$vars[cate3]' and skin ='$vars[skin]' ", $this->link_id)) {
while ($arow = mysql_fetch_assoc($ares)) {
if ($arow['id'] != $newid) {
$_scfg_var = array();
$filename = $arow['newsid'].".php";
@include($this->lib_path."../data/".$dir."/".$filename);
foreach ($_scfg_var as $k => $v) {
$_scfg_var[$k] = addslashes($v);
}
}
$res = write_config_file($this->lib_path."../data/".$dir."/", $filename, "_scfg_var", $_scfg_var, false, false);
///...........省略大部分代码..........
return $newsid;
}
这里通过POST获取name_list参数值,然后将值按“/” 分割存入数组input,然后将数组传输write_config_file
函数,继续跟进write_config_file
#! /lib/cfg/cfglib.php
function write_config_file($path, $file, $name, $input, $is_serial = true, $is_alert = true, $chmod = 0707)
{
global $_NL;
$conf_file = $path.$file;
$tmp_file = ereg_replace("(\.[^.]+)$","_tmp\\1",$conf_file);
$tmp_file2 = ereg_replace("(\.[^.]+)$","_tmp_\\1",$conf_file);
$name = "$".$name;
if( !$is_serial )
{
$output = "<?".$_NL;
foreach($input as $key => $val )
{
$key = str_replace("'", '\'', $key);
if( is_array( $val ) )
{
foreach($val as $key1 => $val1 )
{
$key1 = str_replace("'", '\'', $key1);
$output .= $name."['".$key."']['".$key1."'] = '".str_replace('\"', '"', $val1)."';".$_NL;;
//$output .= $name."['".$key."']['".$key1."'] = \"".str_replace("\'", "'", $val1)."\";".$_NL;
}
}
else $output .= $name."['".$key."'] = '".str_replace('\"', '"', $val)."';".$_NL;;
}
$output .= "?>";
}
// ..................省略写文件部分代码................
}
这里$key1 = str_replace("'", '\'', $key1);
程序员明明是想过滤掉写key值的,但是我估计转义符和单双引号已经把他搞糊涂了,这里这个转移根本就没起到作用。由此可见控制input
即可控制写入文件的内容,如下图:
但受到整体参数过滤影响,所有POST的单引号都会被转义为"\’",一般情况下若提交单引号将会得到如下结果:
但在继续跟进input
函数后半部分代码时候发现在$all_input
为true
的情况下,会从数据库中查询相同的内容,并定包含该文件,将$_scfg_var
值从新写入到文件,代码如下:
if ($all_input) {
$is_one_fail = false;
if ($ares = mysql_query("select * from ".$this->table_name." where cate1='$vars[cate1]' and cate2='$vars[cate2]' and cate3 ='$vars[cate3]' and skin ='$vars[skin]' ", $this->link_id)) {
while ($arow = mysql_fetch_assoc($ares)) {
if ($arow['id'] != $newid) {
$_scfg_var = array();
$filename = $arow['newsid'].".php";
@include($this->lib_path."../data/".$dir."/".$filename);
foreach ($_scfg_var as $k => $v) {
$_scfg_var[$k] = addslashes($v);
}
}
$res = write_config_file($this->lib_path."../data/".$dir."/", $filename, "_scfg_var", $_scfg_var, false, false);
这里就必要有意思了,这了会include
刚才写入的文件,然后将值取出再次写入。这里$_scfg_var[$k] = addslashes($v);
程序员值对值进行addslashes
操作,而键名为做任何处理,在include包含执行该文件后此处的$_scfg_var[$k] = addslashes($v);
中的$k
值就有了正常的单引号“’”了,后续再将$_scfg_var
传入write_config_file
中后单引号未得到过滤,最终写入文件后便成了如下:
这里只需要向目标二次提交代码即可逃逸了,例如target.php?name_list=testsssssss'];phpinfo();#/bbb&test=testskin_file
第一次提交代码被magic_gpc转义后写入文件,第二次提交时候因第一次生成了文件,所以这里程序逻辑条到后半部分,include
第一次写入的文件,然后再将数组二次写,代码成功执行!
此文由大佬们的深度好文有感而发,有不到之处多指教。漏洞原理和成因也比较简单,但当时还是读了许久代码才发现问题,主要是代码写得太乱。