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链