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_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
中後單引號未得到過濾,最終寫入文件後便成了如下:
0x02 利用
這裏只需要向目標二次提交代碼即可逃逸了,例如target.php?name_list=testsssssss'];phpinfo();#/bbb&test=testskin_file
第一次提交代碼被magic_gpc轉義後寫入文件,第二次提交時候因第一次生成了文件,所以這裏程序邏輯條到後半部分,include
第一次寫入的文件,然後再將數組二次寫,代碼成功執行!
0x03 結語
此文由大佬們的深度好文有感而發,有不到之處多指教。漏洞原理和成因也比較簡單,但當時還是讀了許久代碼才發現問題,主要是代碼寫得太亂。