# CVE-2026-41844 Spring Framework 开放重定向漏洞浅析 > QIANXIN Team > 来源:https://forum.butian.net/share/4951 # 0x00 CVE-2026-41844 ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-7aff2d3e51062d3e9e80e4782993df689e9f780d.png) **主要影响范围:** Spring Framework: - 7.0.0 - 7.0.7 - 6.2.0 - 6.2.18 - 6.1.0 - 6.1.27 - 5.3.0 - 5.3.48 以及不再支持维护的版本同样受到影响。 # 0x01 漏洞分析与复现 ## 1.1 分析过程 以spring-webmvc 6.0.7为例。 当Spring MVC接收到请求时,Servlet容器会调用DispatcherServlet的service方法(方法的实现在其父类FrameworkServlet中定义): ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-7a6ba78d145096ca19d0db32a7a1f01aa2e8838c.png) 经过一系列的处理后,会调用doDispatch方法处理: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-71d0c79c4f4784f9079b3da74df6dbe0009daa50.png) 在doDispatch方法中,经过一系列处理获取到url 和 Handler 映射关系后(HandlerAdapter),springMVC就可以根据请求的uri来找到对应的Controller和method,然后处理和响应请求。详细的分析可见[https://forum.butian.net/share/2214](https://forum.butian.net/share/2214) ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-9e259a96ec119b855f92b25d5a18c5a907a688fb.png) 找到对应的HandlerAdapter后,会调用对应的Handler方法,也就是执行Controller里的业务逻辑了,执行完成之后会返回一个ModleAndView对象,然后渲染视图并进行返回,这里的逻辑是本次漏洞的分析的关键: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-4e164661dc717221e5ba9e65bee6bcbd9f69c78f.png) 在获取到ModelAndView对象后,这里会调用applyDefaultViewName处理,这里是Spring 的默认视图解析逻辑: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-58c3dd615e1cf07bbb5dd51174fddb0de5e1b5f9.png) 查看具体的代码逻辑,首先检查 mv 是否为 null(如果代码添加了 `@ResponseBody` 注解,mv 就为 null),然后判断 mv 中是否包含视图,如果对应的Controller代码中未显式指定视图名时,则调用 getDefaultViewName 方法去获取默认的视图名,并将获取到的默认视图名赋值给 mv: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-d7d6ddf0a277d092e9bea01c93c20074d9959680.png) 查看getDefaultViewName,看看具体获取默认视图名的逻辑: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-8665985be7d37bcdd595bf1649a76bb93d873e1e.png) 这里会继续调用viewNameTranslator#getViewName方法对请求进行处理。 viewNameTranslator 其实是 RequestToViewNameTranslator: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-cc432e55fa31276957246359535910482e376bbf.png) ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-5af491a5a2a1957664273a91e18d4c5bc9b7bfe2.png) 本质上,在 SpringMVC 中,RequestToViewNameTranslator 接口只有一个默认的实现类DefaultRequestToViewNameTranslator,WebFlux 则是ViewResolutionResultHandler 。 在DefaultRequestToViewNameTranslator#getViewName方法中,**会从请求路径中直接提取字符串作为视图名**: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-cc9a702a17b6ab267cccef0cc06411855c60952f.png) ServletRequestPathUtils#getCachedPathValue方法是统一获取请求路径的方法。具体的解析可见[https://forum.butian.net/share/2606。](https://forum.butian.net/share/2606%E3%80%82) 提取完请求路径后,会通过 transformPath 方法对路径进行处理,再分别加上前后缀后返回,默认的前后缀都是空字符串(如有需要,也可以进行配置)。 transformPath 方法则主要功能如下: 1. 去掉路径开始的 `/` 2. 去掉路径结尾的 `/` 3. 如果请求路径有扩展名,则去掉扩展名,例如请求路径是 `/1.txt`,经过这一步处理后,就变成了 `/1` ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-cabe7d1f2f688feb30ce83e602883b60633d4017.png) ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-b569289a69ddddbbb63b69d4088baeb90dcb381f.png) 4. 如果 separator 与 SLASH 不同,则替换原来的分隔符(一般情况下,默认是相同的)。 ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-9a8dd48be68c7b588415fbcdf9a163aff861b552.png) 这里以如下Controller为例: ```java @GetMapping("/**") public void catchAll() { } ``` 当正常请求/demo时,可以看到,因为没有显示指定视图,所以ModelAndView对象里的内容均为null: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-885d2da684b8f5a4ce13decea316679d13a992bb.png) 经过applyDefaultViewName方法处理后,取得对应的视图名,也就是默认的请求路径demo: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-7d86b67bbbe0dad2687f3a3717aaba56ee4c7cd5.png) 这里也就得到了漏洞的第一个成因:**当 Spring MVC 或 Spring WebFlux 应用未显式指定视图名时,视图名会默认从请求路径提取。** 处理完上述的一系列逻辑后,会调用processDispatchResult()进行异常处理、请求状态及触发请求完成事件,视图的渲染工作则交给了render()方法: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-c36c4f4dd4039691a506504fad537eb00ce93beb.png) ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-63fe84eca740d397c9f3eb8b3e10b0be961c3c7d.png) render()渲染过程中,如果ModelAndView中的viewName不为空,则调用resolveViewName从视图解析器获取对应的视图对象;否则使用ModelAndView#getview方法获取视图对象。 ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-47486c2f5fd2e80dead1e2c717ceef4e478c414c.png) 这里会进一步调用getCandidateViews方法进行处理,然后遍历所有的视图解析器,进行视图的创建与解析: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-059fe15e5edc2d21ce0d817a38fb4e2862bd78c4.png) ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-a8dcc54516ca04b8fb24d08c26fdc02eeb8bbbc2.png) 而UrlBasedViewResolver会识别视图名中的特殊前缀,例如`redirect:` 会被解析为重定向,返回 302 响应跳转到后续指定的地址: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-883fb90a08d3b922e3bab52d123f338e124d376d.png) 至此,CVE-2026-41844的完整链路大致梳理清楚了。下面是具体的漏洞复现过程。 ## 1.2 漏洞复现 以SpringMVC为例: 相关环境直接使用[https://github.com/andbin/spring-boot3-thymeleaf-basic-demo](https://github.com/andbin/spring-boot3-thymeleaf-basic-demo) 进行验证。 根据前面的分析,可以定义一个Controller如下: ```TypeScript @Controller public class MyController { @GetMapping("/**") public void catchAll() { } } ``` 启动对应的application后,只需访问`http://ip:port/redirect:https://www.attack.com.any`,通过302跳转后即可重定向到`https://www.attack.com` 注意,根据前面的分析,在通过请求获取默认视图时,如果请求路径有扩展名,则去掉扩展名,这个拓展名会通过结尾最后一个`.`来区分。也就是说类似`www.attack.com`会被处理成`www.attack`,所以在最后构造poc时,要在实际跳转的域名后,增加类似`.any`的后缀,避免截断后无法正常跳转。 下面是具体的效果: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-b5e7b1741dde33d857d6b27e8de8c321f5efee63.png) forward的转发同理,先定义一个转发的目标: ```TypeScript @GetMapping("/internal/secret") @ResponseBody public String internalEndpoint() { return "[敏感信息] 内部系统配置接口,仅内网可访问"; } ``` 只需请求`http://ip:port/forward:/internal/secret`即可成功转发: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-ed9f63a43f4960e31a4fa7b0adeaf4fae9b0cd48.png) Spring WebFlux同理,这里就不再赘述了。 另外,WebFlux 原生不支持 `forward:` 内部转发,因此仅受 `redirect:` 开放重定向影响。 ## 1.3 {\*spring}模式 在官方的漏洞通告中,仅仅提到了`/**`的场景,实际上Spring5及之后的PathPattern解析模式,还支持类似`{*spring}`的写法,与`/**`大同小异,那么是否也会存在风险呢? 查看官方文档: Representation of a parsed path pattern. Includes a chain of path elements for fast matching and accumulates computed state for quick comparison of patterns. `PathPattern` matches URL paths using the following rules: - `?` matches one character - `*` matches zero or more characters within a path segment - `**` matches zero or more _path segments_ until the end of the path - `{spring}` matches a _path segment_ and captures it as a variable named "spring" - `{spring:[a-z]+}` matches the regexp `[a-z]+` as a path variable named "spring" - `{*spring}` matches zero or more _path segments_ until the end of the path and captures it as a variable named "spring" **Note:** In contrast to `[AntPathMatcher](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html)`, `**` is supported only at the end of a pattern. For example, `/pages/{}` is valid but `/pages/{}/details` is not. The same applies also to the capturing variant `{*spring}`. The aim is to eliminate ambiguity when comparing patterns for specificity. 根据官方文档的描述,其实`**`跟AntPathMatcher匹配规则区别不大,PathPattern在保持其匹配规则的基础上,新增了`{*spring}`的语法支持。 `{*spring}`表示匹配余下的path路径部分并将其赋值给名为spring的变量(变量名可以根据实际情况随意命名,与`@PathVariable`名称对应即可)。同时,`{*spring}`是可以匹配剩余所有path的,类似`/**`,只是功能更强,可以获取到这部分动态匹配到的内容。 ```TypeScript @GetMapping("/{*path}") public void catchAll() { } ``` 可以发现,同样成功利用,这里与`/**`的配置区别只是解析模式的不同,不影响具体的漏洞触发: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-e01c770706eb665d265e0ca566f554beeb8810ad.png) # 0x02 利用条件 综上所述,相关漏洞的利用条件可以总结如下: 1. 使用 Spring MVC 或 Spring WebFlux 框架 2. 配置了类似 `/**` 通配符路径与`{*path}`映射(如全局视图路由) 3. 对应路径的处理器未显式指定视图名称,依赖 Spring 自动从请求路径推导 4. 使用了继承自 `UrlBasedViewResolver` 的视图解析器(Thymeleaf等主流视图技术默认均满足) # 0x03 修复方案 官方具体修复如下:[https://github.com/spring-projects/spring-framework/blob/v6.2.19/spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultRequestToViewNameTranslator.java](https://github.com/spring-projects/spring-framework/blob/v6.2.19/spring-webmvc/src/main/java/org/springframework/web/servlet/view/DefaultRequestToViewNameTranslator.java) 通过请求获取完视图名后,新增对 `redirect:`、`forward:` 两个危险前缀的开头匹配校验。当匹配到危险前缀时直接抛出非法参数异常,由 Spring MVC 异常处理器转为 `400 Bad Request` 响应: ![image.png](https://cdn-yg-zzbm.yun.qianxin.com/attack-forum/2026/07/attach-ca608ed0990abedcfab14ae5c7667b9cea773cff.png)