事件背景
热点漏洞
漏洞说明
1. 漏洞原理:FineReport/FineBI channel接口能接受序列化数据并对其进行反序列化。配合帆软内置CB链会导致任意代码执行。
2. 组件描述:FineReport是一款企业级报表设计和数据分析工具,它提供了丰富多样的组件,用于创建和展示数据报表。
FineBI 是一款企业级的商业智能 (BI) 平台,提供了丰富的功能和组件,用于数据分析、报表生成和数据可视化等任务
3. 影响版本: 2022-08-12 之前的 FineReport10.0/11.0、FineBI5.1 系列均受影响
漏洞复现
环境搭建:https://fine-build.oss-cn-shanghai.aliyuncs.com/finebi/5.1.5/stable_test/backup/2021-02-26/exe/spider/linux_unix_FineBI5_1-CN.sh
安装在虚拟机环境中,需要给虚拟机至少6G内存才能安装终端输入./linux_unix_FineBI5_1-CN.sh即可安装,安装后,直接运行程序,在环境的bin目录中输入命令
tail -F output.log
可以看到环境的URL,访问并登录后如下
向idea的jar库导入FineBI中web中的库,最后跑下脚本即可
EXP已隐藏,仅分享漏洞分析思路
服务器开启端口监听并执行Java代码
最后反弹shell成功,说明恶意执行成功
漏洞分析
访问漏洞接口/webroot/decision/remote/design/channel发现
对环境文件的接口搜索一下发现
看到一个类com.fr.decision.extension.report.api.remote.RemoteDesignResource.onMessage,在idea全局搜搜索查看可以发现
这里就是channel的入口了,步入WorkContext.handleMessage()中
继续跟进messageListener.handleMessage()
看到this.deserializeInvocation(var1, var2),看着想序列化的方法,继续跟进
这里跟进看SerializerHelper.deserialize()
这里的var1是上面传的GZipSerializerWrapper.wrap(InvocationSerializer.getDefault()),跟进看
这里返回的是GZipSerializerWrapper()对象,同时this.serializer为InvocationSerializer对象再回到前面
var0被包装,送入到var1中的deserialize中,前面提到传入返回的对象为GZipSerializerWrapper()因此找到GZipSerializerWrapper.deserialize步入
这里对传入的包再次进行包装最后送入this.serializer.deserialize(var2),前面提到this.serializer对象在构造函数里被赋值了,为InvocationSerializer,最后再步入InvocationSerializer.deserialize
出现了,有两个readObject(),Java反序列化的触发点,总结
请求体传入的字节 byte[] var0
包装1: ByteArrayInputStream var1 = new ByteArrayInputStream(var0);
包装2: GZIPInputStream var2 = new GZIPInputStream(var1)
包装3: CustomObjectInputStream var3 = new CustomObjectInputStream(var2)
反序列化: (Map)var3.readObject()
因此我们需要做的就是构造与几个包装刚好相反的触发链,根据web的包来看,发现其中自带CB1的链子
因此构造CB1链攻击,但是经过尝试,发现ysoserial生成的payload无法达到攻击目的且报了CB1的错
import com.fr.serialization.JDKSerializer;
import java.io.*;
import java.util.Base64;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class Test {
public static void main(String[] args) {
try {
String base64String = getBase64();
byte[] bytes = Base64.getDecoder().decode(base64String);
// 使用Java反序列化漏洞利用工具生成一个包含恶意代码的序列化对象,并将其序列化成字节数组
byte[] maliciousBytes = bytes;
// 构造一个GZIP格式的字节数组,将恶意字节数组存储在GZIP数据块中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos);
gzipOutputStream.write(maliciousBytes);
gzipOutputStream.finish();
byte[] gzipBytes = baos.toByteArray();
// 将GZIP格式的字节数组封装成一个输入流对象,并作为var0参数传入Env函数中
InputStream var0 = new ByteArrayInputStream(gzipBytes);
Env(var0);
}catch (Exception e){e.printStackTrace();}
}
private static void Env(InputStream var0) throws IOException, ClassNotFoundException {
GZIPInputStream var2 = new GZIPInputStream(var0);
JDKSerializer.CustomObjectInputStream var3 = new JDKSerializer.CustomObjectInputStream(var2);
Map map = (Map) var3.readObject();
}
private static String getBase64() {
return "";
}
}
当将yso的包替换后则成功,最后得出结论应该是yso CB1的链子无法应用于该系统,可能是版本不匹配,不过P牛文章中提到的链子可以
CommonsBeanutils与无commons-collections的Shiro反序列化利用 | 离别歌
可以弹calc,说明P牛的链子可以触发该环境自带jar包的CB1,因此利用了P牛的链子构造出复现中的EXP。
最后攻击的流量为被gzip压缩后带有Java反序列化特征的流。