问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
浅谈Spring Security授权规则配置错误漏洞(CVE-2023-34035)
渗透测试
Spring官方发布了CVE-2023-34035,当应用程序使用了 requestMatchers(String) 和多个 Servlet(其中一个是 Spring MVC 的 DispatcherServlet)的情况下,Spring Security漏洞版本存在可能受到授权规则配置错误的影响。
0x01 漏洞描述 ========= Spring官方发布了CVE-2023-34035,当应用程序使用了 `requestMatchers(String)` 和多个 Servlet(其中一个是 Spring MVC 的 DispatcherServlet)的情况下,Spring Security漏洞版本存在可能受到授权规则配置错误的影响。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-159880c8193876db601cb226248b1248a93ee285.png) 1.1 影响版本 -------- - Spring Security 5.8.0 to 5.8.4 - Spring Security 6.0.0 to 6.0.4 - Spring Security 6.1.0 to 6.1.1 1.2 利用条件 -------- - SpringMVC依赖存在于classpath - Spring Security在单个应用程序中对多个Servlet进行安全保护(其中一个是Spring MVC的DispatcherServlet) - 使用 requestMatchers(String) 来保护非Spring MVC端点 0x02 原理分析 ========= 以SpringSecurity 5.8.4为例,查看具体的原理。查看requestMatchers的具体实现: 首先根据是否存在SpringMVC,来选择不同的方式创建RequestMatcher。如果存在 SpringMVC的话,则创建MvcRequestMatcher,否则创建AntPathRequestMatcher: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-1fba2060d74007df2b667d5dd821656d3d3d5d96.png) 根据漏洞利用条件,可以知道这里创建的是MvcRequestMatcher。 在SpringSecurity中,会通过org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager#check 方法通过遍历请求匹配器列表,根据请求的 URL 和 HTTP 方法来决定哪个授权管理器应该处理该请求,并最终决定是否授予或拒绝对该请求的访问权限: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-724f5167667574fa9c00443dde330939dbc76105.png) 因为这里创建的是MvcRequestMatcher,所以直接查看org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher#matcher的具体实现。 首先接收request对象,然后调用 this.notMatchMethodOrServletPath(request) 方法来检查当前请求的 HTTP 方法和 Servlet 路径是否与预定义的条件匹配。如果不匹配,将返回 MatchResult.notMatch() 表示不匹配: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-2d56b02fad999baf924824d3a66899aaa7fdacbb.png) 否则调用`this.getMapping(request)` 方法获取请求的处理器映射对象(`MatchableHandlerMapping`)。如果不存在与当前请求相匹配的处理器映射,则调用 `this.defaultMatcher.matcher(request)` 方法,使用默认的请求匹配器来进行匹配,否则使用请求匹配器和预定义的 URL 模式来进行匹配,即调用 mapping.match(request, this.pattern) 方法: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-661fc813f8ea7735b8d0b466a0ce8e1c883f7935.png) 最后,根据匹配结果来返回相应的 MatchResult 对象。如果请求匹配成功,则返回 MatchResult.match(result.extractUriTemplateVariables()),其中 result.extractUriTemplateVariables() 用于提取匹配的路径变量。否则,返回 MatchResult.notMatch() 表示不匹配。 继续查看mapping.match(request, this.pattern) 的解析过程,这里会使用请求匹配器来进行请求匹配,得到匹配结果 `RequestMatchResult`: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-caa909930bfcf306c23f0d73457e9cc051e88711.png) 在match方法中,首先获取当前请求的路径,并将其表示为 `PathContainer` 对象,然后通过PathPattern的方式来匹配当前请求的路径: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-42603e9deadb955f17089634ec27e654e17bba99.png) 这里path的获取主要是从pathWithinApplication属性获取,主要是fullPath和contextPath属性决定的: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-4adc19a118854b602ae36453ced6a83ba768ec43.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-e7753962b189cd5ba0f37b0e22a301ec6e11dcaf.png) 在处理contextPath属性时,servlet跟SpringMVC endpoint直接会有差异。主要在ServletRequestPathUtils.ServletRequestPath.parse中: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-c1c35a957b79047f881c4f9c5928d7aa11296754.png) 整个解析的过程比较复杂,通过一个实际的例子来说明: ```Java @Controller @RequestMapping("/admin/") public class AdminController { @GetMapping("/*") public String Manage(){ /*return "Manage page";*/ return "manage"; } } ``` 默认情况下,当通过请求/admin/page解析上述Controller时,在调用getServletPathPrefix方法时返回为null,此时contextPath属性为"": ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-1c6274100ac952d67689295bc74b7f8a922630b5.png) 返回的当前请求的路径为/admin/page。 而对于自定义Servlet来说: ```Java @WebServlet(urlPatterns = "/admin/*") public class UserServlet extends HttpServlet{ private static final long serialVersionUID = 1L; @Override public void init() throws ServletException { } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(req.getContextPath()); resp.getWriter().write("user page"); return ; } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } @Override public void destroy() { } } ``` 当通过请求/admin/page解析时,在调用getServletPathPrefix方法时返回为admin,此时contextPath属性为admin: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-5e48785f9b2ac52be4b0057cc2aeef2a816efe19.png) 此时返回的当前请求的路径为/page: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-5e8db9d3e9bbfcaf7778ea015929c8b49fdb933d.png) 但是对于SpringSecurity来说,理论上requestMatchers("/admin/\*\*").hasRole("ADMIN")都应该能覆盖上述两个资源。根据前面的分析,在match方法中,在获取当前请求的路径后,通过PathPattern的方式来匹配当前请求的路径时servlet明显会获取不到,这里存在解析差异导致意料之外的结果。 0x03 漏洞复现 ========= 根据前面的分析,主要是MvcRequestMatcher对于自定义Servlet端点的解析存在差异,导致Authorization规则可能与预期不一致的问题。以SpringSecurity 5.8.4为例,下面看一个具体的例子: 假设当前配置类的安全规则如下,对于admin路径下的资源要求用户具有"ADMIN"权限,对于manage路径下的资源要求用户具有"MANAGE"权限,而对于其他任意请求路径,则直接放行: ```Java @Configuration @EnableWebSecurity public class SpringSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests().requestMatchers("/admin/**").hasRole("ADMIN").requestMatchers("/manage/**").hasRole("MANAGE").anyRequest().permitAll(); return http.build(); } } ``` 其中应用注册的资源如下,首先是SpringMVC的endpoint: ```Java @Controller public class ManageController { @GetMapping("/manage/page") public String Manage(){ /*return "Manage page";*/ return "manage"; } } ``` 然后是通过@WebServlet注解自定义的UserServlet,这里urlPatterns定义成了`/admin/*`,代表以 "/admin/" 开头的所有 URL 请求都会由这个 Servlet 来处理: ```Java @WebServlet(urlPatterns = "/admin/*") public class UserServlet extends HttpServlet{ private static final long serialVersionUID = 1L; @Override public void init() throws ServletException { } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("user page"); return ; } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } @Override public void destroy() { } } ``` 按照前面SpringSecurity的配置,正常情况下这两个资源在未授权的情况下访问,预期应该都会返回403 status。 - SpringMVC endpoint(符合预期) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-ea6c1df3d7e11e89f85085e5d06e750fdfd3af32.png) - 自定义Servlet(不符合预期) 可以看到这里对于`/admin/**`的防护,并没有生效: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-732e442af2a9cb33dd7b2c561312f552ca11607d.png) 0x04 修复方式 ========= 通过对比SpringSecurity 5.8.4与5.8.5,可以发现关键的修复代码主要在`org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry#requestMatchers`,该方法主要用于匹配对应的RequestMatcher。 - spring-security-config-5.8.4 这里主要是根据是否存在SpringMVC,来选择不同的方式创建RequestMatcher。如果存在 SpringMVC的话,则创建MvcRequestMatcher,否则创建AntPathRequestMatcher: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-fc051156561128ab3f4f4180df2f15f2cf9085bd.png) - spring-security-config-5.8.5 可以看到在5.8.5版本,对应的方法做了比较大的改动。首先检查是否存在SpringMVCmvcPresent 是否为 true以及应用程序是否使用了 WebApplicationContext,如果不满足条件会创建AntPathRequestMatcher。 如果存在SpringMVC并且使用了WebApplicationContext,则获取WebApplicationContext对象,并从中获取 ServletContext。如果 ServletContext 为null同样会创建AntPathRequestMatcher。 如果存在 ServletContext,则检查其中注册的 Servlet。如果不存在任何 Servlet 或没有 Spring MVC 的 DispatcherServlet,同样会创建AntPathRequestMatcher。 如果只有一个注册的 Servlet,并且是 Spring MVC 的 DispatcherServlet,那么将调用 this.createMvcMatchers(method, patterns) 方法创建 MVC 请求匹配器,也就是创建MvcRequestMatcher。如果包含多个Servlet的情况(registrations的size大于1),会抛出异常并打印相应的错误信息,对于非Spring MVC endpoint,请使用AntPathRequestMatcher进行对应的配置: ```Java This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use `requestMatchers(MvcRequestMatcher)`; otherwise, please use `requestMatchers(AntPathRequestMatcher)`. ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-afa48c8024fa418957f94ed42b1eebeddc844da7.png) 查看具体的效果,同样是上面的例子,此时registrations的size大于1,会抛出对应的异常: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-7c156da95e7e4f20c956953187199341522f46d7.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-4ad4829dc48c65b9a07cfc1d06e465369c0dba31.png) 同样是上面的案例,当使用AntPathRequestMatcher处理/admin/\*\*请求后,自定义的Servlet安全防护符合预期: ```Java @Configuration @EnableWebSecurity public class SpringSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests().requestMatchers(new AntPathRequestMatcher("/admin/**")).hasRole("ADMIN").requestMatchers("/manage/**").hasRole("MANAGE").anyRequest().permitAll(); return http.build(); } } ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/07/attach-e302a86263ef39e4e84c8d2ae6de53a16da6117e.png)
发表于 2023-08-11 09:00:01
阅读 ( 8005 )
分类:
漏洞分析
1 推荐
收藏
0 条评论
请先
登录
后评论
tkswifty
64 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!