Apache Shiro Java 反序列化漏洞分析

Author: rungobier (知道创宇404安全实验室)

0x00 概述

Apache Shiro 在 Java 的权限及安全验证框架中占用重要的一席之地,在它编号为550的 issue 中爆出严重的 Java 反序列化漏洞。下面,我们将模拟还原此漏洞的场景以及分析过程。

0x01 漏洞场景还原

首先,需要获取 Apache Shiro 存在漏洞的源代码,具体操作如下:

为了配合生成反序列化的漏洞环境,需要添加存在漏洞的 jar 包,编辑 pom.xml 文件,添加如下行:

修改完成 pom.xml 文件后,开始使用 mvn 进行存在漏洞环境的 war 包进行编译,若中间出现很慢或卡死的情况,需要如何处理你懂的。

最终可以将 target 目录下生成的 samples-web-1.2.4.war 文件拷贝至 tomcat 目录下的 webapps 目录,这里将其重命名为了 shiro.war 文件,启动 tomcat, 在浏览器当中输入 http://localhost:8080/shiro 可以看到登录页面,如下图:

1-1-1024x547[1]

 

其次,我们需要产生payload的 ysoserial ,执行下列命令,可以获取到需要的 jar 文件:

0x02 漏洞分析

从官方的 issue 上来看,存在几个重要的点:

  • rememberMe cookie
  • CookieRememberMeManager.java
  • Base64
  • AES
  • 加密密钥硬编码
  • Java serialization

首先,我们从正常登录返回的 cookie 中获取到 remeberMe 的值如下:

使用 Base64 解码存储为二进制文件后,内容如下:

从这些内容中没有看到有明确的 Java 序列化特征字,因为上述关键字当中提到了 AES 和 加密密钥硬编码,所以需要去跟一下源码。打开 CookieRememberMemanager.java 文件并没有找到硬编码的加密密钥,继续跟它的父类 AbstractRememberMeManager 看到了如下几行:

目前可以断定 Base64.decode("kPH+bIxk5D2deZiIxcaaaA==") 就是我们要找的硬编码密钥,因为 AES 是对称加密,即加密密钥也同样是解密密钥。

除了密钥,还有两个必要的属性,一个是 AES 中的 mode(加解密算法),另外一个是 IV(初始化向量),继续查看 AbstractRememberMeManager 的代码, 在它的方法 encrypt 中看到如下语句:

其中 CipherService 是个接口,而实现这个接口的是一个抽象类 JcaCipherService,在它的成员函数 initNewCipher 中下断点,可以看到我们需要的几个关键信息: AES 的 mode 为 CBC, IV是随机生成的,但是偶然发现这个IV并没有真正使用起来。

那么利用上述获取到的信息,对 Base64 解码后的文件进行解密操作,解密 Python 代码如下:

解密后的文件内容如下:

OK,看到第二行打头的 ac ed 00 05了吗? 这是 Java 序列化的标志,说明解密成功。那么文件第一行是什么呢?我们继续来跟 JcaCipherService 这个类,看它的一个加密函数 encrypt :

可以看出这个加密函数是先将 IV 写入,然后再加密具体的序列化对象的字节码,这样 IV 值我们可以直接通过读取第一行(16个字节,128位)获得了。

这里还需要跟进一个重要的东西,就是加密的序列化对象,回到 CookieRememberMeManager 的父类 AbstractRememberMeManager , 上面贴出的 encrypt 中有个 serialized 的字节数组,这个字节数组是从哪里来的呢?在这个类中直接调用这个方法的是 convertPrincipalsToBytes :

可以看出序列化对象是 PrincipalCollection ,但是这个类是个接口,看了下实现它的类是 SimplePrincipalCollection 对象。 在它的代码当中,可以发现关键的两个方法: writeObject 和 readObject.

最后,具体的 Payload 也就呼之欲出了,代码如下:

将上述代码保存为 /tmp/create_payload.py, 执行如下命令:

运行结果如图:

2[1]

 

 

到这里就结束了吗?其实还没有,因为我们现在还没有找到具体的反序列化触发点在哪里。现在利用这个 payload 进行触发,并下断点,断点设置在前面所述的 AbstractRememberMeManager,具体的函数如下:

这里断点我下在了principals = convertBytesToPrincipals(bytes, subjectContext); 上,进行跟踪调试,最终的反序列化落在了 DefaultSerializer 类的 deserialize 的函数里,具体的函数细节如下:

看到那个令人激动的 readObject 了吧,至此收工,结束。

0x03 漏洞修复

升级 Shiro 版本至 1.2.5 以上

0x04 参考

发表评论