Shiro反序列化分析
2024-10-14 08:32:30

环境搭建

下载shiro源码

1
2
3
4
git clone https://github.com/apache/shiro.git
cd shiro
git checkout shiro-root-1.2.4
cd ./shiro/samples/web

修改pom文件,添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--  需要设置编译的版本 -->
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
...
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<!-- 这里需要将jstl设置为1.2 -->
<version>1.2</version>
<scope>runtime</scope>
</dependency>
.....
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependencies>

修改完成后使用mvn命令编译war包:mvn package -DskipTests,在target目录下生成war包移动到tomcat webapps目录下启动Tomcat即可成功部署。
参考:https://paper.seebug.org/shiro-rememberme-1-2-4/

rememberMe反序列化分析

通过web.xml可以看到每次请求web都会进入到ShiroFilter,并进行相应的链式处理。
-w968
此filter的最上层为javax.servlet.Filter,即是Servlet规范中的Filter接口

-w457
org.apache.shiro.web.servlet.OncePerRequestFilter正常流程最后会调用doFilterInternal方法。
-w1440
此方法为为一个抽象方法,那么实现应该是在其子类中,根据上面的ShiroFilter的继承图可看到OncePerRequestFilter的子类为AbstractShiroFilter.
-w1321

此方法中调用了createSubject方法跟进后发现是调用了org.apache.shiro.subject中的buildSubject方法
-w1440

继续跟进,SecurityManager是一个接口类实现方法中在org.apache.shiro.mgt.DefaultSecurityManager
-w1440
其中又调用了resolvePrincipals方法,此方法调用了getRememberedIdentity方法,接着又调用了RememberMeManagergetRememberedPrincipals方法
-w1378

-w1339
此类是一个接口类,实现在AbstractRememberMeManager类中
-w1440
此方法中主要调用了三个方法

  1. getRememberedSerializedIdentity
  2. convertBytesToPrincipals
  3. onRememberedPrincipalFailure

getRememberedSerializedIdentity
-w859
此方法为抽象方法实现在其子类CookieRememberMeManager
-w1440
在其构造方法中创建另一个名为rememberMe的cookie,

1
2
3
4
5
6
public CookieRememberMeManager() {
Cookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);
cookie.setMaxAge(31536000);
this.cookie = cookie;
}

getRememberedSerializedIdentity方法主要是获取了请求包中的cookie的内容,如果cookie中的rememberMe的值不为deleteMe则进行base64解密返回。
-w1440

接着回到getRememberedPrincipals方法中来,如果getRememberedSerializedIdentity返回的内容不为空则进入到convertBytesToPrincipals方法中。

-w1120
此方法先对getRememberedSerializedIdentity返回的内容进行解密,

1
2
3
4
5
6
7
8
9
protected byte[] decrypt(byte[] encrypted) {
byte[] serialized = encrypted;
CipherService cipherService = this.getCipherService();
if (cipherService != null) {
ByteSource byteSource = cipherService.decrypt(encrypted, this.getDecryptionCipherKey());
serialized = byteSource.getBytes();
}

return serialized;

使用了AES加密
-w1156
-w1440

再然后进入到deserialize方法,实际是执行了org.apache.shiro.io.DefaultSerializer中的deserialize方法。
-w1440
可以明显的看到这里进行了反序列化操作。
回顾下cookie中的流程:
获取Cookie中的rememberMe字段的内容->base64解密一次->aes/cbc解密->反序列化

那么如果知道aes加密的key,将恶意代码序列化后加密放入cookie中的rememberMe字段中即可触发反序列化漏洞。