Shiro-550

部署环境

  1. 操作系统 macOS

  2. 直接用homebrew安装tomcat,brew install tomcat@8

  3. java直接使用oracle java 1.8

  4. shiro环境部署时参考了【R1】中的部署方式

    1
    2
    3
    git clone https://github.com/apache/shiro.git
    cd shiro
    git checkout shiro-root-1.2.4
  5. 使用idea打开shiro/samples/web目录

  6. 修改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

Untitled

Untitled

完成如上配置后点击Apply 保存配置,点击OK关闭Run/Debug Configure窗口。点击一旁的Run按钮,通过浏览器访问配置中的URL配置项的路径(通常会默认打开浏览器访问)查看能否正常运行。

分析过程

初始化过程

根据漏洞细节,我们知道shiro使用了一个固定编码的keykPH+bIxk5D2deZiIxcaaaA==,所以直接在整个项目文件中检索该key,找到该文件的所在位置。

Untitled

该类实现了RememberMeManager接口,接口有5个方法,不同的认证过程进入不同的函数。

Untitled

AbstractRememberMeManager在实例化时直接将encryptionCipherKeydecryptionCipherKey进行初始化

Untitled

从setCipherKey进入,不断跟进,结果是直接将DEFAULT_CIPHER_KEY_BYTES赋值给encryptionCipherKeydecryptionCipherKey

加密方法直接实例化了一个AesCipherService,由此可以见,加密方法为Aes加密

序列化过程

onSuccessfulLogin跟进程序,首先调用了forgetIdentity方法,但该方法没有任何内容执行。之后判断是否设置rememberMe,所以在登陆是要勾选remember me。之后跟进程序进入rememberidentity,在此处下断点,并用浏览器登陆

Untitled

Untitled

根据调试器中的信息传入rememberIdentity,token为用户的登陆信息,info中包含用户的登陆信息与权限信息

Untitled

跟入rememberIdentity方法,首先调用了getIdentityToRemember方法,不断进行跟进,最终将info中的authcinfo中的principals的值返回给变量principals

Untitled

继续跟进,在另一个rememberIdentity中调用了converPrincipalsToBytes,这个convert函数对之前的得到的principals进行了序列化,并使用了AES加密对序列化后的结果进行加密,密钥就是那一串base64解码后的值。

Untitled

跟入rememberSerializedIdentity,将序列化并加密后的数据进行base64编码放入cookie中通过HttpServletResponse传回给浏览器。

Untitled

至此shiro框架的登陆凭证加密过程便完成了。

反序列化过程

根据接口的方法,身份认证可能与getRememberedPrincipals有关,看一下该方法的逻辑

Untitled

上来直接调用getRememberedSerializedIdentity,而该方法在类内是一个抽象方法,具体的实现是在CookieRememberMeManager中实现

Untitled

实现过程大致是将cookie中的认证信息进行base64解码。

回到getRememberedPrincipals中,在完成base64解码后调用了convertBytesToPrincipals

Untitled

在convertBytesToPrincipals方法中,先判断是否初始化了加密服务,如果没有就直接反序列化。

跟随进入deserialize中,在类属性中声明了DefaultSerializer对象

Untitled

还原后的序列化数据被传入deserialze中。通过readObject方法反序列化为一个范型实例,在结果返回过程中回到deserialize方法时进行了强转,得到一个PrincipalCollection对象。

Untitled

AES加解密过程

在序列化与反序列化过程中使用了AES加解密。直接通过动态调试去看AES使用了什么模式进行加解迷,根据上面的分析,断点直接打在decrypt方法中的cipherService判断上查看cipherService中的属性。可以看出AES加解密使用了CBC模式与PKCS5Padding的填充方式。

Untitled

Reference