diff --git "a/bugs/\344\270\200\344\270\252\347\273\225\350\277\207 5 \345\261\202\346\235\203\351\231\220\346\240\241\351\252\214\347\232\204 0day \346\274\217\346\264\236\347\232\204\344\273\243\347\240\201\345\256\241\350\256\241\345\210\206\346\236\220.md" "b/bugs/\344\270\200\344\270\252\347\273\225\350\277\207 5 \345\261\202\346\235\203\351\231\220\346\240\241\351\252\214\347\232\204 0day \346\274\217\346\264\236\347\232\204\344\273\243\347\240\201\345\256\241\350\256\241\345\210\206\346\236\220.md" new file mode 100644 index 000000000..5ef00f37b --- /dev/null +++ "b/bugs/\344\270\200\344\270\252\347\273\225\350\277\207 5 \345\261\202\346\235\203\351\231\220\346\240\241\351\252\214\347\232\204 0day \346\274\217\346\264\236\347\232\204\344\273\243\347\240\201\345\256\241\350\256\241\345\210\206\346\236\220.md" @@ -0,0 +1,133 @@ +> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [mp.weixin.qq.com](https://mp.weixin.qq.com/s/THIyZsYWDQwFH5dZ1s9WsQ) + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450ATcz6jUJnFNeOxRzVZ9LbcCCMJ6Af2WYicgMPA32IwibF8mI2ibC9h8jaHkhxnZzZuqctMLRTxDudicA/640?wx_fmt=png) + + **Part1 前言** + +**大家好,我是 ABC_123**。这是我的第 101 篇原创技术文章,好久没有给大家分享代码审计类的分析了。前一阵子网友发来一个从流量设备中抓到的 0day 漏洞,不理解其绕过 **CAS 单点登录及 Shiro 组件的权限校验**的原理,我也好奇分析了一下,不禁感叹这位漏洞挖掘者的决心和毅力,各种因素结合完美绕过 5 层的权限校验。 + +**建议大家把公众号 “希潭实验室” 设为星标,同时关注我的视频号 "希水涵一讲堂"。** + +![](https://mmbiz.qpic.cn/mmbiz_jpg/OAz0RNU450Dq1Q8s4COc7InkMO0jIGjiaGho1fcJicpibWB4vzvIM1wAib9TiakVECbIM5S0mHCTTeGJJibWtCe25vXw/640?wx_fmt=other&from=appmsg&wxfrom=5&wx_lazy=1&wx_co=1&tp=webp) + + **Part2 技术研究过程** +------------------ + +这里给出这个 POC 的权限校验的关键部分,URL 及参数都做了模糊化处理,大家可以先思考一下: + +POST /file/changeUpload.png + +VersionMetadata:clientCategory + +Host: xxxxxxx + +* **第 1 关:URL 后缀. png 的路由映射问题** + ----------------------------- + + +这个 POC 我一看就有疑问,.png 这个 url 能在 SpringMVC 中正常执行 java 应用吗?首先看一下 web.xml 配置文件,通过 mvc-dispatchcer 得知,该 web 应用是 SpringMVC 应用。发现该 web 应用未对. png、.gif、.css 等静态文件配置默认的 servlet 处理类。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS94JAKGBG4KGweibgfPUFpnexomWOl7wJAkqiclB2z3I9mqPRpSxax4HFQ/640?wx_fmt=png&from=appmsg) + +所以当访问 /file/changeUpload.png 时,这个 URL 首先会被解析为 /changeUpload,然后是 .png 扩展名。@RequestMapping 注解用来定义控制器应该处理哪些 URL,但是如果没有指定请求方法或扩展名,那么这个方法将处理所有以 /changeUpload 开头的 URL,无论它们是否有扩展名。**由于. png 没有被明确映射到其它 servlet,且 /base64FileUpload 部分与根路径 / 匹配,因此由 mvc-dispatcher 将处理这个请求**。 + +* **第 2 关:程序本身的权限校验绕过** + --------------------- + + +接下来看下一个权限校验的 filter,这个 filter 是 Web 应用程序自己写的,注意看 2 个 **** 标签定义的值,很明显有可能是白名单 url 关键字。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450C5iamTSCEbYmmtd2fibB2Ct1o8v3Jvecgiaz6owBbFl9nmCYB5ib3ZM9nbXtdZ600fm0LeWbxXZQficYw/640?wx_fmt=png&from=appmsg) + +跟入相关的 filter 类,查看 doFilter 方法的实现,经过分析,isIgnoreUrl(httpRequest) 方法是关键。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9X8Osn5o6ZR7vVjTT8akibOkJjsQ3NVYULLKEGFU3d39fU5yEcHBIaRw/640?wx_fmt=png&from=appmsg) + +分析如下代码,在请求头中添加 VersionMetadata:clientCategory 可以使 isIgnoreUrl 条件判断返回 true,则上述 fitler 代码会直接跳到 **chain.doFilter(request, response);** 这段代码,将请求和响应传递到下一个 filter 过滤器。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9RZ63VsFqItyXuBEZiaQGrdeWA8LPN193j6KDP2Hsmibubxf1hO3hJGlQ/640?wx_fmt=png&from=appmsg) + +* **第 3 关:CAS 权限校验绕过** + -------------------- + + +接下来看一个 filter 类,来到了 CAS 单点登录的权限校验过程,跟进相关 filter 的类进行分析。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9KgS5SeLyI0slkGJGBvL7GqJU4icXHHzVePlhGSuzicCJDvq7lkPicpI3g/640?wx_fmt=png&from=appmsg) + +经过分析,这一大串 if 条件非常复杂,将其复制出来放到本地 idea 中调试一下,发现只要 http 请求头包括 VersionMetadata:clientCategory 的时候,该 if 条件包裹的一堆权限校验代码直接全部跳过。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9eheCpPbNLcqxDsZ3tANgjz4meu9Bicr3xQFoHRQn5iaLibrqUZKicm75hw/640?wx_fmt=png&from=appmsg) + +跳过 if 权限校验语句之后,程序会执行 filterChain.doFilter(request, response); 代码,从而传递请求去下一个 filter 类进行权限校验,至此就完美绕过了 CAS 的单点登录校验。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9U2neVVOBREjHH2cbthfP2KYJRaKjfdyHRdZcfwfefgWvreLMGbLc4A/640?wx_fmt=png&from=appmsg) + +* **第 4 关:Shiro 组件第一道权限校验** + ------------------------- + + +接下来 filterChain.doFilter 会将 request 对象及 response 对象传递到如下这个 Shiro 组件的 shiroFilter 过滤器。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9e3k8KxmiavuLp6T5dCjBwuesN8EbXRXL7zUhwiczVKpX6nOIDxUSgByA/640?wx_fmt=png&from=appmsg) + +接下来从 **applicationContext-shiro.xml** 中找到了 shiro 组件的关于权限方面的配置文件,里面配置了很多 url 路由及权限校验的实现类。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9G0qgk02dxfBapT0CB4l3Pq3snsZXpibuIKFTBUsiaNvm4LZL83QGWvLQ/640?wx_fmt=png&from=appmsg) + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450C5iamTSCEbYmmtd2fibB2Ct1PgRrE1DLnapicnkia6SPwcPBNQuyTibNuttZ67UyQnWSBEJZ6WMxaA65w/640?wx_fmt=png&from=appmsg) + +查看 shiro 的安全过滤链配置项 **filterChainDefinitions**,发现如下关键代码:**/**** 表示匹配所有 url 路由,指定了两个自定义的过滤器来处理 http 请求,用作权限校验,这两个过滤器分别是 LoginAuthFilter、xxxStatelessAuthcFilter。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450C5iamTSCEbYmmtd2fibB2Ct1mstof1HYIpAlcyC84M20Tias415aUKKGZtMbiaFMmRSQich5snQZcBseQ/640?wx_fmt=png&from=appmsg) + +首先分析第 1 个 LoginAuthFilter 过滤器,代码如下: + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9BvdjEEKUgZeCHUnt7B5zdpXl8S7nVzoqWoko0q0y18mzgv1C1997Wg/640?wx_fmt=png&from=appmsg) + +经过一系列的 if 条件判断,发现上述 EXP 的上传数据包都不符合这些 if 条件判断,因而权限判断会走到最后一步,默认返回为 true,表示通过了第一个过滤器的权限校验。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9lksWSZN1HBGFHicAyNnSUpQDnM9D8aKgFW8Kpxl0nSmlITwDMSVkWjw/640?wx_fmt=png&from=appmsg) + +* **第 5 关:shiro 组件的第 2 道权限校验** + ---------------------------- + + +接下来看 shiro 组件的第二个权限校验过滤器 xxxStatelessAuthcFilter,它的实现类是 com.xxx.shiro.filter.StatelessAuthcFilterXX。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9WaHFvBj4rAxxqhlcIrV3WzhkZyTChPVnUlzDhCicUeGPIiaXrX7Uibyqw/640?wx_fmt=png&from=appmsg) + +接下来查看这个类的具体实现,主要查看 **onAccessDenied** 方法,如果返回 true,则表示当前用户被允许访问请求的资源。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9Y0MLLyyrHDzVxoa93VtG9WFhj6ibt1T3UYSeIqpMOpCVYRvsSGmSInQ/640?wx_fmt=png&from=appmsg) + +这个类代码特别繁杂,我跟了一晚上也没发现这个 exp 是怎么通过这个类的权限校验的。在即将放弃的时候,发现了这段代码 super.include(hReq),由于代码很短,我跟了好几次都没仔细看,后来才发现,这里才是权限校验绕过的关键点。这小段代码调用了父类的 include 方法,并传递一个 request 对象。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS97DrYrYk9aB37AWTWXUDF1kP2ZNpuNXkQW3OPzW1thJgfcR64fwLkAQ/640?wx_fmt=png&from=appmsg) + +如下代码通过遍历 this.esc 的方式实现了对 URL 扩展名的校验。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9LlyagcDDKAicwE7zFBla4sQtb5TviashuiahwvxbZYygZ9icwgEibx9FtDw/640?wx_fmt=png&from=appmsg) + +this.esc 内容如下,定义了权限校验的白名单的扩展名:.jpg、.png、.gif、.css、.js、.jpeg,这几个扩展名都是用户登录校验的。跟到这里就非常明显了,**/file/changeUpload.png 的 url 请求中的. png 符合权限校验的白名单,onAccessDenied 返回为 true,所以权限校验通过**,无需用户名密码登录。至此,作者精心构造的 EXP 完全绕过 5 层权限校验。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450BDfePgS7owIdX4wvpR2OS9PiaajEhwia8D7quvrGHqYTiaGlTd5F9cqYmaENtysfXiahIodLdw76QHcg/640?wx_fmt=png&from=appmsg) + + **Part3 总结** +============== + +**1.**  解决. png 路由访问的问题:可以在 @RequestMapping 注解中指定请求方法和扩展名,或者在 web.xml 中添加更具体的 servlet-mapping 来处理特定的扩展名。例如,可以添加一个 servlet-mapping 来处理所有 .png 文件。这将确保所有 .png 文件由默认的 servlet 处理,而不是 mvc-dispatcher。 + +** default .png** + +**2.**  权限校验方面,对于白名单要慎之又慎。 + +**3.**  后续 ABC_123 会继续给大家分享 java 代码审计的权限校验方案的安全问题。 + +![](https://mmbiz.qpic.cn/mmbiz_png/OAz0RNU450A5qqg2iaK6KIYYR8y6pF5Rh3JHDibOKOop204nXz618iawdRb8dABicMPtHb2PkJE8x6koJO5HyuwZJQ/640?wx_fmt=other&wxfrom=5&wx_lazy=1&wx_co=1&tp=webp) + +**公众号专注于网络安全技术,包括安全咨询、APT 事件分析、红队攻防、蓝队分析、渗透测试、代码审计等,99% 原创,敬请关注。** + +**Contact me: 0day123abc#gmail.com** + +**(replace # with @)** \ No newline at end of file