基于Java Exploit在漏洞研究领域越来越重要的地位,在下本着抛砖引玉的态度,写下了这篇文章,希望对大家了解Java Exploit能有一定帮助。

2.  网页中的Java

目前主要有两种方式在网页中启动Java代码: Applet和Java Web Launch。其中Java Web Launch是Java 1.5之后新加入的,而Applet则是早就存在。
记得当年学校教Java的时候,最后大作业就是写一个功能复杂的Applet。不过现在Applet已经不流行了

在Html里加入如下代码就可以嵌入Applet:

代码:
<APPLET CODE=”HelloWorldApplet.class” WIDTH=200 HEIGHT=100> </APPLET>

或者如果打包成jar的话:

代码:
<APPLET archive=”HelloWorldApplet.jar”  CODE=”HelloWorldApplet.class”   WIDTH=200 HEIGHT=100> </APPLET>
和Applet相对的,Java Web Launch用来从web上启动Java Application。需要遵循Java Network Launch Protocol (jnlp)。


3.安全性和Sandbox

看到这里,大家可能会想,既然我们可以随意地在html中调用Java小程序,而Java语言的功能又非常强大,那直接写个包含恶意代码的Java小程序放到网上,不就相当于挂马了吗?
比如,你可能会想写下如下代码:

代码:
import java.awt.*;import java.applet.*;import java.io.*;public class HelloWorldApplet extends Applet {    public void init()  {    try {      Runtime.getRuntime().exec(“calc.exe”);    } catch (IOException e) {      e.printStackTrace();    }  }    public void paint(Graphics g )  {    g.drawString(“Hello World!”,5,35);  }}
然后在自己的网页中加入如下代码调用这个applet:

代码:
<APPLET CODE=”HelloWorldApplet.class” WIDTH=200 HEIGHT=100> </APPLET>

接着把网页挂到某个服务器上,开始守株待兔,

呃,出错了,我们得到了一个AccessControlException,提示没有权限。

其实Java的设计者早就考虑了安全问题,并提出了Sandbox的概念。
简单来讲这个sandbox的意思就是:Java虚拟机在执行所有系统资源相关的操作(读写文件,运行命令,网络通信。。。)时,都会检查当前代码是否有权限来进行这些操作,如果没有权限,就会抛出异常。
整个Sandbox机制非常复杂,无法用很短的篇幅讲清楚,下面只介绍一些要点:

1.  在Java虚拟机中运行的代码,有受信任(Trusted Code)代码和不被信任代码(Untrusted Code)之分。
默认情况下,Java自带的库中的代码都是受信任的代码,而来自其他地方(比如来自网络)的代码是不受信任的。受信任代码默认可以对任何系统资源进行操作而不受限制,而不受信任的代码权限很低。
比如我们前面这个例子,由于HelloWorldApplet.class的代码来自于网络上,因此它是不受信任的代码。
于是在试图创建进程(ProcessBuilder.start()函数)时,Java虚拟机检查到当前的代码不受信任,于是抛出一个权限异常。
我们可以通过定义一些手段来让自己的代码受信任(比如添加Policy,代码签名等等)。

2.  在Java虚拟机进行权限检查时,会检查整个调用栈上的代码,而不是只检查当前函数(这里会有一些例外,如doPrivilaged和AccessControlContext,暂时可以忽略之)。
整个调用栈上只要有任何一个调用来自不受信任的代码,就判定为没有权限。

还是上面这个例子,当最终检查权限时,调用栈如下:

代码:
Java.AccessControlContext.checkPermission                   (信任代码)Java.AccessController.checkPermission                               (信任代码)Java.SecurityManager.checkPermission                                (信任代码)Java.SecurityManager.checkExec                                                (信任代码)Java.lang.ProcessBuilder.start                                                    (信任代码)Java.lang.Runtime.exec                                                               (信任代码)Java.lang.Runtime.exec                                                              (信任代码)Java.lang.Runtime.exec                                                              (信任代码)HelloWorldApplet.init                                         (非信任代码)sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run   (信任代码)Java.lang.Thread.Run                                                                  (信任代码)

用于我们自己的HelloWorldApplet代码是不受信任的,于是整个检查失败,异常被抛出。

