-
Notifications
You must be signed in to change notification settings - Fork 415
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Upload new file: 一个绕过 5 层权限校验的 0day 漏洞的代码审计分析.md via simpread
- Loading branch information
Showing
1 changed file
with
133 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 @)** |