使用CC6利用Shiro反序列化漏洞

v2-ddbc8963fb1463db61fa814bdfc3cb84_r

框架中的readObject方法来反序列化的例子

前言

Apache Shiro™是一个功能强大且易于使用的Java安全框架,可以执行身份验证、授权、加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的web和企业应用程序。

Apache Shiro | Simple. Java. Security.

就比如一个前后端的blog系统,管理员需要进入后台来进行管理文章,这时候就需要登陆验证的框架,就比如Shiro当然Spring security也可以实现类似功能。

[SHIRO-550] Randomize default remember me cipher - ASF JIRA (apache.org)

image-20240331100704841

  • shiro对每次访问都会用到”记住我”的功能进行以下操作:
  • 检索rememberMe cookie的值 //cookie中是否有这个参数
  • Base 64解码 //对参数的值进行解码
  • 使用AES解密 //对参数的值再进行解密
  • 使用Java序列化(ObjectInputStream)反序列化。 //对解出的参数的值进行反序列化

Shiro的CookieRememberMeManager类里对漏洞参数rememberMe进行序列化,加密等操作,来实现在浏览器或服务器重启后用户不丢失登录状态,下次读取时进行解密再反序列化。

在Shiro 1.2.4版本之前内置了一个默认且固定的AES加密密钥,导致攻击者可以伪造任意的rememberMe Cookie,进而触发反序列化漏洞。

Demo

JavaThings/shirodemo at master · phith0n/JavaThings (github.com)

这个是p神的学习漏洞的小项目

依赖

  • shiro-core、shiro-web,这是shiro本身的依赖。
  • javax.servlet-api、jsp-api,这是JSP和Servlet的依赖,仅在编译阶段使用,因为Tomcat中自带这 两个依赖。
  • slf4j-api、slf4j-simple,这是为了显示shiro中的报错信息添加的依赖。
  • commons-logging,这是shiro中用到的一个接口,不添加会报错。
  • commons-collections,为了演示反序列化漏洞,增加了commons-collections依赖。

image-20240331103904084

使用 mvn package 将项目打包成war包,放在Tomcat的webapps目录下,再进入bin目录下点击startup.bat运行项目。然后访问 http://localhost:8080/shirodemo/ 进入登陆页面。

image-20240331105426245

账号密码,root/secret,成功登录:

image-20240331105441825

如果勾选了remember me会多一个rememberMe的Cookie

image-20240331110225496

调试

IDEA配置调试shiro

1
2
3
4
5
6
7
Run -> 
Edit Configurations ->
点击+号添加TomcatServer(Local) ->
Server中配置Tomcat路径 ->
选择JRE版本 ->
Deployment中点击+号添加tomcat里生成的shiro文件夹 ->
点击Apply

image-20240331110948715

加入war包,IDEA很智能。

image-20240331111027464

再点击运行即可

image-20240331111101637

参数所在位置

也是可以登入,勾选remember me之后,cookie里面就有rememberMe这个名称,而shiro每次都会对cookie中的rememberMe来进行解密后反序列化操作来确定访问者权限,所以直接在cookie传输rememberMe参数就可以控制shiro反序列化的值

image-20240331111256593

加密解密的方法

看看当作入口点的CookieRememberMeManager类,ALT+7查看所有方法。

image-20240331112934107

进入他的父类AbstractRememberMeManager可以看到默认的key

QQ图片20240331113128

AbstractRememberMeManager类函数名为encrypt(加密)下断点

image-20240331114835921

Drop Frame返回上一个方法,serialized的值是我刚才web端登录的用户名root序列化后的数据,shiro验证完了登录的账号密码,然后根据用户名生成序列化数据准备进行加密了。

image-20240331115408588

再步过一步,回到断点的位置仔细看这个cipherService,模式为CBC,128位,填充方式为PKCS5Padding

image-20240331115556609

步入

image-20240331131726397

再步入,就是加密的过程了

image-20240331132036717

planintext为序列化的用户名

key为密钥

iv是随机生成长度为16的字节

output是随机生成的,占用长度为16字节+AES加密结果

接着步过就回到断点的地方。

回到CookieRememberMeManager类,看到队反序列化的内容又进行了base64编码

image-20240331133322608

Shiro-550反序列化漏洞(CVE-2016-4437)

前面分析的就是Shiro-550

攻击流程

  1. 使用以前学过的CommonsCollections利用链生成一个序列化Payload
  2. 使用Shiro默认Key进行加密
  3. 将密文作为rememberMe的Cookie发送给服务端

就跟着p神来构造cc6的吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
public class Shiro {
public static void main(String[] args) throws Exception{
byte[] payloads = new CommonCollections6().getPayload();
System.out.println(payloads);
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(payloads,key);
System.out.printf(ciphertext.toString());
}
}

在之前的CommonCollections6类新建一个名为getPayload()的byte[]返回值方法,然后return一个新分配的字节数组。

image-20240401175611407

登陆进去之后删除掉JSESSIONID,因为当存在 JSESSIONID 时,会忽略 rememberMe。然后替换remeberMe的值。

image-20240401175104746

但是tomact报错了

