• Shiro-550 漏洞分析


    Shiro 550 漏洞学习

    漏洞原理

    在 Shiro <= 1.2.4 中,AES 加密算法的key是硬编码在源码中,当我们勾选remember me 的时候 shiro 会将我们的 cookie 信息序列化并且加密存储在 Cookie 的 rememberMe字段中,这样在下次请求时会读取 Cookie 中的 rememberMe字段并且进行解密然后反序列化
    由于 AES 加密是对称式加密(Key 既能加密数据也能解密数据),所以当我们知道了我们的 AES key 之后我们能够伪造任意的 rememberMe 从而触发反序列化漏洞

    环境搭建

    本地环境

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

    下载好以后直接解压
    然后进入samples/web目录,直接修改pom文件

            
                javax.servlet
    			
                jstl
                1.2
                runtime
            
    		
    			
    			org.apache.commons
    			commons-collections4
    			4.0
    		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    之后直接用idea打开

    远程tomcat

    环境
    手动安装哪里有apt instal 香

    kali2021
    192.168.164.128
    
    • 1
    • 2

    修改ssh配置,允许远程连接/etc/ssh/sshd_config,添加

    PermitRootLogin yes
    
    • 1

    或者

    sed -i 's/#PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
    
    
    service ssh start
    apt-get install tomcat9 tomcat9-docs tomcat9-examples tomcat9-admin
    #whereis tomcat9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    修改/usr/share/tomcat9/bin/catalina.sh添加如下代码

    CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.164.128 -agentlib:jdwp=transport=dt_socket,address=0.0.0.0:60222,suspend=n,server=y"
    
    
    service tomcat9 start
    
    • 1
    • 2
    • 3
    • 4

    idea

    新建tomcat server remote
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    你以为这样就完事了,调试还需要本地的tomcat,是不是感觉远程了个寂寞?

    本地tomcat

    https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.46/bin/apache-tomcat-9.0.46-windows-x64.zip
    
    • 1

    解压在idea里面配置即可
    在这里插入图片描述

    漏洞分析

    首先分析filter
    在web.xml中

    org.apache.shiro.web.servlet.ShiroFilter
    
    • 1

    会调用

    org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter
    
    • 1

    在这里插入图片描述
    之后调用doFilterInternal
    初始化进行处理
    createSubject
    获取上下文信息
    在这里插入图片描述

    这个漏洞的主要成因是加密密钥采用硬编码,所以可以通过定位硬编码找到处理流程
    .m2 epositoryorgapacheshiroshiro-core.2.4shiro-core-1.2.4.jar!orgapacheshiromgtAbstractRememberMeManager.class

    private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
    
    • 1

    首先会在 AbstractRememberMeManager#getRememberedPrincipals 中将上下文中获取数据传入getRememberedSerializedIdentity 函数中,
    在这里插入图片描述
    跟入getRememberedSerializedIdentity

    shiro-web-1.2.4.jar!orgapacheshirowebmgtCookieRememberMeManager.class#getRememberedSerializedIdentity
    
    • 1

    在这里插入图片描述
    在SimpleCookie#readValue
    在这里插入图片描述
    获得rememberme,调用ensurePadding补全base64,之后进行base64解码,返回数组
    跟入AbstractRememberMeManager#convertBytesToPrincipal
    发现调用decrypt,继续跟入
    在这里插入图片描述
    跟入getDecryptionCipherKey
    在这里插入图片描述
    返回了aes key的数组,看下哪里设置的,
    在setDecryptionCipherKey处
    在这里插入图片描述
    在setCipherKey中传入的
    搜索之后发现,在构造函数中进行调用

    public AbstractRememberMeManager() {
            this.setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
        }
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    流程就是AbstractRememberMeManager构造函数进行设置,setCipherKey 中调用了setDecryptionCipherKey设置了decryptionCipherKey属性getDecryptionCipherKey 直接返回了该数组

    跟入decrypt
    在这里插入图片描述
    这里要重点看下,因为是取了硬编码的长度来截取base64解密的前16位,之后使用16位之后的进行aes解密
    之后调用decrypt进行解密
    在这里插入图片描述
    跟入crypt
    在这里插入图片描述
    调用crypt,使用doFinal进行解密,返回解密结果
    在AbstractRememberMeManager#decrypt 中赋值给bytesource传入serialized数组
    返回AbstractRememberMeManager#convertBytesToPrincipals
    在这里插入图片描述
    调用deserialize进行反序列化操作,跟入
    在这里插入图片描述
    进入DefaultSerializer#deserialize 对数据进行反序列化
    在这里插入图片描述
    调用readobject

    shiro检测

    判断框架

    传入rememberme参数

    rememberMe=1
    
    • 1

    跟入JcaCipherService#decrypt
    在这里插入图片描述
    因为ciphertext的长度为0
    所以在

    System.arraycopy(ciphertext, 0, iv, 0, ivByteSize);
    
    • 1

    会抛出异常
    回到AbstractRememberMeManager#getRememberedPrincipals
    在这里插入图片描述
    跟入
    在这里插入图片描述
    调用forgetIdentity 跟入
    在这里插入图片描述
    调用forgetIdentity
    在这里插入图片描述
    跟入removeFrom
    在这里插入图片描述
    抛出异常最终导致输出deleteMe

    密钥错误

    在上面的抛出异常下面调用decrypt
    在这里插入图片描述
    跟入decrypt
    在这里插入图片描述
    跟入crypt
    在这里插入图片描述
    就是在doFinal的位置
    因为解密异常,所以会抛出异常,也就又回到了AbstractRememberMeManager#getRememberedPrincipals
    返回deleteMe

    密钥正确

    前面的报错都不会产生,在经过反序列化之后,把返回的类转换成PrincipalCollection类型

    构造

    前面已经分析了解密流程
    所以构造的流程就是

    1.获取到 反序列化的数据
    2.设置AES加密模式,使用AES.MODE_CBC的分块模式
    3.设置硬编码的 key
    4.使用随机数生成 16 字节的 iv
    5.使用 iv + AES加密(反序列化数据) 拼接
    6.最后base64加密全部内容
    
    
    import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
    import org.apache.shiro.crypto.AesCipherService;
    import org.apache.shiro.subject.SimplePrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import java.io.*;
    
    
    public class main {
        public static void main(String[] args) throws Exception {
            SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection();
            ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream("detect.ser"));
            obj.writeObject(simplePrincipalCollection);
            obj.close();
            String path = "detect.ser";
            byte[] key = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
            AesCipherService aes = new AesCipherService();
            ByteSource ciphertext = aes.encrypt(getBytes(path), key);
            System.out.printf(ciphertext.toString());
        }
    
    
        public static byte[] getBytes(String path) throws Exception{
            InputStream inputStream = new FileInputStream(path);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int n = 0;
            while ((n=inputStream.read())!=-1){
                byteArrayOutputStream.write(n);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            return bytes;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    key正确
    在这里插入图片描述
    key错误
    在这里插入图片描述

    参考文章

    https://www.yuque.com/tianxiadamutou/zcfd4v/op3c7v#aad3715c
    https://blog.csdn.net/god_zzZ/article/details/108391075

  • 相关阅读:
    nginx配置netty
    PreScan快速入门到精通第二十二讲驾驶员模型之路径跟踪
    野火FPGA入门(3):简单组合逻辑
    python就是学不会怎么办?
    yolo系列面试题
    无锡矽杰微电子----XC8P9520MCU
    2023湖北民族大学计算机考研信息汇总
    基于HTML+CSS+JavaScript角色后台管理系统设计毕业论文源码
    Kubernetes学习笔记-kubernetes API服务器的安全防护(1)通过基于角色的权限控制加强集群安全20220814
    elementui table 合并列
  • 原文地址:https://blog.csdn.net/geejkse_seff/article/details/126496207