前言
调试shiro 1.2.4 remember反序列化漏洞。
实验环境
vulhub的CVE-2016-4437环境,springboot+shiro 1.2.4,修改docker-compose.yaml为以下配置,使docker支持远程调试。
1 | version: '2' |
将shirodemo-1.0-SNAPSHOT.jar解压,导入idea,项目结构图如下:
将BOOT-INF的lib导入为依赖,然后将classes文件夹加入dependences,使class文件中的断点生效。
最后需要注意的是开启远程调试时,用的jdk版本和docker中的jdk大版本一直就可。
idea远程调试配置如下
漏洞调试
Shiro-1.2.4主要的成因是硬编码秘钥和不受限制的反序列化,首先要找出反序列化点。
在shiro-core中的readObject方法下断点。然后登录后将获取到的rememberMe重发。
断在SimplePrincipalCollection类的294行,这里反序列化未做任何限制。
1
2
3
4
5
6
7private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
boolean principalsExist = in.readBoolean();
if (principalsExist) {
this.realmPrincipals = (Map<String, Set>) in.readObject();
}
}看下堆栈。
注意到getRememberedIdentity方法,应该是在这个方法中获取到rememberMe的值并解密。
回溯到getRememberedIdentity,convertBytesToPrincipals,在该方法中完成解密。
1
2
3
4
5
6protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
if (getCipherService() != null) {
bytes = decrypt(bytes);
}
return deserialize(bytes);
}在decrypt方法中的getDecryptionCipherKey获取秘钥
1
2
3public byte[] getDecryptionCipherKey() {
return decryptionCipherKey;
}发现AbstractRememberMeManager类的构造器初始化秘钥
1
2
3
4
5public AbstractRememberMeManager() {
this.serializer = new DefaultSerializer<PrincipalCollection>();
this.cipherService = new AesCipherService();
setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
}1
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
秘钥硬编码
最终在JcaCipherService中找到加解密的模式,AES/CBC/PKCS5Padding。
poc
ysoserial cc10链生成**.ser**
1 | from Crypto.Cipher import AES |
指定**.ser**文件和key可以直接生成加密序列,需要注意的是shiro的的aes加密需要把iv放在最前面。
修复方式
将AbstractRememberMeManager中的硬编码改为随机生成编码
1 | public AbstractRememberMeManager() { |
关于cc链打不通的应对
- spring boot+shiro的组合是可以打通cc链的
- shiro打cc链有时候可能不通的原因很有可能是shiro并没有真正依赖cc,而cc的依赖没有被加载到classpath中,这个时候就要考虑使用jrmp,jdk7u22或者jdk8u20这样的原生依赖链。