此篇为上一篇分析文章的后续,如果对上一篇感兴趣,也可以去看一下,或许会对你理解本次漏洞有点帮助。
0x01 漏洞简介
Apache Shiro作为常用的Java安全框架,拥有执行身份验证、授权、密码和会话管理等功能,可以和不同的框架进进行整合,比如spingboot、spring等等,使用十分方便。本次漏洞是因为shiro和springboot之间对请求的解析差异导致的绕过。算是shiro漏洞分析二步曲。
0x02 环境搭建
环境我放在github上了,地址:https://github.com/apwgss/Apache-Shiro-one.git,需要略作修改,将pom.xml中shiro-spring-boot-web-starter依赖的版本修改为1.5.2刷新即可。
0x03 漏洞分析
在漏洞分析之前,我要先通过一个小案例,让大家更好的理解request对象的getServletPath和getPathInfo方法的作用和区别。
我本地搭建了一个环境,web,xml配置如图
主要配置了两个东西,一个是servlet,url-pattern地址为/test/,还有一个是filter,url-pattern地址为/,拦截所有请求。
配置这两个类,主要是为了帮助理解getServletPath方法的一些小差异。来看一下TestFilter文件的内容,如图
可以看到只是简单的转换了request对象,然后输出了servletPath和pathInfo,将程序运行起来后访问:localhost:8081/springmvc_day02_04_interceptor_war/test/1234(springmvc_day02_04_interceptor_war为项目路径,不用在意),控制台输出如图
可以看到servletPath为/test而pathInfo为/1234,当我们修改test为test1,再次访问如图
可以看到servletPath为/test1/1234而pathInfo为null,看到这里是不是感觉有点奇怪,为什么pathInfo的值为null了。其实这里就是我要讲的一个知识点,只有在配置中配置了servlet的值的时候,在其后面的路径才能被getPathInfo方法获取到,比如这里的servlet路径为/test/,那么当我访问/test/hello是,pathInfo就是/hello也就是代表的位置。
然后还有一个小tips,就是当我访问localhost:8081/springmvc_day02_04_interceptor_war/testaa/;aaa/bbb的时候,看下控制台的输出如图
可以看到分号和其后面的内容被截断了,所以我们是无法直接用分号的,这里也和后面要讲的漏洞分析有关。
好了,下面开始正式漏洞分析了,我们先来看一下新版本使用上一个版本的payload的执行结果,如图
可以看到跳转到了登录页面,说明上一个版本的payload不好使了。
我们来看一下源码上的区别,先看旧版本的org.apache.shiro.web.util.WebUtils#getRequestUri方法
再来看一下新版本的方法
定睛一看,应该都能看出区别在哪,很明显新版本对获取uri的方法做了修复,由原来的request.getRequestURI() 变成了 uri = valueOrEmpty(request.getContextPath()) + “/” + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo()),上一个版本之所以可以绕过shiro的权限校验,就是因为它在获取uri的时候,获取了完整的uri,从而导致的一系列绕过。
而新版本弥补了上一个缺陷,使用了request.getServletPath()来获取请求的uri,在上面我们讲过,如果使用分号的话,就会被直接剔除,看一下效果图
可以看到获取后的uri变成了//admin/test,所以导致后面无法通过检测。
那么我现在来问个问题,如果让你们不去看漏洞payload,独立自己分析,你会怎么绕过?
答案可能会因人而异吧,我是在不看payload的情况下,分析了一番,找到了绕过方法,我也建议初学者先自己尝试一下绕过,可能会有所收获哦。
回到正题,我们先拿旧的payload跑一下,看一下新的处理流程是怎么样的。由于上一个漏洞分析分析的比较详细,所以我们这次直接分重点位置。进入getRequestUri方法,如图
跟进decodeAndCleanUriString方法如图
可以看到和之前的版本没区别,先是url解码了一下,然后进行了字符串截断,然后返回uri,这里因为没有分号所以还是//admin/test。
回到getChain方法,如图
可以发现这里比旧版本多了两块内容,就是我圈出来的,我们主要关注上面那个圈。它是判断如果requestURI不为null同时不为 / 还有以 / 结尾的时候,就会将尾部的 / 给截取掉,这里是一个比较关键的信息点,首先看到这里第一个想法就是既然你要截取最后一个 / ,那么我就让你截取,给个/admin/试一下,如图
可以看到处理后的效果,这样就可以绕过shiro的判断了,因为我当时authMap中存储的是/admin/*,它匹配的是/admin/xxx这种类型的uri,而无法匹配/admin这样的uri,所以就饶过了shiro的权限判断。但是!真的可以访问到目标资源了吗?答案是NO,如图
可以看到虽然绕过了shiro,但是这样访问,狗屁资源都访问不到,并不能达到我们的目的,访问受保护的资源。
所以我们因该改一下思路,再想想还有什么办法能够既绕过了shiro,又能访问到资源。经过一番测试和思考,我想到了一个办法,回顾shiro的解析过程,有一个环节也可以利用上,就是url解码那里,shiro会对请求的uri进行一次url解码,然后对分号进行截取,截取分号前面的内容,这么一想,聪明的你应该也有想法了吧。
当然,我也是经过了一些其他尝试,不想文章拖太长,就写上最终的思想。已知我们要访问的资源路径如图
只要我们能够在/admin/ 后面加上任意字符,并且饶过shiro,那么不就可以访问到资源了吗。所以我想到的是/admin/%3b,这是因为shiro的处理过程,它会先对uri进行url解码和截取分号前部分内容,如图
可以看到经过处理后的uri变成了/admin/,然后我们放行到org.springframework.web.util.UrlPathHelper#getPathWithinServletMapping方法,如图
可以看到servletPath为/admin/;,这里的分号没有被处理是因为,服务器是在获取之后自动解码了一次,而不是直接以分号请求,所以分号被保留了,然后再放行,如图
可以看到成功跳转到资源控制器,到这里就说明成功了,漏洞分析也就到此结束啦。
0x04 修复建议
升级到最新版本,目前最新是1.7.1。
免责声明:本站提供安全工具、程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负!
转载声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
订阅查看更多复现文章、学习笔记
公众号:伟盾网络安全
专注网络安全,用心做好安全这件事。
个人博客:博客
个人知乎:知乎