Skip to content

Commit

Permalink
Upload new file: 一个绕过 5 层权限校验的 0day 漏洞的代码审计分析.md via simpread
Browse files Browse the repository at this point in the history
  • Loading branch information
MrWQ committed Aug 30, 2024
1 parent bb635ca commit e3d38fe
Showing 1 changed file with 133 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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 个 **<init-param>** 标签定义的值,很明显有可能是白名单 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。

**<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>.png</url-pattern></servlet-mapping>**

**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 @)**

0 comments on commit e3d38fe

Please sign in to comment.