java远程代码注入_Java RMI远程反序列化任意类及远程代码执行解析(CVE-2017-3241 )...

原标题:Java RMI远程反序列化任意类及远程代码执行解析(CVE-2017-3241 )

本打算慢慢写出来的,但前几天发现国外有研究员发了一篇关于这个CVE的文章,他和我找到的地方很相似。然而不知道是不是Oracle认为是同一个漏洞然后合并了CVE,还是说我找错了CVE。

总之,先简单描述一下漏洞:对于任何一个以对象为参数的RMI接口,你都可以发一个自己构建的对象,迫使服务器端将这个对象按任何一个存在于class path中的可序列化类来反序列化。听起来可能有点绕,请往下看。

就直接上问题代码了。在Java RMI的sun.rmi.server.UnicastRef类中,有如下一段代码:

8de6f9f9f73c056a07a0c5aaa3f91751.png

看324行,如果你熟悉java反序列化漏洞,看到此你应该就可以激动了。该代码直接调用readObject,且在原生Java类里。结合2016 black hat上那个spring-tx.jar或者之前apache common中的类,都可以实现远程代码执行。spring-tx里的那个我实验成功了,且Spring rmi中继承了这个漏洞。但Spring team表示不修,和他们没关系。。。

其实写到这,很多技术大牛已经可以自己找出怎么黑了。下面只是简单写写我如何通过正常Java RMI程序来攻击的,因为我觉得这招还是比较淫荡的。

以下是一个正常的服务器端接口,接口参数为Message对象,Message对象是要被序列化的对象:

be119a750b4605a7b5adea4c3743eace.png

服务器端程序,sendMessage接口实现只是调用getMessage打印字符串:

9bfc6a1c30a966f95774244a09363a83.png

假设服务器端类路径里还存在一个PublicKnown类,比如spring或者apache common包里的某个类:)。这种类大部分情况下会被开发人员会一起打包进项目,但从来不用:

d8deb4fcc9a51ca1a5d74646ef54eddb.png

如上,该类自己实现了一个readObject方法,用来做XXX事情。。。

以下是正常的客户端代码:

58a6d0f2ccf40146778cccce5631a19a.png

输出我就不放了,就是打印个Hello。

好了,如何攻击呢?

首先在客户端程序里当然要有Message类,而Message类基本应该是公开已知的。然后,虽然Spring tx和Apache common都是开源的,但我们先假设攻击者不知道源代码,但知道PublicKnown的类名和包名,于是他在客户端里构建如下的一个类:

b830316dddf6fd08e3cac6134c99ffea.png

重点是包名,类名必须一致,且继承Message,serialVersionUID可以先不知道,之后能找出来。

然后改一改客户端程序:

0bf4a572fbc77f17222e920ee67c8412.png

服务器端端的输出如下(直接从报告里拷贝过来的截图):

也就是说,服务器端在接收到客户端发送的对象后会按PublicKnown类来反序列化,然后调用PublicKnown的readObject方法。

至此,如何配合Spring-tx.jar里的那个JtaTransactionManager类实现远程代码执行我想大家也知道了。把JtaTransactionManager源代码抄一份,让其继承Message类或者实现Message实现了的接口(如果有)就行。两种我都试验过可行。哦,对了,在JtaTransactionManager中你还需要控制userTransactionName变量的值,直接写在客户端代码里就行了,神奇的服务器端会用客户端提供的变量值和服务器端定义的readObject去执行。

还剩最后一个问题,serialVersionUID怎么得到?在我实验的时候,第一次发PublicKnown类过去的时候不要包含这个变量,服务器端会返回一个错误信息给你,错误信息里会带有这个值。。。。。。

且根据不同的错误信息,你还可以知道你的目标类是否存在于服务器的类路径里。

虽然Oracle已经发了补丁,但我打赌很多地方是不会升级JDK的。。。。

要是有类似于JtaTransactionManager这种可以配合使用的类,还请大家共享一下呀!

例子1:原理上面说了,补一张项目截图:

忽略里面和spring相关的包,那些是为了下面的例子在做准备。这个例子中的代码都是拷贝上面我贴的。你还可以在服务器端的PublicKnown中加个本地变量,并在readObject方法中输出,然后在客户端的PublicKnown中加个同样的变量,赋值,传到服务器端,你会看到变量值会在服务器端被输出出来。

上面也提到不知道服务器端的serialVersionUID,但服务器端会在出现任何异常的情况下把异常信息返回到客户端,如下:

例子2:利用JtaTransactionManager进行JNDI注入的例子:

返回到客户端的部分异常信息:

0e7d84e4fc8f7c2014eb9f2ef38a20d2.png

9e45c8deb6d6415c643345c5af058e81.png

客户端的JtaTransactionManager代码如下:

38d5d54bbbf916923f38188a08672900.png

Message有稍做修改:

27fd507dd51ef5b71ce77086e94e82f1.png

f0d34043a82a84f91019f59829198520.png

我以前的例子是在Spring RMI中测试的,做起来比这个顺利多了。这次是单独建Java项目测试。。。。

需要主意以下几点:

1,当你把假的JtaTransactionManager对象发到服务器端的时候,服务器端其实也要各种初始化,所以会依赖到各种Spring的包,还有一个Apapche common的logger以及jta包。所以服务器端不是单有个Spring-tx.jar就能成功攻击的,但Spring项目里这几个依赖包出现的几率比spring-tx.jar高得多。

2,客户端编译的时候似乎也依赖几个类,我直接把所有spring jar包都放进去了。

3,看到截图,有的小伙伴可能会质疑这个是客户端编译的错误。其实我刚运行出来的时候也这么质疑的。。。但这其实是服务器端发过来的异常信息。

首先,initUserTransactionAndTransactionManager是被调用了的.。这个方法只会是在readObject中被调用,客户端哪里有调用readObject?

其次,客户端JtaTransactionManager代码我是改过的,根本没有相关代码。

最后,客户端jar包里的JtaTransactionManager类我已经删了:

文章来源:FreeBuf

作者:jfeiyi返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值