Java反序列化-shiro-550反序列化漏洞(四)
0X01 shiro-550反序列漏洞
最近审计到一套CMS,发现存在shiro反序列化漏洞,但之前没有分析复现过,审计的时候比较吃力。所以有了本篇文章,迟早要学,不如就现在。
环境搭建
下载地址:https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4
注意:主目录是shiro-root-1.2.4/samples/web
修改 shiro-root-1.2.4/samples/web 目录下的 pox.xml,添加如下内容
<!-- 需要设置编译的版本 --> |
重新编译后,配置 Tomcat


启动

环境配置成功
踩坑点:
toolchain 报错
在maven的con目录下配置toolchain.xml
<toolchain>
<type>jdk</type>
<provides>
<version>1.8</version> //版本
<vendor>sun</vendor>
</provides>
<configuration>
<jdkHome>/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/</jdkHome>//jdk目录
</configuration>
</toolchain>一定要注意 jdk 运行的版本 是否是对应的。
漏洞分析
勾选 Remember Me

抓包获取 Remember 值

Ln9fYDGXXhJiXa5nqLEHLvK+cK/azlnBclTgfKih5vLK+x90blH6SDrmTNMB8RN1Ri8Vh1TEojg8rGK4Fa7KvakkQzYYHpWHznKBJ1Q71qRdqeDAKCu5HglqvLO7XDEZKimqeh7gXY8i80hBp/AQ0fwSXiPWr91P1XgCct0MN/ciit3U957ZMzcWN53AQXm9sk1i7FVPdMs2QhFgZKRMNxQsNypj/G29Vqnp7zxw3r5+49emVJJZpF/wEUEmsIUr3rrXLZMVZW/N9iLVbg36KkncAh8O6b6wuoJahHt2JBXxCTxHmVxBfxMg4MLBIp+wMVdnMj3vZ8fGt6sC/Al/Eva1mXaOhtzfTMq5V1vGMgLU1FKBrMXjDHNLUvMaagxj7SmySV7dijNtvelAN9RBX2w5TPE4A9uOcZM6FYMg/0PTpxRAoEuHSQ02hrdJpDHqYLMwY+1hGQP/A8gtdAHk756IfkXIXVodI5F8TF+NS8bqj4XClmwu5T/kmFKfylZf |
加密过程
在入口点打上断点,分析一下加密过程
入口点文件 /shiro-root-1.2.4/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java

跟进 onSuccessfulLogin 方法,此方法获取
- subject 单个用户的状态、安全认证、授权等
- token 用户名、密码及其他信息
- info 用户信息

forgetIdentity 处理了 subject 对象,跟进 forgetIdentity 方法

此处 getcookie 并调用了 removeFrom 方法,继续跟进

跟进 addCookieHeader 这里其实就是 set-cookie,在 response 头部添加 set-cookie: rememberMe=deleteMe


至此 forgetIdentity(subject) 方法跟完了,在 subject 对象中获取 request 和 response 值,并设置了set-cookie 的值。
回到 onSuccessfulLogin 继续向下走, isRememberMe 判断我们是否勾选了 rememberMe

跟进 rememberIdentity

convertPrincipalsToBytes 处理用户名,还调用了 rememberSerializedIdentity 我们先跟 convertPrincipalsToBytes

serialize 继续跟进

可以看见此处是对用户名进行了序列化

跳回 convertPrincipalsToBytes

getCipherService 是 shiro 提供的 AES 加密算法
getEncryptionCipherKey 获取秘钥的 key,查看此方法

由于没找到调用 key 的点,反向跟踪了一下
src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java

AbstractRememberMeManager 构造方法中调用了

而 setCipherKey 中调用了 setEncryptionCipherKey 方法设置 key

最后 getEncryptionCipherKey 获取到 key

返回加密后的 bytes 流数据


我们跳回 rememberIdentity

bytes 的值是被序列化后的用户名, rememberSerializedIdentity 处理了 subject 值和 bytes值

可以看见使用 base64 加密了数据,并进行了保存,至此,我们的加密流程算是走完了。
解密过程
断点
/shiro-root-1.2.4/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java |

getRememberedSerializedIdentity 处理登录请求,跟进

cookie 中获取 base64加密值

解密 base64

下一步 我们跳回 getRememberedPrincipals, 跟进 convertBytesToPrincipals方法

这里的 decrypt 是解密,这里和加密一样,都是获取AES加密算法及秘钥。


serialized 返回的序列化的字符串 bytes 值
rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBzcgAXamF2YS51dGlsLkxpbmtlZEhhc2hNYXA0wE5cEGzA+wIAAVoAC2FjY2Vzc09yZGVyeHIAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAADHcIAAAAEAAAAAF0AAhpbmlSZWFsbXNyABdqYXZhLnV0aWwuTGlua2VkSGFzaFNldNhs11qV3SoeAgAAeHIAEWphdmEudXRpbC5IYXNoU2V0ukSFlZa4tzQDAAB4cHcMAAAAED9AAAAAAAABdAAEcm9vdHh4AHcBAXEAfgAFeA== |
解密后

下一步 跳回到 convertBytesToPrincipals 执行反序列化操作

跟进 deserialize ,可以看见 调用了readObject 执行了反序列化操作 得到用户名

这里的 readObject 并不是原生的 ObjectInputStream 而是重写的 ObjectInputStream,最后调用过程从
cookie 中获取 Remember 值 ——> base64 解密 ——> AES 解密 ——> 反序列化
中间有一些关于java的加密解密的算法 IV 等 由于没学过,不太了解,但是构造 POC 需要关于这一块的知识
[java安全之安全加密算法][https://www.cnblogs.com/nice0e3/p/13894507.html]
漏洞复现
poc: 这个poc 没成功….
# -*-* coding:utf-8 |
本地监听:
python -m SimpleHTTPServer 8083 |

使用工具打了一波,显示成功了,但是手工生成的时候链子有点问题,后续得详细分析下一下 shiro 的各种CC链






