java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

寬位元組安全 發表於 2021-07-26

本文中xray高階版shiro payload來源於雷石安全實驗室公眾號釋出的shiroExploit.jar

感謝雷石安全實驗室,雷石安全實驗室牛逼

本文主要描述如何從shiro的payload中提取有效payload。該方法適合從各種java反序列化漏洞中提取payload

0x01 前言

某日小夥伴發來雷石安全實驗室的shiro利用工具,據稱payload提取自xray。利用如下
java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

0x02 反編譯雷石利用工具

首先拖入idea中反編譯,檢視如何利用payload。程式碼如下

java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

t是遍歷陣列的下標變數

不得不說,當我看到這人寫的程式碼時,心中一萬個草泥馬飄過。第一次看到這樣遍歷陣列的方式!!!!再看一下變數命名,濃郁地鄉村非主流風格。雷石安全實驗室寫的程式碼,自帶混淆,牛皮。

payload呢,原來是xray已經生成並加密的,一共430個。所以我們的問題變為如何從這430個已加密的payload中提取未加密的內容

0x03 shiro payload解密

根據shiro漏洞的原理,shiro的cookie通過如下方式加密, key為kPH+bIxk5D2deZiIxcaaaA==解碼後的內容

base64Encode(iv+aes(java serialize object))

而java反序列化後的資料,以aced開頭。於是我們可以使用shiro預設金鑰,批量解密上面的430個key。如果正常解密,且解密後的資料以正確的magic number開頭。則儲存檔案。程式碼如下

    public static String bytesToHex(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if(hex.length() < 2){
                sb.append(0);
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    public static byte[] decrypt(byte[] cipherTextBytes, byte[] pwdBytes, byte[] iv) {
        try {
// 1 獲取解密金鑰
            SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM);
// 2 獲取Cipher例項
            Cipher cipher = Cipher.getInstance(CIPHER_MODE);
// 檢視資料塊位數 預設為16(byte) * 8 =128 bit
// System.out.println("資料塊位數(byte):" + cipher.getBlockSize());
// 3 初始化Cipher例項。設定執行模式以及加密金鑰
            IvParameterSpec iv1 = new IvParameterSpec(iv);//使用CBC模式,需要一個向量iv,可增加加密演算法的強度
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv1);
// 4 執行
            byte[] clearTextBytes = cipher.doFinal(cipherTextBytes);
// 5 返回明文字符集
            return clearTextBytes;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
// 解密錯誤 返回null
        return null;
    }

    public static void main(String... args) throws IOException {
        for(int i = 0; i< xray.cookie.length; i++){
            String payload = xray.payloads(i);
            System.out.println(payload);
            byte[] rawpayloadBytes = Base64.decode(payload);
            byte[] key = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
            int ivSize = key.length;
            byte[] iv = new byte[ivSize];
            System.arraycopy(rawpayloadBytes, 0, iv, 0, ivSize);
            byte[] payloadBytes = new byte[rawpayloadBytes.length - ivSize];
            System.arraycopy(rawpayloadBytes, ivSize, payloadBytes, 0, payloadBytes.length);
                byte[] result = EncryptUtil.decrypt(payloadBytes, key, iv);
                if (result != null){
                    String hexResult = bytesToHex(result);
                    if (hexResult.startsWith("aced")) {
                        System.out.println(payload);
                        new FileOutputStream(new File("res.ser")).write(result);
                    }
                }
        }
    }

結果通過010editor檢視如下
java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

0x04 檢視java反序列化資料

通過SerializationDumper工具即可檢視
java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

shiro中通過反序列化漏洞回顯,需要執行自定義的java程式碼。檢視ysoserial程式碼,執行自定義程式碼,需要通過com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的方式,編寫一個繼承自該類的class,讀取位元組碼並設定_bytecodes,程式碼如下
java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

所以我們的重點在於如何提取bytecode陣列中的內容。而serializationDumper預設只列印陣列中的值。
java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

檢視一下serializationDumper工具是如何讀取array陣列的,程式碼如下
java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

首先讀取array的size,然後調(byte)cd.getClassName().charAt(1)去讀取array的型別,再呼叫readBytesField方法讀取每一項具體的值,程式碼如下

	/*******************
	 * Read a byte field.
	 ******************/
	private void readByteField() {
		byte b1 = this._data.pop();
		if(((int)b1) >= 0x20 && ((int)b1) <= 0x7e) {
			//Print with ASCII
			this.print("(byte)" + b1 + " (ASCII: " + ((char)b1) + ") - 0x" + this.byteToHex(b1));
		} else {
			//Just print byte value
			this.print("(byte)" + b1 + " - 0x" + this.byteToHex(b1));
		}
	}

所以只需要魔改一下讀取陣列的地方即可,並寫入檔案。

java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

0x05 利用

已經整合到shiro綜合利用工具中,可以選擇gadget以及生成方式,不過目前只整合16個key。

java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程
java反序列化提取payload之xray 高階版的shiro回顯poc的提取過程

參考

  1. https://mp.weixin.qq.com/s/fZhL-GjoXLPqAIKBKCC_fg