0x01 https://github.com/wchen-r7/metasploit-framework/blob/791ebdb679d53f6363cf88a54db57722719cd62a/external/source/exploits/CVE-2012-0507/Exploit.java
这里就是java的cve 2012 0507的poc所在了,大部分网上流传的基本上都基于这里,而且大家要注意这个poc的攻击模块并没有在其中写出来,所以直接运行是没有结果的。
0x02 这个poc之所以出名估计和inbreak的七次打新浪云关系很大,当然这个poc也引发了一波网上的java挂马热潮。其实在一般应用中,大部分人都意识不到java存在一个Security Manager 的保护机制,在实际使用中,我们无论在写jsp还是java的本地软件时,很少有人会去配置这个Security Manager,(ps:我反正没见过….),这个保护机制的原理其实很简单,搞java的都知道java以前搞过一个applet的东西,虽然现在已经没落了,但是它里面和这个保护机制关系就很大,试想下,applet是运行在客户的浏览器上的,如果applet里面实现了大量的敏感功能,岂不是用户的信息都被偷完了么,当然sun公司也想到了这点,所以Security Manager就正好可以对抗你,说白了Security Manager就是检查一个权限的白名单(java.policy),它规定了你这个java程序,只能在我白名单限制的权限内运行。而applet默认就存在这样一个非常严格权限的policy,这样applet程序就不要想在用户的机子上去调用exec之类的敏感函数了。
0x03 当然除了applet中用到了这个功能外,新浪云也用到了这个安全机制,很简单的原因他要保证他的java云只有适当的权限,最主要的是每个用户间要保持隔离,这里使用Security Manager将会是非常合适的技术(ps:虽然用的不好,被inbreak各种攻破…)
0x04具体的Security Manager,网上很多,我就针对这个poc说下吧,首先测试这个poc,就需要和平时不太一样的方式,项目导入到eclipse后,运行时,需要开启Security Manager这个功能才可以,方法如下
VM arguments中就是开启命令,其中policy.txt即权限文件,放到你的main函数所在类的目录下面,而最后的msf.x.Test则是Main函数所在类的全路径,同样注意修改working directory。
0x05 这段poc中的java代码都非常好懂,但是在开始处通过一个
As数组来生成了一个关键的object数组,从而带来了后面的一连串关键操作,那么这段字符串是什么,为什么要这样写,就成为了很多人的疑问。
0x06 首先解释下这段字符串通过objectinputstream转化成了一个object数组具体如下
<!--[endif]-->
可以看到这个数组只有两个元素
Object[0]=Help[1] 是一个只有一个元素的Help类型数组,我们记作a
Object[1]= AtomicReferenceArray,而AtomicReferenceArray的array则指向了Help 数组a
这里大家肯定会有疑问为什么要这样设计,而不能我去用代码创建这么个数组,看着也不难创建么。
这段代码看似好像也没问题,但是我们在运行后会发现一个大问题
<!--[endif]-->
和前面的poc产生的数组一对照发现AtomicReferenceArray中的array指向不一样了,这里实际上就是关键所在了,在后面的poc中的关键方法里面必须要求AtomicReferenceArray中的array必须指向Help数组才可以实现成功的越权。
0x07 那么通过查看AtomicReferenceArray的源代码我们可以发现
<!--[endif]-->
在AtomicReferenceArray中array是一个private的类型,
<!--[endif]-->
而且通过AtomicReferenceArray初始方法初始时,实际上上是新建了了一个object数组,这样与要求不符。所以我们要想指向Help数组,就需要使用反射机制才可以
<!--[endif]-->
这样子才可以成功的创建出
<!--[endif]-->
这样的结构,但是因为创建这样的数组会用到反射机制去修改私有变量,那么必然在secure manager的禁止范围内,所以必须在正常情况下通过objectOutputStream写入到文件里面去,然后在低权限情况下利用objectinputstream来读取创建出来,规避掉反射的问题。
0x08 这下这段字符串的来历就很清楚了,下来就是最关键的几行代码了,他巧妙的实现了偷天换日
<!--[endif]-->
这里的大部分代码加上上面对arrayOfObject的解释就很好理解了,这里需要注意的一点是,Help类的定义必须是public class Help extends ClassLoader implements Serializable
这里的Serializable是用来进行序列化的,实质就是保证了他可以object read 和write,只有这样才能把Help数组直接输出成字符串,并从字符串把它直接读回到object状态。
而ClassLoader则是实现越权的关键。
<!--[endif]-->
这个方法就是ClassLoader实现越权的关键,他可以重定义一个类的权限,但是这个方法最致命的地方在于它是protected的,这意味着他只能被同一个包下面的类调用或者它的子类调用。
而ClassLoader最大的问题是,在严格限制的低权限环境中肯定是不允许你创建他的子类的实例的,而通过getClass.getClassLoader则虽然可以得到当前类的ClassLoader,但是由于这个一般都是系统类,和我们不在同一个包下面,无法去调用这个protected的defineClass方法。
这个poc的解决就在于下面
这里最关键的就是localAtomicReferenceArray.set(0, localClassLoader);,这句代码的执行完,
<!--[endif]-->
结构变成了这个样子,可以看到出现了一个实际上不可能和不应该出现的情况,
Help[0]实际指向了Launcher$AppClassLoader,理论上绝对不应该相等的两个类。这两个类唯一的共同点就是有一个共同的父类ClassLoader。
0x09 深入分析localAtomicReferenceArray.set(0, localClassLoader),
<!--[endif]-->
这里调用了个import sun.misc.Unsafe;光从名字来看,估计就可以猜到这个方法不太安全,实际上这里的赋值可能并没有去检查类型,就直接赋值了,从而导致了上面说的不合理现象的发生。当然java本身这样写的原因我估计是他觉得这个array是个object,不会出现类型不匹配的情况,Object类型是java中所有类的父类么,但是这里联系到0x06中所说的巧妙利用反射修改了这个array的实际指向,从而导致了这个赋值的错误。
0x10 分析到这里,其实核心就已经完了,得到了一个可以自由使用的ClassLoader则意味着可以随意调用defineClass方法,到这里这个poc的精华差不多了。利用这个Help[0],去defineClass,在这里系统会认为Help[0]是一个实际存在的Help对象,那么他的defineClass方法调用就是合法的,而实际上Help[0]实质是Launcher$AppClassLoader,实际调用的是它的defineClass
<!--[endif]-->
0x11 剩下的代码没什么好说的了。。。。那么我们通过这个poc可以发现一个java的体系漏洞,就是去找找java代码中调用了unsafe方法的地方,只要我们可以控制这个unsafe的参数,那么类似的漏洞还是有可能找到的