3.  权限检查代码是穿插在相关的Java API里面的,还是我们上面的例子, Runtime.exec调用了ProcessBuilder.start,代码如下:
代码:
public Process start() throws IOException {        // Must convert to array first — a malicious user-supplied        // list might try to circumvent the security check.        String[] cmdarray = command.toArray(new String[command.size()]);        cmdarray = cmdarray.clone();        for (String arg : cmdarray)            if (arg == null)                throw new NullPointerException();        // Throws IndexOutOfBoundsException if command is empty        String prog = cmdarray[0];       SecurityManager security = System.getSecurityManager();        if (security != null)            security.checkExec(prog);        String dir = directory == null ? null : directory.toString();        try {            return ProcessImpl.start(cmdarray,                                     environment,                                     dir,                                     redirects,                                     redirectErrorStream);        } catch (IOException e) {            // It’s much easier for us to create a high-quality error            // message than the low-level C code which found the problem.            throw new IOException(                “Cannot run program \”” + prog + “\””                + (dir == null ? “” : ” (in directory \”” + dir + “\”)”)                + “: ” + e.getMessage(),                e);        }    }}

注意里面的SecurityManager.checkExec就是权限检查代码了。
SecurityManager是Java安全机制的核心,一个运行中的Java Virtual Machine可以有SecurityManager,也可以没有,但是一旦设置了SecurityManager就不能再更改。
如果没有SecurityManager,很多权限检查都不会发生。如果在本地运行一个Java程序,默认是没有SecurityManager的,
但是如果是通过浏览器启动一个Applet,那相应的浏览器是一定会设置一个SecurityManager。

4.几种类型的Java Exploit

通过前面的介绍,我们知道由于Sandbox机制的保护,正常情况下是不能用Java程序挂马做坏事的,于是Java Exploit要做的事情就很明显了:突破Java Sandbox的保护机制。

设想一下我们现在被关在一个封闭的房子里,想要逃出去,那么我们可能可以有两种思路:
1.直接把墙给砸了。 2.找找看房子里面有没有没关严实的门窗,或者地道什么的。

在已有的Java漏洞中,第一种方法对应于那些针对Java虚拟机实现(主要是包括运行库)和插件进行exploit的漏洞,典型的有CVE-2009-3867,CVE-2009-3869,CVE-2010-3552,CVE-2010-0886等。
这类漏洞的主要思想是:Java程序虽然运行在虚拟机中,但是整个虚拟机(包括运行时库)的实现需要平台相关的本地代码来支撑(在windows上,就有诸如awt.dll,java.dll等本地代码)。

如果这些代码中存在漏洞,并且可以通过java代码来触发,我们就可以利用这些漏洞来运行shellcode,此时Sandbox机制就无能为力了(因为Sandbox针对的是Java代码)。

我们来看一个例子,CVE-2009-3867。这是一个栈溢出漏洞,存在于Java MidiSystem类的getSoundbank函数中
我们可以通过传一个超长的URL来触发这个漏洞,请看代码:

代码:
String str1 = repeat(‘/’, 30200);MidiSystem.getSoundbank(new URL(str1));

Java运行库中的一个strcpy操作引发了这个漏洞:

非常典型的栈溢出,大家可以自己调试一下。
值得注意的是如果通过IE浏览访问exploit并调试,IE在隔了一段时间得不到响应后会终止Java虚拟机,可以通过在IE的Terminate Process上下断来防止Java被关闭。


第二种突破Java Sandbox的方法是“绕”:

用Sandbox来保障安全的想法是非常好的,但是人非圣贤,孰能无过,真正到了代码实现的时候,开发Java的大牛们还是偶尔会出一些小差错,导致在某些情况下Sandbox机制可以被绕过。

典型的有CVE-2010-0840  和前几天的CVE-2011-3554。

我个人感觉比起第一类“暴力攻击Java虚拟机”的漏洞,这类漏洞的危害更大一点。
因为攻击者不需要费劲心思考虑如何让自己的exploit变得稳定,不需要考虑烦人的DEP和ASLR,只要写一段做坏事的Java代码就好了。

下面看一下CVE-2010-0840:
大牛的blog已经讲的非常详细了:

http://slightlyrandombrokenthoughts….-cve-2010.html

我这边总结一下,这个漏洞的核心思想如下:

前面提到过,当Java  Sandbox权限检查时,会检查整个栈上的代码,只要有任何非信任的代码,检查就失败。
而CVE-2010-0840通过构造一个表达式(Expression),该表达式将执行setSecurityManager(null)来关闭Sandbox的安全机制。
调用setSecurityManager将触发权限检查,因此如果在我们自己的代码中直接执行这个表达式是没有权限的。
但是通过将这个表达式加入一个JList容器,让Applet的UI线程来执行这个表达式,则可以做到权限检查时,整个调用栈上都是Java自己的受信任库代码。于是成功绕过的Java Sandbox。

5.总结

终于写完了,希望本文能帮助大家了解一些Java安全的相关知识