简介
FastJson是Java的一个库,它可以将Java对象转换成JSON字符串,也可以将JSON字符串转换成Java对象
回显Fastjson使用版本
在实战中如果不知道使用的是什么版本的Fastjson,可以将请求包中的 Content-Type
改成字段内容改成 application/json
,请求体中让后端报错,比如 [{"a":"a\x]
FastJSON使用
package com.fastjson_study;
import com.alibaba.fastjson.*;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class f1 {
public static void main(String[] args) {
Person person = new Person();
String s = JSON.toJSONString(person, SerializerFeature.WriteClassName); // 将JavaBean对象转换成JSON字符串,并写入类名,调用构造方法和所有getter方法
System.out.println(s);
// 构造方法被调用...
// getAge方法被调用...
// getName方法被调用...
// {"@type":"com.fastjson_study.Person","age":19,"name":"xiaowang"}
Object ObjectParse = JSON.parse(s); // 将JSON字符串转换成Object对象,会调用构造方法和所有的setter方法
System.out.println(ObjectParse.getClass().getName()); // com.fastjson_study.Person
// 构造方法被调用...
// setAge方法被调用...
// setName方法被调用...
// com.fastjson_study.Person
Person PersonParse = JSON.parseObject(s,Person.class); // 将JSON字符串转换成指定的对象类型,会调用
System.out.println(PersonParse);
// 构造方法被调用...
// setAge方法被调用...
// setName方法被调用...
// Person{name='xiaowang', age=19}
}
}
package com.fastjson_study;
public class Person {
public String name = "xiaowang";
public Integer age = 19;
public Person(){
System.out.println("构造方法被调用...");
}
public void setName(String name) {
System.out.println("setName方法被调用...");
this.name = name;
}
public void setAge(Integer age) {
System.out.println("setAge方法被调用...");
this.age = age;
}
public String getName() {
System.out.println("getName方法被调用...");
return name;
}
public Integer getAge() {
System.out.println("getAge方法被调用...");
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
FastJSON提供了特殊字段@type
,该字段可以指定反序列化任意类,会自动调用其类属性的特定的set、get方法。
在反序列化时,public修饰符的属性会被反序列化赋值,private修饰符的属性不会直接的进行反序列化赋值而是通过setXxx的方法进行赋值。
Fastjson反序列化流程分析
根据这一特性,我们可以自己写一个类来测试一下,这里我在Person2类中定义了私有的属性_name,所以 set
和 get
方法都是小写加_
import com.alibaba.fastjson.JSON;
class Person2{
private String _name;
public Person2() {
System.out.println("构造方法执行");
}
public void get_name() {
System.out.println("getName执行");
}
public void set_name(String cmd) throws Exception{
Runtime.getRuntime().exec(cmd);
}
}
public class Test {
public static void main(String[] args) {
String pstr = "{\"@type\":\"FastjsonResearch.Person2\",\"_name\":\"/System/Applications/Calculator.app/Contents/MacOS/Calculator\"}";
Person2 p = JSON.parseObject(pstr, Person2.class);
}
}
程序从这开始将json字符串给Fastjson去解析
我们来到这里,parseObject
有好几个重写方法,目前我们先熟悉一下Fastjson反序列化整个流程,后面遇到再说。
这个 pareObject
调用的是另一个重写方法的 pareObject
这里还是调用了另一个重写的 pareObject
方法,继续跟进
DefaultJSONParser
将我们输入的json字符串和全局解析配置创建了一个默认JSON解析器。
再往下走,这个默认JSON解析器调用了 pareObject
方法
这里在反序列化器中尝试获取我们写的Person2类的反序列化器,很显然是没有的。
随后调用了 getDeserializer
方法,再跟进看一下
在这里就是获取我们的Person2这个类的全类名,有个替换操作,然后再判断我们的这个类名在不在黑名单(拒绝生成反序列化器列表)内,这个列表里只有一个 java.lang.Thread
,当然也是不在的。
再往下走,走到了调用 createJavaBeanDeserializer
方法这里,看似是要创建一个JavaBean的反序列化器,继续跟进看一下。
这里获取Person2类上的JSONType注解,我们没有使用该注解,所以返回null
再继续往下跟,发现这里实例化了 JavaBeanDeserializer
类
进入 JavaBeanDeserializer
类,发现调用自己的另一个重写的构造方法。
这里的操作其实就是获取所有的 set
方法,由于我们这里只有一个 set
方法,就继续往下看了。
此时, createJavaBeanDeserializer
方法已经执行完毕了,使用 putDeserializer
把Person2类作为键,获取的JavaBean反序列化器作为值存到 ParserConfig
中的 derializers
变量中,该变量是一个HashMap,随后返回了这个JavaBean反序列化器。
这里也返回了反序列化器
继续往下走,可以看到反序列化器调用了 deserialze
反序列化方法。
走到这里条件符合,是默认的TYPE KEY(@type)
这里判断token是否等于JSONToken.RBRACE,JSONToken.RBRACE也就是 }
符号,符合条件调用了 lexer
的 nextToken
方法,继续下一次循环。
此时,key是 _name
也就是json字符串中的键名,已经不是默认的TYPE KEY(@type)了。
再往下走,到这里调用了 createInstance
方法,跟进看一下
它获取了JavaBean反序列化器的默认构造方法并且实例化了,返回了一个对象。
这里已经得到了一个Person2类的对象了,object
自然不是null
再往下,调用了 parseField
方法,看这个名字应该是解析字段,跟进看一下。
这一连串的操作是处理字段中的 _
和 -
符号,再通过 getFieldDeserializer
方法就获取对应的字段反序列化器了。
后面调用这个字段反序列化器的 parseField
方法,继续跟进
可以看到这里调用了 setValue
方法,参数 object
正是我们的Person2实例化出来的对象,value
则是71行反序列化获取的 _name
键的值,看看 setValue
方法里面是在干嘛。
这里判断了 fieldInfo
的method方法是不是只有一个get方法,很显然不是。
再继续往下,好像有点意思了,这里是真正调用 set_name
方法的地方,调用method的invoke方法,method是 set_name
方法,那么正好,我们只需要在Person2类中定义一个对应字段的setter方法,其方法有一个参数即可。value
就是 set_name
方法需要传入的参数,也就是执行的命令。
这里,就弹出了计算器了,说明触发点是在这里。
好了,现在我们已经理清楚了Fastjson整个反序列化流程,接下来我们需要配合一些JDK内的一些链来利用。
Author: wileysec
Permalink: https://wileysec.github.io/1f78acdc2202.html
Comments