shiro反序列化免疫方案

一、本文基於 oneblog 爲基礎作爲方案演示,詳情請見 jdk反序列化安全防護研究:續 文中的環境

二、本文的目的是解答一個哥們兒的問題:能不能做到只對shiro進行反序列化限制 ,尤其是 java.util.PriorityQueue 確實不敢進行全侷限制

三、本文代碼演示了 黑名單+白名單 的一個思路,shiro場景複雜情況下請自行調整,勿噴

 

通過學習研究大佬的一系列shiro的文章,我們在 org/apache/shiro/io/ClassResolvingObjectInputStream 中可以找到如下代碼

圖中存在 java.util.PriorityQueue 是因爲我截取的是我在idea中的調試模式,用反序列化payload進行彈計算機的情況

如果我們重寫對應的代碼,其實就可以做到 白名單+黑名單 的安全方案了。

編碼開始:

1. oneblog使用的是springboot,我們可以使用一個很簡答的操作就能改寫原始邏輯:沖掉shiro的原始類

如上圖所示,oneblog使用shiro的rememberMe功能存在於 blog-admin 之中

    1.1 我們就直接創建一個同名爲 org.apache.shiro.io 的package,並在其中創建同名的 ClassResolvingObjectInputStream 類

    1.2 我們將idea反編譯後的 ClassResolvingObjectInputStream 類代碼直接覆蓋到該自建類之中

    1.3 我們來驗證下,我們自建的類是否沖掉了原始的邏輯

    

2. 如圖所示,我們在該類中的兩個方法上都是打上斷點,然後開始調試

3. 

4. 如下圖所示,我們新建的類中 osc.getName() 可以獲取到具體的類名

同時爲了調試,我們刪掉 JSESSIONID 只保留rememberMe 時,也會觸發 rememberMe 的反序列化功能

這樣我們可以得到一次正常反序列化所需要的類

org.apache.shiro.subject.SimplePrincipalCollection
java.util.LinkedHashMap
java.util.HashMap
java.util.LinkedHashSet
java.util.HashSet
java.lang.Long
java.lang.Number

5. 所以我們現在將自己創建的 ClassResolvingObjectInputStream 類文件進行如下修改

package org.apache.shiro.io;

import org.apache.shiro.util.ClassUtils;
import org.apache.shiro.util.UnknownClassException;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;

public class ClassResolvingObjectInputStream extends ObjectInputStream {
    public ClassResolvingObjectInputStream(InputStream inputStream) throws IOException {
        super(inputStream);
    }

    protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
        try {
            String s = osc.getName();
            // 幹掉常見的gadget,爲了避免 [ ; 符號,必須使用contains方法
            // 簡單的使用 s.equals 可能導致fastjson 以前出現的黑名單逃逸問題
            if (s.contains("java.util.PriorityQueue") || s.contains("xsltc.trax.TemplatesImpl")) {
                throw new ClassNotFoundException("Unable to load Dangerous ObjectStreamClass [" + osc + "]");
            }
            if (s.contains("org.apache.")) {
                // 直接幹掉了 org.apache ,但是要保留shiro自己
                if (s.startsWith("org.apache.shiro.subject.")) {
                    return ClassUtils.forName(s);
                }
                throw new ClassNotFoundException("Unable to load Dangerous ObjectStreamClass [" + osc + "]");
            }
            // 使用白名單保證業務的正常開展
            if (s.startsWith("java.lang") || s.startsWith("java.util")) {
                return ClassUtils.forName(s);
            } else {
                throw new ClassNotFoundException("Unable to load Dangerous ObjectStreamClass [" + osc + "]");
            }
        } catch (UnknownClassException var3) {
            throw new ClassNotFoundException("Unable to load ObjectStreamClass [" + osc + "]: ", var3);
        }
    }
}

 

所以,現在執行時反序列化中rce時,由於觸發了安全規則會直接導致異常,達到斷鏈的目的,請見下圖

同時,通過測試,正常的 rememberMe 功能不受影響。

 

總結:

一、我們在使用shiro時必須嚴格控制 cookieRememberMeManager 的密鑰,建議採用256或512位的長度,以保證爆破難度

二、聯繫開發人員,對shiro底層類進行改寫操作,保證就算得到了key也難以反序列化rce

 

謝謝

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章