基础知识|反序列化命令执行漏洞

作者: h0we777
免责声明:本文仅供学习研究,严禁从事非法活动,任何后果由使用者本人负责。

0x00 简介

反序列化漏洞是基于序列化和反序列化的操作。

0x01 序列化

序列化常用于将程序运行时的对象状态以二进制的形式存储于文件系统中,然后可以在另一个程序中对序列化后的对象状态数据进行反序列化恢复对象。简单的说就是可以基于序列化数据实时在两个程序中传递程序对象。

0x02 java反序列化

把java对象转换为子节序列的过程便与保存在内存或文件中,实现跨平台通讯和持久化存储。ObjectOutputStream类的writeObject()方法可以实现序列化。反序列化则指把子节序列恢复为java对象的过程,相应的,ObjectInputStream类的readObject()方法用于反序列化。下面是将字符串对象先进行序列化,存储到本地文件,然后再通过反序列化进行恢复到示例代码:

public static void main(String args[]) throws Exception {
String obj = "hello world!";
// 将序列化对象写入文件object.db中
FileOutputStream fos = new FileOutputStream("object.db");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj); os.close();
 // 从文件object.db中读取数据
FileInputStream fis = new FileInputStream("object.db");
ObjectInputStream ois = new ObjectInputStream(fis);
// 通过反序列化恢复对象obj String obj2 = (String)ois.readObject();
ois.close();
}

问题在于,如果java应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能带来任意代码执行。所以这个问题的根源在于类ObjectInputStream在反序列化时,没有对生成的对象的类型做限制;假若反序列化可以设置java类型的白名单,那么问题的影响就小了很多。

0x03 成因

如果java应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能带来任意代码执行。

重写ObjectInputStream对象的resolveClass方法中的检测可被绕过。

使用第三方的类进行黑名单控制。容易存在漏网之鱼,如果后期添加了新的功能,还可能引入新的漏洞利用方式。

使用了不安全的基础库:


commons-fileupload 1.3.1
commons-io 2.4
commons-collections 3.1
commons-logging 1.2
commons-beanutils 1.9.2
org.slf4j:slf4j-api 1.7.21
com.mchange:mchange-commons-java 0.2.11
org.apache.commons:commons-collections 4.0
com.mchange:c3p0 0.9.5.2
org.beanshell:bsh 2.0b5
org.codehaus.groovy:groovy 2.3.9
org.springframework:spring-aop 4.1.4.RELEASE

0x04 原理


public class test{
    public static void main(String args[])throws Exception{
          //定义obj对象
        String obj="hello world!";
          //创建一个包含对象进行反序列化信息的”object”数据文件
        FileOutputStream fos=new FileOutputStream("object");
        ObjectOutputStream os=new ObjectOutputStream(fos);
          //writeObject()方法将obj对象写入object文件
        os.writeObject(obj);
        os.close();
          //从文件中反序列化obj对象
        FileInputStream fis=new FileInputStream("object");
        ObjectInputStream ois=new ObjectInputStream(fis);
          //恢复对象
        String obj2=(String)ois.readObject();
        System.out.print(obj2);
        ois.close();
    }
}

0x05 用途

把对象的字节序列永远地保存到硬盘上,通常存放在一个文件中;

在网络上传送对象的字节序列。

0x06 应用场景

一般来说,服务器启动后,就不会再关闭了,但是如果逼不得已需要重启,而用户会话还在进行相应的操作,这时就需要使用序列化将session信息保存起来放在硬盘,服务器重启后,又重新加载。这样就保证了用户信息不回丢失,实现永久化保存。

在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便减轻内存压力或便与长期保存。

比如最常见的是web服务器中的session对象,当有10万用户并发访问,就有可能出现10万个session对象,内存可能吃不消,于是web容器就会把一些seesion先序列化到硬盘中,等要用了再把保存在硬盘中的对象还原到内存中。

