哥斯拉简介
哥斯拉,他是继菜刀、蚁剑、冰蝎之后具有更多优点的Webshell管理工具,由java语言开发,如名称一样,他的“凶猛”之处主要体现在:
全部类型的shell能绕过市面大部分的静态查杀
流量加密能绕过过市面绝大部分的流量Waf
Godzilla自带的插件是冰蝎、蚁剑不能比拟的
它能实现的功能除了传统的命令执行、文件管理、数据库管理之外,根据shell类型的不同还包括了:MSF联动、绕过OpenBasedir
加密流量提取思路
特征识别
互联网通讯协议众多,不同的应用通常会采用不同的协议,而各种协议都有其特殊的指纹,这些指纹可能是特定的端口、特定的字符串或者特定的比特序列。基于特征的识别技术,正是通过识别数据报文中的指纹信息来确定业务所承载的应用以及该应用是否存在异常。
如通过特征指纹识别常见的的黑客工具,如菜刀、蚁剑、冰蝎、Godzilla等webshell工具的通讯;Regeorg、Tunna、Frp、EarthWorm等网络穿透工具的通讯;CobaltStrike、MSF等渗透工具的通讯。
在加密流量检测上可以通过对加密握手过程中产生的JA3指纹,SNI信息,证书颁发者等信息,做特征匹配,精准识别到APT组织中使用的特种木马或基于框架生成的商业木马,发现内网失陷资产。
隐蔽信道
隐蔽信道可以分为使用未知协议与利用已知协议两种。
使用未知协议的隐蔽信道检测通过对网络层、应用层协议的匹配分析,对常见端口下的数据传输进行协议位特征的匹配,选择多种特征位进行数据匹配,当所有匹配特征都无法匹配该端口下的通讯数据,则认为通讯不是该端口该运行的协议,即为网络隐蔽信道通信。
对于利用已知协议正常业务字段的进行隐蔽通信部分,可以首先按照协议对流量进行分类解析,然后针对与每种协议进行字段划分,特征提取,统计建模,比如DNS隧道,APT组织经常使用DNS隧道来对木马进行指令下发数据传输等控制,僵尸网络使用DGA上线等。
哥斯拉软件安装及使用
安装
项目地址:https://github.com/BeichenDream/Godzilla/releases
下载.jar
文件
运行:
1 | java -jar godzilla.jar |
靶场攻击
php搭建的网站和jsp搭建网站:
apache 服务器一般使用PHP搭建。tomcat 服务器一般使用jsp搭建。木马是要在服务器上自动执行,执行脚本,所以木马格式要与服务器的语言保持一致。比如php的网站,要用php的木马。
访问网站index.php,可以看出是php网站,生成php的马
使用upload的靶场
1 | docker search upload-labs |
使用哥斯拉生成一个木马
修改木马后缀
改为.png,上传,同时用burp拦截,改包,改成.php再发送
检查一下,确实已经上传
哥斯拉连接
目标地址:http://ip:8080/upload/test.php
连接成功
反编译获得源码
JD-GUI 反编译下载工具
https://jd-gui.apponic.com/mac/
M1 系统报错:
解决方案1:
右键JD-GUI,显示包内容
进入
Contents/MacOS
文件夹,打开universalJavaApplicationStub.sh
文件,使用新版的文件替换,新版文件内容可以在如下地址复制解决方案2:
右键JD-GUI.app 显示包内容,打开info.plist。
修改第149行,把1.8+改成1.8即可。
1 <key>JVMVersion</key> <string>1.8</string>
双击打开软件,将需要反编译的jar包拖到软件界面中
选中全部文件,点击File-Save All Sources
保存反编译完的源码,选择保存路径
整体分析流程概述
哥斯拉全套的攻击流程
- 在客户端生成shell,生成shell的时候可以设置参数,包括:密码、密钥、有效载荷、加密器
- 有效载荷分为java、php、c#三种类型的payload
- 加密器分为base64和raw两大类。其中java的加密方式是通过AES加密,在此基础上再进行base64编码(base64),或直接AES加密完的数据(raw)。C#的加密方式是通过AES加密,在此基础上再进行base64编码(base64),或直接AES加密完的原数据(raw)。PHP的加密方式是原始数据和密钥的md5值的前16位按位异或,然后再进行base64的编码(base64)。或直接进行异或(raw)
- 生成
shell.php
(文件名可自定义,根据选择的有效载荷不同,可以有jsp、php、aspx等文件格式),该shell.php
需要上传到攻击的目标主机上。- 这个文件,会出现在数据包的POST请求中。
- 该文件会将密码、密钥的md5值前16位明文写入。
- 该文件的实现功能还有:将密码和密钥进行拼接,然后进行md5的计算。一共32位。服务器端返回数据的时候,会进行拼接。即服务器端返回数据 = md5前16位+加密数据+md5后16位。(加密数据可以通过对服务器端的解密算法进行解密)
- 哥斯拉客户端进行连接
- URL是目标主机上的
shell.php
- 请求配置的参数,可以添加冗余数据,该冗余数据会在数据的正式内容的前后添加进去。
- URL是目标主机上的
- 连接成功后,进入shell便可以进行操作。
数据包分析
- 在哥斯拉客户端进行连接的时候,会有一步测试连接的过程。一共产生三个数据包:
- 第一个数据包会将
shells/payloads/php/assets/payload.php
的内容发送过去。该文件定义了shell所有功能所需的一系列函数,哥斯拉第一次连接shell时,将这些函数定义发送给服务器并存储在session中,后续的shell操作只需要发送函数名称以及对应的函数参数即可。 - 第二、三数据包内容一样,解密后的内容为
methodName=test
。该数据包的含义是将payload.php
中的函数名传入。它会实现payload.php
中test
函数的功能
- 第一个数据包会将
- 进入shell也会产生和测试连接时相同的数据包。主要都是第一个数据包是发送
payload.php
的内容。后面的数据包是传入函数名和参数。以methodName=xxxx
的形式。(命令执行会是cmdline=xxx&methodName=xxx
)
加密过程
客户端发送的request:
先对原始数据进行base64的编码
然后和密钥key按位异或(这个密钥key是取shellsetting时自定义设置的那个密钥的md5值前16位。)
将得到的数据再base64一次,再url编码一次。
最后将得到的数据与密码进行拼接。
相对应的加密函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32public byte[] encode(byte[] data) {
/* */
try {
/* 53 */
return E(data);
/* 54 */
} catch (Exception e) {
/* 55 */
Log.error(e);
/* 56 */
return null;
/* */
}
/* */
}
public byte[] E(byte[] data) {
/* 83 */
byte[] cs = functions.base64Encode(data).getBytes();
/* 84 */
int len = cs.length;
/* 85 */
for (int i = 0; i < len; i++) {
/* 86 */
cs[i] = (byte) (cs[i] ^ this.key[i + 1 & 0xF]);
/* */
}
/* 88 */
return (String.valueOf(this.pass) + "=" + URLEncoder.encode(functions.base64Encode((new String(cs)).getBytes()))).getBytes();
/* */
}
服务器端的respond:
解密过程
先调用
findStr
函数删除服务器响应数据左右附加的混淆字符串(生成shell时设置的参数,自定义添加的冗余数据)- 对于
PHP_XOR_BASE64
加密方式来说,前后默认各附加了16位的混淆字符:将密码和密钥进行拼接,然后进行md5的计算。一共32位。服务器端返回数据的时候,会进行拼接。即服务器端返回数据 = md5前16位+加密数据+md5后16位。(加密数据可以通过对服务器端的解密算法进行解密)
- 对于
将得到的数据进行
url
解码然后base64
解码最后再和shell连接密钥md5值的前16位按位异或,将结果最终
base64解码
即完成响应数据的解密。相对应的解密函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41# 先删除data的混淆数据
public String findStr(byte[] respResult) {
String htmlString = new String(respResult);
return functions.subMiddleStr(htmlString, this.findStrLeft, this.findStrRight);
}
# 按位异或(解密base64)
public byte[] D(String data) {
byte[] cs = functions.base64Decode(data);
int len = cs.length;
// 原始数据与密钥md5的前16位按位异或
for (int i = 0; i < len; i++) {
cs[i] = (byte) (cs[i] ^ this.key[i + 1 & 0xF]);
}
return functions.base64Decode(new String(cs));
}
# 解密
public byte[] decode(byte[] data) {
if (data != null && data.length > 0) {
try {
return D(findStr(data)); //先删除data的混淆字符串,再调用D函数进行解密
} catch (Exception e) {
Log.error(e);
return null;
}
}
return data;
}
解密代码
自己简单的写了一个,可以实现功能,但比较粗糙
1 |
|
哥斯拉加密分析
加密shell设置
密码:Post请求中的参数名称(默认密码为pass)
密钥:用于对请求数据进行加密,不过加密过程中并非直接使用
密钥
明文,而是计算密钥
的md5值,然后取其前16位用于加密过程有效载荷
加密器
扰乱数据:用于自定义HTTP请求头,以及在最终的请求数据前后额外再追加一些扰乱数据,进一步降低流量的特征。
哥斯拉shell种类
哥斯拉提供了三种shell,分别是java、C#、PHP
源代码中,也对应了这三种类型:cshapAes, JavaAes, phpXor
分析客户端的加密代码,找到cryptions
,因为用的是php的shell,所以分析php shell的代码
加密方式,采用的是base64的异或加密。shells/cryptions/phpxor/template/base64.bin
这是一个php文件,提取出来进行分析
整个shell的基本执行流程是:服务器接收到哥斯拉发送的第一个请求后,由于此时尚未建立session,所以将POST请求数据解密后(得到的内容为shell操作中所需要用到的相关php
函数定义代码)存入session中,后续哥斯拉只会提交相关操作对应的函数名称(如获取目录中的文件列表对应的函数为getFile
)和相关参数,这样哥斯拉的相关操作就不需要发送大量的请求数据。
1 | echo substr(md5($P.$T),0,16); |
它将密码和密钥进行拼接,然后进行md5的计算。一共32位。服务器端返回数据的时候,会将这个数据的前16位和后16位中间拼接上base64等加密过的返回数据。
抓包分析-查看“测试连接”操作所发送的数据包
设置代理,用burpsuite拦截,查看产生了什么数据包
代理端口设置为8888
测试链接后,产生三个数据包
第1个请求会发送大量数据,该请求不含有任何Cookie
信息,服务器响应报文不含任何数据,但是会设置PHPSESSID
,后续请求都会自动带上该Cookie。
第2、3个请求完全一致,并且数据量很少,会带上PHPSESSID
第一个数据包分析
点击测试按钮,会进行操作,根据这一点去查看源代码。
根据ui的布局,找到测试按钮相关UI,再去追溯到功能代码部分
如果updateTempShellEntity()
为 true
则执行向下执行,检查initShellOpertion()
,检查通过后,执行getPayloadModel().test()
则连接成功。
函数
updateTempShellEntity()
的作用是:检查shellsetting的那些信息是否填写完全。url, password,secretKey ,payload ,cryption, encoding
函数
initShellOpertion()
找到函数
initShellOpertion()
的位置,是ShellEntity
中的跳到import中,查找
Shellentity
去
ShellEntity.java
找到函数initShellOpertion()
tip:函数的执行过程,以注释的方式在代码后面标明
查看
cryptionModel
初始化的函数init
在上述init()
函数中,首先初始化了上下文对象、Http对象、密钥等信息,然后加载src\shells\payloads\php\assets\payload.php
文件内容作为payload数据(到目前为止,还未进行任何加密操作)。payload.php
文件内容如下所示,其中定义了shell所有功能所需的一系列函数,哥斯拉第一次连接shell时,将这些函数定义发送给服务器并存储在session中,后续的shell操作只需要发送函数名称以及对应的函数参数即可。
最终第一个数据包的发送,是通过shells/cryptions/phpXor/PhpXor.java
的init
函数,这段代码实现的👇
最终POST请求数据= 左边追加数据+编码后的原始数据+右边追加数据
其中encode
函数👇 加密的过程
1 | public byte[] encode(byte[] data) { |
数据的加密过程是:先对原始数据进行base64的编码,然后和密钥key按位异或,这个密钥key是取shellsetting时自定义设置的那个密钥的md5值前16位。 将得到的数据再base64一次,再url编码一次。最后将得到的数据与密码进行拼接。
综上可知,哥斯拉发送的第一个POST请求中,请求数据的加密过程为:将原始数据(即shells/payloads/php/assets/payload.php
文件)进行base64编码,然后与shell密钥(生成shell时设置的那个,初始为key)md5值的前16位按位异或,得到编码数据,最终还需要对数据编码进行URL
编码,拼接上shell密码(生成shell时设置的那个,作为POST请求参数的那个,初始为pass)。作为POST报文请求体,POST到服务器。
解密过程与加密过程正好相反:从pass=编码数据
中提取编码数据
,依次经过URL
解码然后base64解码,再与shell密钥md5值的前16位按位异或,最后base64
解码即可得到原始请求数据。
第二个数据包分析(2和3数据包是一样的)
第二个数据包的产生在this.payloadModel.test()
找到test()
函数 是一个布尔型的。测试成功后,会返回true。if函数顺利运行,连接成功。
1 | if(this.payloadModel.test()){ |
trim() 函数移除字符串两侧的空白字符或其他预定义字符。
查看evalFunc
函数,发送了请求
this.payloadModel.test()
的执行过程为:调用了evalFunc
函数。该函数将数据POST给服务器,并获得服务器的响应。
1 | byte[] result = evalFunc(null, "test", parameter); // 获得服务器的响应 |
将服务器的返回结果格式修改后,与ok对比,判断为ok则连接成功。
1 | String codeString = new String(result); |
说明在evalFunc
函数执行过程中,已经得到了明文的服务器返回数据,因此服务器返回报文的解密过程肯定是在evalFunc
函数中。
将evalFunc
的内容单独提出来
1 | public byte[] evalFunc(String className, String funcName, ReqParameter praameter) { |
定位到public HttpResponse sendHttpResponse
函数,探究sendHttpResponse
的执行过程。找到HttpResponse
文件(util/http/HttpResponse.java
)查看是否有解密过程。(即这一行运行的时候,应该是明文的“OK”,现在需要探究是从哪里开始变成解密后的明文ok)
1 | this.result = this.shellEntity.getCryptionModel().decode(this.result); //解密响应数据 |
找到decode
函数(shells/cryptions/phpXor/PhpXor.java)
解密函数D
1 | public byte[] D(String data) { |
删除混淆字符函数findStr
(左右追加的部分,这个例子里没有追加)
1 | public String findStr(byte[] respResult) { |
由上述代码可知,服务器响应数据解密过程并不复杂,先调用findStr
函数删除服务器响应数据左右附加的混淆字符串(对于PHP_XOR_BASE64
加密方式来说,前后各附加了16位的混淆字符),然后将得到的数据进行url
解码然后base64
解码,最后再和shell连接密钥md5值的前16位按位异或,将结果最终base64解码
即完成响应数据的解密。
至此,我们已经明白了哥斯拉的PHP_XOR_BASE64
加密shell,在测试连接
过程中产生POST请求和响应数据的加密和解密过程。哥斯拉的其他shell功能,除了产生的POST请求内容不同外,数据的加解密过程都是一样的。
解密部分
涉及到的函数
(可以运行的解密代码,在概述里附上了)
1 | # 先删除data的混淆数据 |
第一个数据包请求
内容是payload.php
第二、三个数据包请求
2、3数据包请求一样
抓包分析-进入shell的数据包
进入shell后,会产生三个数据包
第一个数据包
解码后发现,数据包内容是payload.php
的内容,也就是payload功能函数。之所以在第一个数据包将payload.php
内容传入,目的是后续的shell操作请求功能的时候,只需要发送函数名称以及对应的函数参数即可。不需要再请求一大长串数据包。
第二个数据包
解码后发现,数据包内容是methodName=test
,他产生于this.payloadModel.test()
的eval
函数中
1 | // this.payloadModel.test()的test()函数功能 |
可以到payload.php
中,去查找test函数的内容
1 | function test(){ |
根据上面的函数可以分析得到:test函数调用了eval
函数,eval
函数将传进的funcName
对应的就是payload.php
中的功能函数。eval将去执行payload.php
里面的test函数。得到的结果是返回了OK。eval函数再将这个结果存入byte[] result
。this.payloadModel.test()
的test()函数
会对比结果,如果和ok值一样,则代表连接成功。
第三个数据包
解码后发现,数据包内容是methodName=getBasicsInfo
和第二个数据包分析思路一致,这里的getBasicsInfo
也是payload.php
中的函数。它的作用是,显示攻击目标的基础信息。包括php信息、用户、编码等一系列信息。
服务器端返回结果解密:
去掉前16位和后16位的冗余数据,将中间的base64部分解密(不需要再进行一步url解码了),可以得到详细的信息👇
抓包分析-其他操作数据包
执行命令
进入shell之后执行命令
ls
抓包:👇(产生一个数据包)
解码(有一些request的数据包base64有一些小错误,输出结果只需要再把个别部分base64解码一下就可以了。)
request解码内容为:cmdLine=ls&methodName=execCommand
服务器响应结果为ls执行后显示的内容👇
规则提取思路
默认的密码为pass,密钥为key,可以将默认的密码作为规则进行筛选。
payload.php
文件内容如下所示,其中定义了shell所有功能所需的一系列函数,哥斯拉第一次连接shell时,将这些函数定义发送给服务器并存储在session中,后续的shell操作只需要发送函数名称以及对应的函数参数即可。数据包请求会有该流量特征:
pass=methodName=‘函数名’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21run
bypass_open_basedir
formatParameter
evalFunc
deleteDir
deleteFile
copyFile
moveFile
getBasicsInfo
getFile
readFileContent
uploadFile
newDir
newFile
execCommand
execSql
base64Encode
test
get
includeCode
base64Decode默认密钥key带来的第一个请求包,包含特征
AQQWDQ==