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

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

        <dependency>
            <groupId>javax.servlet</groupId>
			<!--  这里需要将jstl设置为1.2 -->
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <scope>runtime</scope>
        </dependency>
		<dependency>
			<!--  添加利用链 -->
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-collections4</artifactId>
			<version>4.0</version>
		</dependency>

之后直接用idea打开

远程tomcat

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

kali2021
192.168.164.128

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

PermitRootLogin yes

或者

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

修改/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

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

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

漏洞分析

首先分析filter
在web.xml中

org.apache.shiro.web.servlet.ShiroFilter

会调用

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

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

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

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

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

shiro-web-1.2.4.jar!\org\apache\shiro\web\mgt\CookieRememberMeManager.class#getRememberedSerializedIdentity

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

public AbstractRememberMeManager() {
        this.setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
    }

在这里插入图片描述
流程就是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

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

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

会抛出异常
回到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;
    }
}

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

参考文章

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值