0. 前言

记录在FastJson反序列化RCE漏洞分析和利用时的一些细节问题。

1. TemplatesImpl的利用链

关于 parse 和 parseObject

FastJson中的 parse() 和 parseObject()方法都可以用来将JSON字符串反序列化成Java对象,parseObject() 本质上也是调用 parse() 进行反序列化的。但是 parseObject() 会额外的将Java对象转为 JSONObject对象,即 JSON.toJSON()。所以进行反序列化时的细节区别在于,parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject() 由于多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法。parseObject() 的源代码如下:

public static JSONObject parseObject(String text) {
Object obj = parse(text);
if (obj instanceof JSONObject) {
return (JSONObject) obj;
} return (JSONObject) JSON.toJSON(obj);
}

举个简单的例子:

public class FastJsonTest {

    public String name;
public String age;
public FastJsonTest() throws IOException{
} public void setName(String test) {
System.out.println("name setter called");
this.name = test;
} public String getName() {
System.out.println("name getter called");
return this.name;
} public String getAge(){
System.out.println("age getter called");
return this.age;
} public static void main(String[] args) {
Object obj = JSON.parse("{\"@type\":\"fastjsontest.FastJsonTest\",\"name\":\"thisisname\", \"age\":\"thisisage\"}");
System.out.println(obj); Object obj2 = JSON.parseObject("{\"@type\":\"fastjsontest.FastJsonTest\",\"name\":\"thisisname\", \"age\":\"thisisage\"}");
System.out.println(obj2);
} }

上述代码运行后可以看到,执行parse() 时,只有 setName() 会被调用。执行parseObject() 时,setName()、getAge()、getName() 均会被调用。

为什么会触发getOutputProperties()

感觉上 parse() 进行反序列化创建Java类应该只会调用 setter 方法进行成员变量赋值才对,会什么会触发TemplatesImpl类中的 getOutputProperties() 方法呢?

另外 _outputProperties 成员变量和 getOutputProperties() 明明差了一个_字符,是怎么被 FastJson 关联上的?

如上一小节所述,parse() 进行反序列化时其实会调用某些特定的 getter 方法进行字段解析,而 TemplatesImpl类中的 getOutputProperties() 方法恰好满足这一条件。

FastJson反序列化到Java类时主要逻辑如下:

  1. 获取并保存目标Java类中的成员变量、setter、getter。
  2. 解析JSON字符串,对字段逐个处理,调用相应的setter、getter进行变量赋值。

我们先看第一步,这里由 JavaBeanInfo.build() 进行处理,FastJson会创建一个filedList数组,用来保存目标Java类的成员变量以及相应的setter或getter方法信息,供后续反序列化字段时调用。

filedList大致结构如下:

[
{
name:"outputProperties",
method:{
clazz:{},
name:"getOutputProperties",
returnType:{},
...
}
}
]

FastJson并不是直接反射获取目标Java类的成员变量的,而是会对setter、getter、成员变量分别进行处理,智能提取出成员变量信息。逻辑如下:

  1. 识别setter方法名,并根据setter方法名提取出成员变量名。如:识别出setAge()方法,FastJson会提取出age变量名并插入filedList数组。
  2. 通过clazz.getFields()获取成员变量。
  3. 识别getter方法名,并根据getter方法名提取出成员变量名。

可以看到在 JavaBeanInfo.build() 中,有一段代码会对getter方法进行判断,在某些特殊条件下,会从getter方法中提取出成员变量名并附加到filedList数组中。而TemplatesImpl类中的 getOutputProperties() 正好满足这个特定条件。getter方法的处理代码为:

JavaBeanInfo.java