http参数、cookie、seesion、存储方式可能是base64(rO0)、压缩后的base64(H4sl)、MII等。

Servlets HTTP、Sockets、Session管理器包含的协议就包括JMX、RMI、JMS、JNDI等(\xac\xed)

xml Xstream、XMLDecoder等(HTTP body:Content-Type:applicatin/xml)

json(Jackson、fastjson)http请求中包含。

0x07 检测流程

反序列化操作一般应用在导入模版文件、网络通信、数据传输、日志格式化存储或DB存储等业务场景。因此审计过程中重点关注这些功能模块。

找到对应的功能模块后,检索源码中对反序列化函数的调用来静态寻找反序列化的输入点,如以下函数:

ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject

确定了反序列化输入点后,查看应用的Class Path中是否包含Apache Commons Collections等危险库(ysoserial所支持的其他库亦可),若不包含危险库,则查看一些涉及命令、代码执行的代码区域,防止程序员代码不严谨,导致bug。若包含危险库,则使用ysoserial进行攻击。
0x07 java中的API实现
位置:Java.io.ObjectOutputStreamjava.io.ObjectInputStream

序列化:ObjectOutputStream类->writeobject()

注:该方法对参数指定的obj对象进行序列化,把子节序列写到一个目标输出流中,按java的标准约定是给文件一个.ser扩展名

反序列化:objectInputStream类->readobject()

注:该方法从一个源输入流中读取子节序列,再把它们反序列化为一个对象,并将其返回。

0x08 Serializable和Externalizable接口类的对象序列化

实现Serializable和Externalizable接口的类的对象才能被序列化。

Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式。

对象序列化包括如下步骤:

1、创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;

2、通过对象输出流的writeObject()方法写对象。

对象反序列化的如下步骤:

1、创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;

2、通过对象输入流的readObject()方法读取对象。

0x09 Apache Commons Collections

9.1 简介

Apache Commons Collections是一个扩展了java标准库里的Collection结构的第三方基础库。它包含有很多jar工具包。它提供了很多强有力的数据结构并且实现了各种集合工具类。

org.apache.commons.collections提供一个类包来扩展和增加标准的java的collection框架,也就是说这些扩展也属于collection的基本概念,只是功能不同罢了。java中的collection可以理解为一组对象,collection里面的对象称为collection的对象。具象的collection为set、list、queue等等,它们是集合类型。换一种理解方式,collection是set、list、queue的抽象。

作为Apache开源项目的重要组件,commons collections被广泛应用于各种java应用的开发,而正是因为在大量web应用程序中这些类的实现以及方法的调用,导致了反序列化漏洞的普遍性和严重性。

9.2 项目地址

官网:    http://commons.apache.org/proper/commons-collections/ 
Github:  https://github.com/apache/commons-collections

9.3 POC构造

构造一个对象 ——> 反序列化 ——> 提交数据

Map类->TransformedMap

Map类是存储键值对的数据结构。Apache Commons Collections中实现了TransformedMap,该类可以在一个元素被添加/删除/或是被修改时(即key或value:集合中的数据存储形式即是一个索引对应一个值,就像身份证与人的关系那样),会调用transform方法自动进行特定的修饰变换,具体的变化逻辑由Transformer类定义。也就是说,TransformedMap类中的数据发生改变时,可以自动对进行一些特殊的变化,比如在数据被修改时,把它改回来;或者在数据改变时,进行一些提前设定好的操作。至于会进行怎么样的操作或变换,这是由用户提前设定的,这个叫做transform。通过TransformedMap.decorate()方法获得一个TransformedMap:


TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。

    第一个参数为待转化的Map对象
    第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
    第三个参数为Map对象内的value要经过的转化方法

Transformer接口

Defines a functor interface implemented by classes that transform one object into another.
作用:接口于Transformer的类都具备把一个对象转化为另一个对象的功能
1)构造一个Map和一个能够执行代码的ChainedTransformer,
2)生成一个TransformedMap实例
3)利用MapEntry的setValue()函数对TransformedMap中的键值进行修改
4)触发我们构造的之前构造的链式Transforme(即ChainedTransformer)进行自动转换

