• 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/ 为例,一步步分析整个流程中的请求过程

    1. protected String getPathWithinApplication(ServletRequest request) {
    2. return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
    3. }
    4. public static String getPathWithinApplication(HttpServletRequest request) {
    5. String contextPath = getContextPath(request);
    6. String requestUri = getRequestUri(request);
    7. if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
    8. // Normal case: URI contains context path.
    9. String path = requestUri.substring(contextPath.length());
    10. return (StringUtils.hasText(path) ? path : "/");
    11. } else {
    12. // Special case: rather unusual.
    13. return requestUri;
    14. }
    15. }
    16. public static String getRequestUri(HttpServletRequest request) {
    17. String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
    18. if (uri == null) {
    19. uri = request.getRequestURI();
    20. }
    21. return normalize(decodeAndCleanUriString(request, uri));
    22. }

     

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

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

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

    1. private static String normalize(String path, boolean replaceBackSlash) {
    2. if (path == null)
    3. return null;
    4. // Create a place for the normalized path
    5. String normalized = path;
    6. if (replaceBackSlash && normalized.indexOf('\\') >= 0)
    7. normalized = normalized.replace('\\', '/');
    8. if (normalized.equals("/."))
    9. return "/";
    10. // Add a leading "/" if necessary
    11. if (!normalized.startsWith("/"))
    12. normalized = "/" + normalized;
    13. // Resolve occurrences of "//" in the normalized path
    14. while (true) {
    15. int index = normalized.indexOf("//");
    16. if (index < 0)
    17. break;
    18. normalized = normalized.substring(0, index) +
    19. normalized.substring(index + 1);
    20. }
    21. // Resolve occurrences of "/./" in the normalized path
    22. while (true) {
    23. int index = normalized.indexOf("/./");
    24. if (index < 0)
    25. break;
    26. normalized = normalized.substring(0, index) +
    27. normalized.substring(index + 2);
    28. }
    29. // Resolve occurrences of "/../" in the normalized path
    30. while (true) {
    31. int index = normalized.indexOf("/../");
    32. if (index < 0)
    33. break;
    34. if (index == 0)
    35. return (null); // Trying to go outside our context
    36. int index2 = normalized.lastIndexOf('/', index - 1);
    37. normalized = normalized.substring(0, index2) +
    38. normalized.substring(index + 3);
    39. }
    40. // Return the normalized path that we have completed
    41. return (normalized);
    42. }

            经过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/, 成功访问了后台请求
  • 相关阅读:
    抖音短视频矩阵系统搭建
    统计学习方法 感知机
    恭喜马斯克、纳德拉当选美国工程院院士,张宏江、方岱宁入选外籍院士
    基于AE的基础的GIS系统的开发
    【MySQL索引】MySQL索引失效的几种场景详解
    普通人怎么在一年内赚到100万?
    SAP OData 开发教程 - 从入门到提高(包含 SEGW, RAP 和 CDP)
    JSP智能小区物业管理系统
    【工程师整活】Ai-WB1-A1S实现离线语音+APP+天猫精灵控制风扇
    windows使用docker部署gitlab
  • 原文地址:https://blog.csdn.net/m0_61506558/article/details/126895701