public class test {
private String name ="c1oud";
public int age = 15;
public String getName() {
System.out.println("调用了getName");
return name;
}
public int getAge() {
System.out.println("调用了getAge");
return age;
}
public void setAge(int age) {
System.out.println("调用了setAge");
this.age = age;
}
public void setName(String name) {
System.out.println("调用了setName");
this.name = name;
}
}
然后创建这个对象,并且使用fastjson中的toJSONString把他序列化为JSON格式
public static final String toJSONString(Object object); // 将JavaBean序列化为JSON文本
public static final String toJSONString(Object object, boolean prettyFormat); // 将JavaBean序列化为带格式的JSON文本
import com.alibaba.fastjson.JSON;
public class ser {
public static void main(String args[]){
test a = new test();
String Json = JSON.toJSONString(a);
System.out.println(Json);
}
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class ser {
public static void main(String args[]){
test a = new test();
String Json = JSON.toJSONString(a, SerializerFeature.WriteClassName);
System.out.println(Json);
}
}
反序列化
接着来看看JSON字符串转化为JAVA对象
在fastjson用于反序列化的函数主要是下面两个:parse和parseObject
public static final Object parse(String text); // 把JSON文本parse为JSONObject或者类对象
public class test {
private String name ="c1oud";
public int age = 15;
public String getName() {
System.out.println("调用了getName");
return name;
}
public int getAge() {
System.out.println("调用了getAge");
return age;
}
// public void setAge(int age) {
// System.out.println("调用了setAge");
// this.age = age;
// }
// public void setName(String name) {
// System.out.println("调用了setName");
// this.name = name;
// }
}
反序列化类ser
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class ser {
public static void main(String args[]){
String str = "{\"age\":18,\"name\":\"c1\"}";
System.out.println("反序列化:");
System.out.println(JSON.parseObject(str,test.class));
System.out.println(JSON.parseObject(str,test.class).getName());
System.out.println(JSON.parseObject(str,test.class).getAge());
}
}
前言
在学
fastjson
之前,先来说下JSON
:JSON
是一种轻量级的数据交换格式,可以跨语言,JSON
是JS
对象的字符串表示法,它使用文本表示一个JS
对象。在平常应用里,经常会遇到JSON
与字符串互相转换、解析,可以说是标准的数据交换格式。Fastjson介绍
fastjson
是阿里巴巴的开源Json
解析库,它可以解析Json
格式的字符串,快速将Json
与Java Bean对象
相互转换,相当于就是一个JSON
处理器,可以将Java Bean
对象序列化为JSON
字符串,也可以从JSON
字符串反序列化到JavaBean
对象它有三个关键方法:
JSON
字符串:JSON.toJSONString
JSON
字符串转换成对象:JSON.parse和JSON.parseObject()
在利用
toJSONString
来序列化对象的时候,这个方法里有很多的重载方法,常用的有:DEMO
序列化
这里看一个
demo
,先添加一段依赖然后随便写一个
Java Bean
类然后创建这个对象,并且使用
fastjson
中的toJSONString
把他序列化为JSON
格式可以看到,调用
JSON.toJSONString
其实是调用了类的getter方法
的,调试跟一下也是可以看到的。除了这样序列化以外,还可以在
toJSONString
方法后面加一个SerializerFeature.WriteClassName
参数,这样序列化后会多写入一个@type
,即写上被序列化的类名(有包的话,输出的会是包名.类名
的格式):反序列化
接着来看看
JSON字符串
转化为JAVA
对象在
fastjson
用于反序列化的函数主要是下面两个:parse和parseObject
先来看看
toJSONString
中只有一个参数序列化后再反序列化的情况再来看看
toJSONString
有两个参数序列化后再反序列化后的情况可以发现如果不带上
@type
指明类名,是没法得到类对象的。得到的是Jsonobject
如果指明了类名,使用
parse
的时候,不仅会得到类对象,还会调用这个对象的setter
方法但是为什么指明了类名序列化后再反序列化得到的类对象是
test@46ee7fe8
呢,@
前面是类名好理解,后面这个是什么呢? 其实是因为所有的类都直接或者间接继承自object
,你在你的类里面没有写toString
,他就会默认执行object
里面的指定代码,这只是object
类中tostring
的写法是这样而已。接着看第二个反序列化的函数
parseObject
其实看源码可以知道,
parseObject
其实就是调用一次parse
,然后转换成JSONObject
还是先看
toJSONString
中只有一个参数序列化后再反序列化的情况这个的返回值和
parse第一种情况
一样,都是一个JSONObject
对象,接着看看toJSONString
有两个参数序列化后再反序列化后的情况可以看到它也得到了类对象,还调用了
setter
和getter
方法。这个是重点,后面还会提到,先继续看这里
parseObject
还有另一种用法,当没有指定@type
,但又想反序列化成指定的类对象时,则需要通过parseObject
同时传入该类的class
对象才能反序列化成指定的对象。也就是说第二个参数是我们手动控制用来转化类型的类对象可以看到这里其实也除了返回类对象,只调用了
setter
方法。简单总结一下:其实也就是在引入
@type
后,parse
成功触发了setter
方法,parseObject
则成功触发了setter
和getter
方法漏洞出现
这里再来看刚刚我们提到是重点的地方:
在
fastjson
中,有一个autotype
功能,只要json
的字符串中有@type
属性,那么它的值就会被反序列化成指定的类型;如果使用的是parseObject
进行反序列化的话,同时还会调用setter
和getter
方法。看到这个调用
setter
和getter
的机制,很容易想到之前Commons Beanutils
文章里的PropertyUtils.getProperty()
方法,让它会调用对应属性的getter
方法,也就是要调用到TemplatesImpl#getOutputProperties()
方法,以此一步一步到最后加载字节码来rce
。但是如果此时如果目标类里没有
setter方法
,但又想在反序列化时给变量赋值,那该怎么做呢?实际上用当成员变量声明为
public
的时候,也是可以调用的,这里我们把age
属性设置为public,name
属性设置为private
来看看测试结果Test类
反序列化类ser
可以看到由于只有
age
属性是public
,所以age
可以赋新的值;name
是private
,所以只能是之前的值,我们想反序列化的c1
并没有写进去。但是这里存在一个问题,如果成员变量设为
public
属性,就不是javabean
了,并且开发人员开发javabean
的时候,里面更不会有public
属性了,那还有什么方法能解决上面的问题呢?其实只需要在
parseObject
里再提供一个Feature.SupportNonPublicField
参数即可:可以看到这样的话,
private
属性的name
就被我们重新赋值写进去了。Fastjson1.2.24的Gadgets
在实际应用里,基本是不会出现直接给恶意类来利用的,所以在这个版本要想利用的话,有这两条链子:
先来看下
TemplatesImpl
的利用链:TemplatesImpl Gadgets
之前说了,只要调用到
TemplatesImpl#getOutputProperties()
这个getter
方法,后面的链子就和之前分析CB链
一样的了,所以在这里的目的是要调用到getOutputProperties()
方法回顾一下
TemplatesImpl
加载字节码需要的几个条件还要注意,因为这不是反序列化
java
的序列化对象,所以这里没法用反射得到私有变量并为其赋值,所以:_name
属性和_tfactory
属性都是私有属性,并且没有setter
方法,所以需要用Feature.SupportNonPublicField
参数来为其赋值写出
poc
:参考文章
http://arsenetang.com/2022/03/26/Java篇之fastjson反序列化/
https://blog.csdn.net/rfrder/article/details/123216053