public static JavaBeanInfo build(Class<?> clazz, Type type, PropertyNamingStrategy propertyNamingStrategy) {
...
for (Method method : clazz.getMethods()) { // getter methods
String methodName = method.getName();
if (methodName.length() < 4) {
continue;
} if (Modifier.isStatic(method.getModifiers())) {
continue;
} if (methodName.startsWith("get") && Character.isUpperCase(methodName.charAt(3))) {
if (method.getParameterTypes().length != 0) {
continue;
} // 关键条件 if (Collection.class.isAssignableFrom(method.getReturnType()) //
|| Map.class.isAssignableFrom(method.getReturnType()) //
|| AtomicBoolean.class == method.getReturnType() //
|| AtomicInteger.class == method.getReturnType() //
|| AtomicLong.class == method.getReturnType() //
) {
String propertyName; JSONField annotation = method.getAnnotation(JSONField.class);
if (annotation != null && annotation.deserialize()) {
continue;
} if (annotation != null && annotation.name().length() > 0) {
propertyName = annotation.name();
} else {
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
} FieldInfo fieldInfo = getField(fieldList, propertyName);
if (fieldInfo != null) {
continue;
} if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
} add(fieldList, new FieldInfo(propertyName, method, null, clazz, type, 0, 0, 0, annotation, null, null));
}
}
}
...
}

接下来,FastJson会语义分析JSON字符串。根据字段key,调用filedList数组中存储的相应方法进行变量初始化赋值。具体逻辑在 parseField() 中实现:

JavaBeanDeserializer

public boolean parseField(DefaultJSONParser parser, String key, Object object, Type objectType,
Map<String, Object> fieldValues) {
JSONLexer lexer = parser.lexer; // xxx FieldDeserializer fieldDeserializer = smartMatch(key); ... return true;
}

这里调用了一个神奇的 smartMatch() 方法,smartMatch()时会替换掉字段key中的_,从而 _outputProperties 和 getOutputProperties() 可以成功关联上。

JavaBeanDeserializer

