Shiro漏洞CVE总结合集
Shiro漏洞CVE总结合集
这里对Shiro的漏洞CVE做了一些归纳整理,主要两个方面:
1.加密方式可知被使用加密数据反序列化
2.Spring与Shiro对URL匹配规则的差异导致权限绕过
CVE-2016-4437
影响版本:
Apache Shiro<1.2.5
当未修改用于“remember me”特征的AES密钥时,存在反序列化漏洞
搭建环境
本地环境搭建可以参考我之前的文章:传送门
源码安装包: shiro-root-1.2.4
docker搭建:
docker pull medicean/vulapps:s_shiro_1
docker run -d -p 8888:8080 -p 7777:6666 medicean/vulapps:s_shiro_1
复现:
这里有两个链子可选:
- 打CC(需要手动添加CC依赖): CommonsCollectionsShiro传送门
- 打CB(默认情况下即可,但是需要对CB链的BeanComparator稍作小改造): CommonsBeanutilsShiro传送门
高版本加密
Shiro高版本加密方式从AES-CBC换成了AES-GCM,由于加密算法的变化导致用于攻击shiro-550的exp无法试用于新版Shiro
加密模式的变化发生在针对Oracle Padding Attack的修复,1.4.2版本更换为了AES-GCM加密方式
这个具体的加密解密原理现在还没学密码学所以我也不是不是很清楚, 在这里埋个坑等学了密码学再回来填坑吧, 详细的Java代码加密解密流程可以看: 参考文章
在这里直接给加密解密的python脚本吧:
加密脚本
设定加密模式为aes-gcm,随机生成16位iv,使用encrypt_and_digest
生成密文和tag,将iv+密文+tag base64编码输出即为最终的rememberMe内容
import os,base64,uuid
from Crypto.Cipher import AES
def encode(p):
global key
BS = AES.block_size
mode = AES.MODE_GCM
iv = uuid.uuid4().bytes
encryptor = AES.new(base64.b64decode(key), mode, iv)
file_body=base64.b64decode(p)
enc,tag=encryptor.encrypt_and_digest(file_body)
base64_ciphertext = base64.b64encode(iv + enc + tag)
print("Encode_result:")
print(base64_ciphertext)
print("\n")
return base64_ciphertext
解密脚本
设定加密模式为aes-gcm,base64解密后取前16位作为iv,取后16位作为tag进行签名验证,中间的为密文
import os,base64,uuid
from Crypto.Cipher import AES
def decode(s):
global key
BS = AES.block_size
mode = AES.MODE_GCM
cipher=base64.b64decode(s)
iv=cipher[0:16]
enc=cipher[16:-16]
tag=cipher[-16:]
decryptor = AES.new(base64.b64decode(key), mode, iv)
plaintext=decryptor.decrypt_and_verify(enc,tag)
print("decode_plaintext:")
print(plaintext)
base64_plaintext=base64.b64encode(plaintext).decode()
print ("\nbase64_plaintext:\n"+base64_plaintext+"\n")
return base64_plaintext
加解密和Key探测工具: shiro-exploit
CVE-2020-1957
影响版本:
Apache Shiro < 1.5.2
时间轴
-
2020/3/25:Apache Shiro官网披露该漏洞。
-
2020/9/5 : 深信服千里目安全实验室发布漏洞分析文章。
在Apache Shiro 1.5.2以前的版本中,在使用Spring动态控制器时,攻击者通过构造 ..; 这样的跳转可以绕过Shiro中对目录的权限限制
Apache Shiro 1.5.2之前的版本,由于Shiro拦截器和requestURI的匹配流程与Web框架的拦截器的匹配流程有差异,攻击者构造一个特殊的http请求,可以绕过Shiro的认证,未授权访问敏感路径。
此漏洞有两种攻击方式,第一种攻击方式适用于Shiro < 1.5.0版本,由于Shiro 1.5.0版本修复补丁考虑不全面,导致补丁绕过,出现了第二种攻击方式,适用于Shiro < 1.5.2版本。
1.4.2demo源码: shiro-basic
具体环境搭建还需要修改一些配置,详细可见于: Shiro权限绕过漏洞分析(CVE-2020-1957)
-
访问/hello/1接口,可以看到被authc拦截器拦截。
-
访问/hello/1/,成功绕过authc拦截器,获取到了资源。
漏洞初始成因可以定位到 PathMatchingFilterChainResolver的getChain函数下,该函数作用根据URL路径匹配中配置的url路径表达式来匹配输入的URL,判断是否匹配拦截器,匹配成功将会返回响应的拦截器执行链,让ShiroFither执行权限操作的。
其对于URL路径表达式和输入URL的匹配主要通过pathMathches函数进行匹配。
pathMatches函数其最终会调用shiro.util.AntPathMatcher类中doMatch的对于ant格式的pathPattern和requestURI进行匹配。
//pathMatches:135, PathMatchingFilterChainResolver (org.apache.shiro.web.filter.mgt)
protected boolean pathMatches(String pattern, String path) {
PatternMatcher pathMatcher = this.getPathMatcher();
return pathMatcher.matches(pattern, path);
}
doMatch:109, AntPathMatcher (org.apache.shiro.util),当Shiro 的Ant格式的pathPattern 中的的通配符是不支持匹配路径的,所以/hello/不能成功匹配/hello/1/,也就不会触发authc拦截器进行权限拦截。从而成功绕过了Shiro拦截器,而后再进入到spring拦截器中,/hello/1/与/hello/1能获取到相同的资源。
漏洞修复
1.5.0版本修复源
自tomsun28提交的PR代码,代码修复位置为pathsMatch:125, PathMatchingFilter (org.apache.shiro.web.filter),该修复方式是通过判断requestURI是否以/为结尾,如果以/结尾的话,则去掉尾部的/符号在与URL表达式进行比较。
也就是当requestURI为/hello/1/等以/为结尾的URI的时候,都会被清除最后的/号,再进行URL路径匹配。
≤1.5.1版本绕过
观察1.5.2版本中新添加的测试用例。
切换测试版本到1.5.1中,然后从中上面的测试用例提取payload进行绕过。
在1.5.1版本中,添加/还是会直接跳转到登录。: /hello/1/
使用payload,/fdsf;/../hello/1
,成功绕过。
问题同样可以定位到getChain函数中对于requestURI的获取中,如下图所示,this.getPathWithinApplication(request)获取的requestURI为/fdsf,而不是我们输入的/fdsf;/../hello/1,从而导致后面的URI路径模式匹配返回False,从而再次绕过了shiro拦截器。
getPathWithinApplication函数中会调用WebUtils (org.apache.shiro.web.util)中的getRequestUri函数获取RequestUri。
public static String getRequestUri(HttpServletRequest request) {
String uri = (String)request.getAttribute("javax.servlet.include.request_uri");
if (uri == null) {
uri = request.getRequestURI();
}
return normalize(decodeAndCleanUriString(request, uri));
}
RequestUri函数中最终调用decodeAndCleanUriString函数对URI进行清洗。
private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
uri = decodeRequestString(request, uri);
int semicolonIndex = uri.indexOf(59);//获取;号的位置
return semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri;
}
如果URI中存在;号的话,则会删除其后面的所有字符。/fdsf;/../hello/1/最终也就变成了/fdsf。
1.5.2版本修复
再1.5.2版本中对其进行了修复,获取requestURI的方式从request.getRequestUri直接获取的方式更改为获取request的ContextPath,ServletPath,PathInfo,然后再重新拼接而成。
输入的/fdsf;/../hello/1/,将会被拼接为//hello/1/1再进行URI路径匹配,则无法绕过拦截器。
修复方案
1.升级1.5.2版本及以上
2.尽量避免使用*通配符作为动态路由拦截器的URL路径表达式。
1.5.2版本绕过
实际上就是下面的两个CVE
CVE-2020-13933(Apache Shiro < 1.6.0)
CVE-2020-11989(Apache Shiro < 1.5.3)
总结
在web容器中,Shiro的拦截器是先与spring(Servlet)执行,两者拦截器对于URI模式匹配的差异,导致Shiro拦截器的绕过,而Shiro对其进行了两次修复,其一为删除requestURI后面的/号进行URL路径匹配,算是简单的修复了添加/号绕过的方式,而后在1.5.2版本中通过requestURI自主拼接的方式修复了/fdsf;/../hello/1/等使用了;号方式的绕过。
CVE-2020-11989
Apache Shiro 身份验证绕过漏洞 (CVE-2020-11989)
影响版本:
Apache Shiro < 1.5.3
漏洞时间线
- 2020-5-27 腾讯安全玄武实验室研究员向Apache Shiro官方报告此漏洞位置
- 2020-6-16 Apache Shiro官方开始处理此漏洞
- 2020-6-22 Apache Shiro官方发布漏洞公告与致谢信息
将Apache Shiro与Spring控制器一起使用时,特制请求可能会导致身份验证绕过。
漏洞复现
环境搭建:
docker pull vulfocus/shiro-cve_2020_1957
docker run -d -p 8080:8080 -v /var/run/docker.sock:/var/run/docker.sock -e VUL_IP=192.168.27.180 vulfocus/shiro-cve_2020_1957
shiro1.4.2版本权限绕过
访问 /hello/1 接口,可以看到被authc拦截器拦截了,将会跳转到登录接口进行登录:
访问 /hello/1/ ,成功绕过authc拦截器,获取到了资源:
shiro<=1.5.1版本绕过
绕过payload: /dwdww;/../hello/1 成功绕过
当进入应用后我们的请求页面被解析成/hello/a%2fa
,所以它可以进入到spring controller中的/hello/{name}
,但是因为shiro再次做了url解码,导致判断的uri成为了/hello/a/a
它不属于我们配置的权限判断地址/hello/*
。但是hello/a/a所加载返回的资源就是需要身份验证的/admin/*
资源。
org.apache.shiro.web.util.WebUtils#getPathWithinApplication
采用的是getRequestUri
方法同时里面会调用到decodeAndCleanUriString
-> decodeRequestString
进行再一次的解码。
也就造成了
%2f -> /
可以看到这里就造成了和Spring的uri处理不一致的问题,也就导致了接下来的问题。
当进入到org.apache.shiro.util.AntPathMatcher#doMatch
去匹配是否符合我们之前定义的权限路由/hello/*
)
这套流程判断后发现pattIdxStart > pattIdxEnd
,也可以通过观察当前变量来理解,如下
这里可以看到path成了/hello/a/a
有两个/
,所以根据上图所示代码跳过了这次的判断,导致了身份验证绕过。
官方修复:
采用了标准的getServletPath(request) + getPathInfo(request)
同时不再进行url解码。
自查:
- 通过WAF检测请求的uri中是否包含
%25%32%66
关键词- 通过WAF检测请求的uri开头是否为
/;
关键词
CVE-2020-13933
影响版本:
Apache Shiro < 1.6.0
时间线
2020-08-17 Apache Shiro发布通告
2020-08-18 360-CERT 发布预警
2020-09-22 360-CERT 发布分析
利用条件:
结合Spring框架并且权限ant风格的配置需要是*
而不是**
构造特定 PoC 请求指定资源时,不触发身份验证,并绕过权限(通过%3b绕过)
org.apache.shiro.web.util.Webutils#getPathWithinApplication,shiro1.5.3进行了修改,直接在这里下断点,然后dubug调试
这里挺值得细说的,后面的分析见另一篇文章:CVE-2020-13933 | Shiro权限绕过漏洞
Shiro-550
Apache Shiro 反序列化漏洞CVE-2016-4437 - Shiro-550
SHIRO-682
其实就是CVE-2020-1957
根据Shiro开发者在1.5.2版本中提交的commit中关于PathMatchingFilter类的测试用例,可以直接关联到JIRA issueSHIRO-682,该issue在1.5.0版本中进行了修复。而1.5.2版本中更新则是对其绕过的修复。
SHIRO-682的修复了spring框架下uri = uri + ‘/’ 绕过Shiro防护的问题。然后下面的描述则清晰得描述了造成改错误的原因。
在Spring web项目中,请求URI/resource/menus和/resource/menus/都可以访问到服务器的资源。
但在Shiro中的URL路径表达式pathPattern可以正确匹配/resource/menus,但不能正确匹配/resource/menus/,导致过滤链无法正确匹配,从而绕Shiro的防护机制。
shiro-721
Apache Shiro cookie中通过 AES-128-CBC 模式加密的rememberMe字段存在问题,用户可通过Padding Oracle 加密生成的攻击代码来构造恶意的rememberMe字段,并重新请求网站,进行反序列化攻击,最终导致任意代码执行。
影响版本:
Apache Shiro < 1.4.2
解决方案
升级shiro,升级到1.2.5版本及以上版本,但是可能升级了shiro版本仍然存在反序列化漏洞,其原因是因为我们使用了别人的开源框架,他们在代码里会配置shiro的密钥,如果使用了开源的框架,而没有修改shiro的密钥,其实这就相当于你使用的shiro密钥已经泄露
参考文章:
Apache Shiro权限绕过漏洞CVE-2020-1957