环境搭建
git clone https://github.com/apache/shiro.git shiro-rootcd shiro-root
git checkout -f shiro-root-1.4.0
参考之前SHIRO-550的搭一下就行
漏洞分析
首先是SHIRO-550,触发点是cookie中的RememberMe,漏洞触发流程:
- base64 解码
- 使用 AES 解密
- 反序列化解密后的字符串
shiro-1.25以前,AES密钥是硬编码到源码中的,因此可以更改RememberMe的值进行反序列化RCE
而1.2.5之后,shiro采用了随机密钥,也就引出了SHIRO-721,通过padding oracle attack的方式得到,
根据p0师傅之前的文章,在shiro中,当我们更改padding值时,padding正确但反序列化错误则会爆deserialize error;padding错误爆padding error,
具体处理代码如下:
padding正确但爆反序列化error
try {
ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);
@SuppressWarnings({"unchecked"})
T deserialized = (T) ois.readObject();
ois.close();
return deserialized;
} catch (Exception e) {
String msg = "Unable to deserialize argument byte array.";
throw new SerializationException(msg, e);
}
padding错误爆padding error
try {
return cipher.doFinal(bytes);
} catch (Exception e) {
String msg = "Unable to execute 'doFinal' with cipher instance [" + cipher + "].";
throw new CryptoException(msg, e);
}
而shiro中如果解密rememberMe的过程中有错误,统一的处理方式都是调用removeFrom,最终返回deleteMe
这就很矛盾,没办法构造出padding oracle需要的bool条件。
解决这个问题用到了java反序列化中的一个小trick,java中的ObjectOutputStream是一个Stream,他会按格式以队列方式读下去,后面拼接无关内容,不会影响反序列化。通过这种方式,在抓到的rememberMe之后加新的iv和value,就既能反序列化成功,又能验证padding是否正确了,从而满足了padding oracle所需要的bool条件。具体的padding oracle过程和CBC bit flipping就不详细写了,参考之前大佬们的文章即可。
除此之外,shiro的接口在验证登陆时有authc和user两种权限,authc是认证过,user是登录过,如果开启了rememberMe功能的话,user可以通过的,而authc通过不了。因此rememberMe只在有user权限的接口有用。
综上,该洞的利用条件如下:
- 可以登录
- 找到可以用rememberMe的接口
- 可以padding oracle
本地测试
网上的poc很多,这里直接找一个跑一下本地的shir-simpleweb环境就行,URLDNS很容易验证。
实战挖掘
漏洞刚出时找到了一个RuoYi CMS,使用了shiro 1.4.1。
本地照文档搭起来,看一下接口:
public AjaxResult ajaxLogin(String username, String password, Boolean rememberMe)
{
UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
Subject subject = SecurityUtils.getSubject();
try
{
subject.login(token);
return success();
}
...
}
这里用到了rememberMe,下面找一下哪些接口有使用的权限。
// 所有请求需要认证
filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
shiroConfig里很清楚,所有接口都有user权限,大概稳了。
exp打一发,收到了URLDNS,稳了
参考
https://p0sec.net/index.php/archives/126/
https://superxiaoxiong.github.io/2019/11/26/shiro-padding-oracle-attack%E5%88%86%E6%9E%90/