public FieldDeserializer smartMatch(String key) {
if (fieldDeserializer == null) {
boolean snakeOrkebab = false;
String key2 = null;
for (int i = 0; i < key.length(); ++i) {
char ch = key.charAt(i);
if (ch == '_') {
snakeOrkebab = true;
key2 = key.replaceAll("_", "");
break;
} else if (ch == '-') {
snakeOrkebab = true;
key2 = key.replaceAll("-", "");
break;
}
}
if (snakeOrkebab) {
fieldDeserializer = getFieldDeserializer(key2);
if (fieldDeserializer == null) {
for (FieldDeserializer fieldDeser : sortedFieldDeserializers) {
if (fieldDeser.fieldInfo.name.equalsIgnoreCase(key2)) {
fieldDeserializer = fieldDeser;
break;
}
}
}
}
}

为什么需要对_bytecodes进行Base64编码

细心的你可以发现,PoC中的 _bytecodes 字段是经过Base64编码的。为什么要这么做呢? 分析FastJson对JSON字符串的解析过程,原来FastJson提取byte[]数组字段值时会进行Base64解码,所以我们构造payload时需要对 _bytecodes 进行Base64处理。FastJson的处理代码如下:

ObjectArrayCodec
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
final JSONLexer lexer = parser.lexer;
// ......省略部分代码
if (lexer.token() == JSONToken.LITERAL_STRING) {
byte[] bytes = lexer.bytesValue(); // ... 在这里解析byte数组值
lexer.nextToken(JSONToken.COMMA);
return (T) bytes;
} // 接着调用JSONScanner.bytesValue() JSONScanner
public byte[] bytesValue() {
return IOUtils.decodeBase64(text, np + 1, sp);
}

 

FastJson反序列化漏洞利用的三个细节 - TemplatesImpl的利用链的更多相关文章

  1. fastjson反序列化漏洞实际案例利用

    fastjson反序列化rce实际案例利用全过程: 存在问题网站:http://***.com/ 在网站上寻找一些安全漏洞的时候,发现一条json数据包 数据包如下: POST /*** HTTP/1 ...

  2. Fastjson反序列化漏洞概述

    Fastjson反序列化漏洞概述 ​ 背景 在推动Fastjson组件升级的过程中遇到一些问题,为帮助业务同学理解漏洞危害,下文将从整体上对其漏洞原理及利用方式做归纳总结,主要是一些概述性和原理上的东 ...

  3. Java安全之Fastjson反序列化漏洞分析

    Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...

  4. Fastjson反序列化漏洞分析 1.2.22-1.2.24

    Fastjson反序列化漏洞分析 1.2.22-1.2.24 Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间互相转换,提供两 ...

  5. Fastjson反序列化漏洞复现

    Fastjson反序列化漏洞复现 0x00 前言 对Fastjson反序列化漏洞进行复现. 0x01 漏洞环境 靶机环境:vulhub-fastjson-1.2.24 ip:172.16.10.18 ...

  6. Fastjson反序列化漏洞基础

    Fastjson反序列化漏洞基础 FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转换为等效的Java对象. 0x0 ...

  7. fastjson反序列化漏洞研究(上)

    前言 最近护网期间,又听说fastjson传出“0day”,但网上并没有预警,在github上fastjson库中也有人提问关于fastjson反序列化漏洞的详情.也有人说是可能出现了新的绕过方式.不 ...

  8. .NET高级代码审计(第三课)Fastjson反序列化漏洞

    0X00 前言 Java中的Fastjson曾经爆出了多个反序列化漏洞和Bypass版本,而在.Net领域也有一个Fastjson的库,作者官宣这是一个读写Json效率最高的的.Net 组件,使用内置 ...

  9. fastjson 反序列化漏洞利用总结

    比赛遇到了,一直没利用成功,这里做个记录. 环境搭建 首先用 vulhub 搭建 fastjson 的漏洞环境. 漏洞环境程序的逻辑为接收 body 的数据然后用 fastjson 解析. 漏洞利用 ...

随机推荐

  1. (0!=0)==true? 记一个匪夷所思的问题

    最近换了份工作,公司的开发框架是基于SSH自己搭建的.这个问题是我在解决一个需求的时候遇到的,其实解决这个疑惑的过程也就是读框架源码的过程,特此记录一下. 问题:ba.getState()!=CbBa ...

  2. poi学习

    需要节点  <dependency>  <groupId>org.apache.poi</groupId>  <artifactId>poi</a ...

  3. 线程安全的Dictionary

    线程安全的DictionaryConcurrentDictionary<TKey, TValue> 类

  4. js带文字的圆随机运动

    首先是html代码(其实就只有一个画布,记得要把外部js引入写在body底部 <!doctype html> <html> <head> <meta http ...

  5. 【Linux】应用程序内存段布局

    一.各段内存布局 内存布局图 1.BSS段(Block Started by Symbol)未初始化段 2.linux ELF base address is 0x8048000 应用程序虚拟地址起始 ...

  6. 浅谈SQL Server中的事务日志(四)----在完整恢复模式下日志的角色

    简介 生产环境下的数据是如果可以写在资产负债表上的话,我想这个资产所占的数额一定不会小.而墨菲定律(事情如果有变坏的可能,无论这种可能性有多小,它总会发生)仿佛是给DBA量身定做的.在上篇文章介绍的简 ...

  7. Android学习——Fragment静态加载

    从今天开始做一套安卓的学习笔记,开发环境是Android Studio,把学习过程中的知识和遇到的问题都写在这里,先从Fragment开始学起. Fragment概述 Fragment是Android ...

  8. NodeJs安装less(npm方式)

    上一次讲了如何在浏览器端解析less文件,这次是在cmd中使用npm中的less模块来解析 详解如下 首下我们去下载一个https://nodejs.org/en/,   一路next之后,因为文件不 ...

  9. python 正则表达式 re(邮箱匹配为例)

    import rere.match(pat,string)re.compile()re.sub(pat,repl,string)re.findall(pat,text)一般字符:. 匹配任意除换行符& ...

  10. 如何删除Windows 10中的内存转储文件

    内存转储文件是由Windows产生的.以下情况下可能产生内存转储文件: 计算机崩溃蓝屏 内存错误 硬件问题 内存转储文件包含计算机系统崩溃时的详细的参数副本.用于帮助识别导致系统崩溃的原因.Windo ...