AnnotationInnvocationHandler类


这个类有一个成员变量memberValues是Map类型 
AnnotationInvocationHandler的readObject()函数中对memberValues的每一项调用了setValue()函数对value值进行一些变换。

1)首先构造一个Map和一个能够执行代码的ChainedTransformer,
2)生成一个TransformedMap实例
3)实例化AnnotationInvocationHandler,并对其进行序列化,
4)当触发readObject()反序列化的时候,就能实现命令执行。
  POC执行流程为 TransformedMap->AnnotationInvocationHandler.readObject()->setValue()- 漏洞成功触发

0x10 漏洞挖掘

1.漏洞触发场景
   在java编写的web应用与web服务器间java通常会发送大量的序列化对象例如以下场景:
  1)HTTP请求中的参数,cookies以及Parameters。
  2)RMI协议,被广泛使用的RMI协议完全基于序列化
  4)JMX 同样用于处理序列化对象
  5)自定义协议 用来接收与发送原始的java对象

2. 漏洞挖掘
  (1)确定反序列化输入点
    首先应找出readObject方法调用,在找到之后进行下一步的注入操作。一般可以通过以下方法进行查找:
      1)源码审计:寻找可以利用的“靶点”,即确定调用反序列化函数readObject的调用地点。
       2)对该应用进行网络行为抓包,寻找序列化数据,如wireshark,tcpdump等
     注:java序列化的数据一般会以标记(ac ed 00 05)开头,base64编码后的特征为rO0AB。
  (2)再考察应用的Class Path中是否包含Apache Commons Collections库
  (3)生成反序列化的payload
  (4)提交我们的payload数据

0x11 危害

  • 执行逻辑控制(例如:变量修改、登录绕过)

  • 代码执行

  • 命令执行

  • 拒绝服务

  • 远程代码注入

0x12 防御

认证和签名

通过认证,来避免应用接受攻击者的异常输入。要知道,很多序列化和反序列化的服务并不是提供给用户的,而是提供给服务自身的。比如,存储一个对象到硬盘、发送一个对象到另一个服务中去。对于这些服务,通过加入签名的方式来进行防护。比如,对存储的数据进行签名,以此对调用来源进行身份校验。只要攻击者获取不到密钥信息,它就无法向进行反序列化的服务接口发送数据,也就无从发起反序列化攻击了。

限制序列化和反序列化的类

在反序列化漏洞中,攻击者需要构建调用链,而调用链是基于类的默认方法来构造的。然而,大部分类的默认方法逻辑很少,无法串联成完整调用链。因此,在调用链中通常会涉及非常规的类。因此通过构建黑名单的方式,来检测反序列化过程中调用链的异常。

在Fastjson的配置文件文件中,就维护了一个黑名单的列表,其中包括了很多可能执行代码的方法类。这些类都是平常会使用,但不会序列化的一些工具类,因此可以将它们纳入到黑名单中,不允许应用反序列化这些类(在最新的版本中,已经更改为hashcode的形式)。

RASP检测

RASP(Runtime Application Self-Protection,实时程序自我保护),RASP通过hook等方式,在这些关键函数的调用中,增加一道规则的检测。这个规则会判断应用示范执行了非应用本身的逻辑,能够在不修改代码的情况下对反序列化漏洞攻击实现拦截。

0x13 总结

如有错误请各位师傅指正。

0x14 了解更多安全知识

欢迎关注我们的安全公众号,学习更多安全知识!!!
欢迎关注我们的安全公众号,学习更多安全知识!!!
欢迎关注我们的安全公众号,学习更多安全知识!!!
基础知识|反序列化命令执行漏洞_第1张图片

你可能感兴趣的:(知识,java,开发语言,后端)