CVE-2020-1957 Apache Shiro 认证绕过漏洞

目录

(一)Apache Shiro介绍

 1、漏洞原理分析

(二)漏洞复现

 1、查看环境

2、进入页面

 3、BP抓包

(三)原理分析

总结:


        由于笔者个人水平有限,行文如有不当,还请各位师傅评论指正,非常感谢

(一)Apache Shiro介绍


        Apache Shiro是一款开源安全框架,它的功能主要用于身份验证、授权、会话管理、加密......漏洞发生原因是:登入的时候序列化保存了登入信息到cookie(序列化、AES加密、Base64)

 1、漏洞原理分析


        Spring Boot中使用 Apache Shiro 进行身份验证、权限控制时,利用 Apache Shiro 和 Spring Boot 对URL的处理的不同,实现越权访问。
比如/xxx/..;/admin/这个路径,在shiro看到“;”分号后,就会进行截断,校验分号前面路径/xxx/..这个路径并没有包含admin/**于是校验通过。Spring Boot看到此路径后,会直接取有效路径/admin/于是就访问成功了。

        在shiro的1.5.1及其之前的版本都可以完美地绕过权限检验:

(二)漏洞复现


        我这里用的是vulhub的shiro-2020,docker-compose up -d 启动

 1、查看环境

docker-compose ps

 

2、进入页面

ifconfig查看ip后,加上端口即可访问。

 3、BP抓包

 将url改为admin会显示302跳转到登录界面

 将url改为/xxx/..;/admin/后绕过了登录

 关闭环境:docker-compose down

(三)原理分析


以/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));
    }

 

        此时的URL还是我们传入的原始URL:/xxx/..;/admin/接着,程序会进入到decodeAndCleanUriString(), 得到:

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

        decodeAndCleanUriString 以 ;截断后面的请求,所以此时返回的就是/xxx/.. 然后程序调用normalize() 对decodeAndCleanUriString()处理得到的路径进行标准化处理,都是一些很常见的标准化方法.

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/... 最终会进入到 org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver 中的 getChain()方法会URL校验. 关键的校验方法如下:

         由于/xxx/.... 并不会匹配到 /admin/** 所以shiro权限校验就会通过.

        最终我们的原始请求/xxx/..;/admin/ 就会进入到 springboot中. springboot对于每一个进入的request请求也会有自己的处理方式,找到自己所对应的mapping. 具体的匹配方式是在:org.springframework.web.util.UrlPathHelper 中的 getPathWithinServletMapping()

                 getPathWithinServletMapping() 在一般情况下返回的就是 servletPath, 所以本例中返回的就是 /admin/.最终到了/admin/对应的requestMapping, 如此就成功地访问了后台请求.

Download Apache Shiro | Apache Shiro修补方案

总结:

  1. 客户端请求URL: /xxx/…;/admin/
  2. Shrio 内部处理得到校验URL为 /xxxx/…;校验通过
  3. SpringBoot 处理 /xxx/…;/admin/ , 最终请求 /admin/, 成功访问了后台请求
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@Camelus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值