image-20240401175732876

image-20240401175756552

Transformer方法出现了问题,看到最后一行的这个方法,重写了resolveClass方法。

image-20240401180249432

进入方法看到注释:加载与指定流类描述等价的本地类。子类可以实现此方法,以允许从备用源获取类。读取序列化流的时候,读到一个字符串形式的类名,需要通过这个方法来找到对应的 java.lang.Class 对象。

image-20240401180716352

在ClassResolvingObjectInputStream类抛出异常的地方打上断点看看。可以看到出异常时加载的类名为[Lorg.apache.commons.collections.Transformer; [L是一个JVM的标记,说明实际上这是一个数组,即Transformer[]

image-20240401191818961

Shiro resovleClass使用的是ClassLoader.loadClass()而非Class.forName(),而ClassLoader.loadClass不支持装载数组类型的class。

想要进入Tomcat要加入依赖,版本要与使用的Tomcat对应。

1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.84</version>
</dependency>

进到ClassUtils类的forName方法,

image-20240402093628562

设置

image-20240401215537207

运行到这步就报错了,

image-20240401220115150

WebappClassLoaderBase (Apache Tomcat 9.0.87 API Documentation)

image-20240401221435945

加载具有指定名称的类,使用以下算法进行搜索,直到找到并返回 类。如果找不到该类,则返回 。ClassNotFoundException

  • 调用以检查类是否已加载。如果有,则返回相同的对象。findLoadedClass(String) Class
  • 如果该属性设置为 ,则调用该方法 父类加载器(如果有)。delegate true loadClass()
  • 调用以在本地定义的存储库中查找此类。findClass()
  • 调用父类加载器的方法(如果有)。loadClass()

如果使用上述步骤找到该类,并且标志为 ,则此方法 然后将调用生成的 Class 对象。

当本地cache或存储库中均无时,则通过父类加载器URLClassLoader.loadClass(),在URLClassLoader.loadClass()处添加断点。加载主角登场image-20240402093403040

可以看到并没有找到class,然后就进入他的父类方法里面了

image-20240402095518482

一开始path虽然正常但是返回结果却是null,后面还出来了错误的path

image-20240402094429123

进入findLoadedClass0这个path是根据name来获取的就会出现bug,肯定是找不到的。

1
/[Lorg/apache/commons/collections/Transformer;.class

Tomcat和JDK的Classpath是不公用且不同的,Tomcat启动时,不会用JDK的Classpath

  1. 数组形式会使得shiro想尝试从本地加载时,path也被赋上数组标识,导致无法从本地jar包中正常获取。
  2. 而URLClassLoader中是因为Tomcat和JDK的Classpath的不同,导致即使path正确,也无法找到对应class

构造不含数组的反序列化Gadget

通过上面的分析也是知道了问题所在,那么应该怎么避免数组呢?

置为TemplatesImpl对象

看到org.apache.commons.collections.functors.InvokerTransformer#transform

image-20240402102253060

这个方法通过反射根据传入的input对象,调用其iMethodName。可以将iMethodName置为newTransformer来实现传入的input为构造好的TemplatesImpl对象这样就不是数组了。

传入到ConstantTransformer.transform函数的input,该transform函数不依赖input,而直接返回iConstant

image-20240402113346432

TiedMapEntry,其作为中继,调用了LazyMap(map)的get函数。

image-20240402113527875

其中mapkey我们都可以控制,而LazyMap.get调用了transform函数,并将可控的key传入transform函数

image-20240402113712820

也就是LazyMap#get 的参数key,会被传进transform(),来传递TemplatesImpl对象。

把CommonsCollections6改一下

1
2
3
4
5
6
7
8
9
java.util.HashSet.readObject()
-> java.util.HashMap.put()
-> java.util.HashMap.hash()
-> TiedMapEntry.hashCode()
-> TiedMapEntry.getValue()
-> LazyMap.get()
-> InvokerTransformer.transform()
-> java.lang.reflect.Method.invoke()
...

poc

https://github.com/phith0n/JavaThings/blob/master/shiroattack/src/main/java/com/govuln/shiroattack/CommonsCollectionsShiro.java

image-20240402123802198

总结

shiro还有很多需要学习的东西,比如各种链子进行利用,因为Shiro是一个反序列化的入口,还有权限绕过问题。很老的洞了,实际生活中应该没了,可能一些竞赛会拿出来玩玩吧。主要在于锻炼思维。

参考

知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具 (zsxq.com)

Shiro反序列化分析带思路及组件检测笔记 - 先知社区 (aliyun.com)

redis未授权到shiro反序列化之session回显马|NOSEC安全讯息平台 - 白帽汇安全研究院

redis未授权到shiro反序列化 - 先知社区 (aliyun.com)

shiro反序列化漏洞原理分析以及漏洞复现 - FreeBuf网络安全行业门户

shiro-1.2.4反序列化分析踩坑 - W4nder - 博客园 (cnblogs.com)

Java反序列化利用链分析之Shiro反序列化-安全客 - 安全资讯平台 (anquanke.com)

Orange: Pwn a CTF Platform with Java JRMP Gadget