参考文章

shiro-550 介绍

shiro-550(shiro小于1.2.5)主要是由shiro的rememberMe内容反序列化导致的命令执行漏洞

造成的原因是AES密钥被硬编码在shiro源码中, 这就导致了可以通过在cookie的rememberMe字段插入payload实现任意代码执行

在这以后shiro使用随机密钥, 而不再硬编码, 这又造成padding oracle attack导致的shiro-721

特征

返回包中包含rememberMe=deleteMe字段

请求包中存在rememberMe=remember-me, 返回包中存在rememberMe=deleteMe字段以及一段很长的rememberMe=…, 可以通过base64解码

当客户端再次请求服务端时,都会带上这个服务端第一次返回设置的Set-Cookie里面的rememberMe的密文,让服务端进行身份验证

动调

加密

断点位置为AbstractShiroFilter.class的doFilterInternal()

尝试登录后进入onSuccessfulLogin()继续步入rememberMeSuccessfulLogin()函数

RememberMeManager参数不为空,步入AbstractRememberMeManager的onSuccessfulLogin(), 随后到达convertPrincipalsToBytes()函数

发现首先将进行序列化,之后由this.getCipherService()获取到加密服务方式不为空后,进入this.encrypt(bytes)阶段

encrypt()函数的CipherKey就是AbstractRememberMeManager.class开头的

DEFAULT_CIPHER_KEY_BYTES="kPH+bIxk5D2deZiIxcaaaA=="

获取CipherKey返回后进入cipherService.encrypt函数中,生成初始化向量ivBytes后,进入具体的加密函数,最后return

然后就是记住身份并将信息加入到cookie中

解密

同样在相同位置下断点

单步到resolvePrincipals()函数, 进入this.getRememberedPrincipals(subjectContext), 其中有两个函数

第一个函数getRememberedSerializedIdentity()中, 先获取cookie中的值, 然后base64解密, 生成二进制数后返回

第二个函数convertBytesToPrincipals()中,先获取解密服务不为空后,将二进制数据传入decrypt函数进行解密,之后return this.deserialize(bytes)

deserialize(bytes)中有readObject(),触发apache.commons利用链漏洞

总结

加密时:序列化转为二进制、encrypt加密、base64加密、放入cookie 解密时:从cookie中取出、base64解密、decrypt解密、反序列化 在利用时,可以根据硬编码在源码中的key构造payload

怎么打-待完善

一般都是说用commons-collections依赖(简称CC?)

但是网站正常运行时不一定会使用这个依赖, 所以我们会使用commons-beanutils

IDEA有插件可以查看有哪些依赖真正运行(仅有compile和runtime真正运行)

CC链