Shiro-550
部署环境
操作系统 macOS
直接用homebrew安装tomcat,
brew install tomcat@8
java直接使用oracle java 1.8
shiro环境部署时参考了【R1】中的部署方式
1
2
3git clone https://github.com/apache/shiro.git
cd shiro
git checkout shiro-root-1.2.4使用idea打开
shiro/samples/web
目录修改
pom.xml
,在其中补充1
2
3
4
5
6
7
8
9
10
11
12<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
<!-- 在javax.servlet.jstl中补充一个version标签 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
部署到Tomcat
在idea中配置tomcat, 在左侧添加Tomcat Server —> Local
完成如上配置后点击Apply
保存配置,点击OK
关闭Run/Debug Configure
窗口。点击一旁的Run
按钮,通过浏览器访问配置中的URL配置项的路径(通常会默认打开浏览器访问)查看能否正常运行。
分析过程
初始化过程
根据漏洞细节,我们知道shiro使用了一个固定编码的keykPH+bIxk5D2deZiIxcaaaA==
,所以直接在整个项目文件中检索该key,找到该文件的所在位置。
该类实现了RememberMeManager
接口,接口有5个方法,不同的认证过程进入不同的函数。
AbstractRememberMeManager
在实例化时直接将encryptionCipherKey
与decryptionCipherKey
进行初始化
从setCipherKey进入,不断跟进,结果是直接将DEFAULT_CIPHER_KEY_BYTES
赋值给encryptionCipherKey
与decryptionCipherKey
加密方法直接实例化了一个AesCipherService
,由此可以见,加密方法为Aes加密
序列化过程
从onSuccessfulLogin
跟进程序,首先调用了forgetIdentity方法,但该方法没有任何内容执行。之后判断是否设置rememberMe,所以在登陆是要勾选remember me。之后跟进程序进入rememberidentity
,在此处下断点,并用浏览器登陆
根据调试器中的信息传入rememberIdentity
,token为用户的登陆信息,info中包含用户的登陆信息与权限信息
跟入rememberIdentity
方法,首先调用了getIdentityToRemember
方法,不断进行跟进,最终将info中的authcinfo中的principals的值返回给变量principals
继续跟进,在另一个rememberIdentity
中调用了converPrincipalsToBytes,这个convert函数对之前的得到的principals进行了序列化,并使用了AES加密对序列化后的结果进行加密,密钥就是那一串base64解码后的值。
跟入rememberSerializedIdentity
,将序列化并加密后的数据进行base64编码放入cookie中通过HttpServletResponse传回给浏览器。
至此shiro框架的登陆凭证加密过程便完成了。
反序列化过程
根据接口的方法,身份认证可能与getRememberedPrincipals
有关,看一下该方法的逻辑
上来直接调用getRememberedSerializedIdentity
,而该方法在类内是一个抽象方法,具体的实现是在CookieRememberMeManager
中实现
实现过程大致是将cookie中的认证信息进行base64解码。
回到getRememberedPrincipals
中,在完成base64解码后调用了convertBytesToPrincipals
在convertBytesToPrincipals方法中,先判断是否初始化了加密服务,如果没有就直接反序列化。
跟随进入deserialize
中,在类属性中声明了DefaultSerializer
对象
还原后的序列化数据被传入deserialze中。通过readObject方法反序列化为一个范型实例,在结果返回过程中回到deserialize方法时进行了强转,得到一个PrincipalCollection对象。
AES加解密过程
在序列化与反序列化过程中使用了AES加解密。直接通过动态调试去看AES使用了什么模式进行加解迷,根据上面的分析,断点直接打在decrypt
方法中的cipherService判断上查看cipherService中的属性。可以看出AES加解密使用了CBC模式与PKCS5Padding的填充方式。