Castiel's Blog

蹭热度分享一份写配置漏洞案例

蹭热度分享一份写配置漏洞案例
2020-03-22 · 5 min read
代码审计 漏洞利用

0x00 前言

今天奇安信ATEAM团队分享一篇深度好文这是一篇“不一样”的真实渗透测试案例分析文章引爆朋友圈多次转发分享,Phith0n 师傅也专门写了一篇经典写配置漏洞与几种变形的总结,看了P神师傅文章想起我之前也挖到过一个类似漏洞,这里我把中间思路和部分代码贴出来分享一份。

0x01 分析

漏洞代码还是出在一个写配置文件的函数当中,代码如下(大部分代码删去,只留核心部分):

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_inputtrue的情况下,会从数据库中查询相同的内容,并定包含该文件,将$_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中后单引号未得到过滤,最终写入文件后便成了如下:
在这里插入图片描述

0x02 利用

这里只需要向目标二次提交代码即可逃逸了,例如target.php?name_list=testsssssss'];phpinfo();#/bbb&test=testskin_file 第一次提交代码被magic_gpc转义后写入文件,第二次提交时候因第一次生成了文件,所以这里程序逻辑条到后半部分,include第一次写入的文件,然后再将数组二次写,代码成功执行!

0x03 结语

此文由大佬们的深度好文有感而发,有不到之处多指教。漏洞原理和成因也比较简单,但当时还是读了许久代码才发现问题,主要是代码写得太乱。