基于路由转发导致的权限认证绕过漏洞分析
基于路由转发导致的权限认证绕过漏洞分析
原创 Mmuz 奇安信天工实验室 2025-05-21 03:31
目
录
一、前 言
二、路由转发导致权限认证绕过
三、漏洞分析
四、总 结
一
前 言
在 Web 应用程序的开发过程中,安全问题始终是至关重要的。权限认证作为保障系统安全的重要防线,能够确保只有经过授权的用户才能访问特定的资源。然而,由于开发人员的疏忽或者对某些技术细节的理解不够深入,可能会引发一些安全漏洞,导致权限认证机制被绕过。
本文将介绍一种基于转发机制的权限认证绕过漏洞模式。在实际的 Web 应用中,常常会使用过滤器
(Filter
)
或者Authenticator
做权限认证。但当应用中存在一些可以未授权访问的代码允许用户通过请求参数指定转发的目标 URL 时,就可能会出现权限认证被绕过的情况。这种漏洞可能会使未授权的用户访问到本应受保护的资源,从而对系统的安全性造成严重威胁。接下来,我们将详细探讨这种漏洞模式的具体细节。
二
路由转发导致权限认证绕过
01
Filter机制
Java Filter
是一个实现了
javax.Servlet.Filter
接口的 Java 类,它可以对客户端的请求进行拦截,在请求到达目标资源(如
Servlet
、
JSP
等)之前进行一些预处理操作,也可以在服务器返回响应给客户端之前对响应进行后处理操作。常用在权限认证 、字符编码处理、日志记录等场景。
一般在
web.xml
中配置如下:
关键点在于
dispatcher
选项,作用是指定过滤器的触发条件(即请求的转发类型)。
可选值
如下:
– REQUEST
(默认配置):当客户端直接发送请求时触发过滤器。
- FORWARD
:当请求被转发(使用
RequestDispatcher#forward
方法)时触发过滤器。
-
INCLUDE
:当请求被包含(使用
RequestDispatcher#include
方法)时触发过滤器。 -
ASYNC
:当异步请求触发时(用于
Servlet 3.0+
的异步处理场景)。 -
ERROR
:当发生错误并进入错误页面时触发过滤器。
02
Authenticator机制
Authenticator
不是
Servlet
标准,是Tomcat 和
Jetty
各自私有实现中的一个概念,用于实现
Servlet
容器的认证机制。
Servlet
容器一般会根据
web.xml
中的配置(
login-config
和
security-constraint
)来决定何时对请求进行认证。
认证方式可以自定义,也可以使用自带的认证方式:
– BASIC:浏览器弹出用户名密码框,使用
HTTP Basic
认证头。
-
FORM:自定义表单登录页,用户名密码提交到服务器。
-
DIGEST:
HTTP Digest
认证(不常用)。 -
CLIENT-CERT:基于客户端证书的
SSL
认证。
如下是个简单的配置例子,限制了
/admin/*
资源必须先通过
FORM
认证:
Tomcat中的
Authenticator
都继承
AuthenticatorBase
实现
org.apache.catalina.Authenticator
接口,以
valve
(阀门)的形式存在于
Pipeline
(管道)中。每一层容器中都有自己的
Pipeline
,它将一系列的
Valve
接在一起,形成一个链式调用。当请求到达顶层容器
Engine
后,沿着
Engine → Host → Context → Wrapper
传递,进入某层容器时该容器的
Pipeline
中的
Valve
就会依次被调用对请求和响应进行特定的处理。
FormAuthenticator
属于
context
层的
valve
。当请求到达
Authenticator
时就会在
AuthenticatorBase
根据配置决定是否需要认证。
Jetty
中的
Authenticator
都继承
LoginAuthenticator
实现
org.eclipse.Jetty.security.Authenticator
接口。启动时解析
web.xml
中
或者代码中的配置,自动为
WebApp
配置
SecurityHandler
,创建对应的
Authenticator
。
Jetty
中所有的请求处理逻辑都以
Handler
的形式组织和串联在一起,构成一条请求处理链(
Handler Chain
),当请求到达
SecurityHandler
时,就会根据配置决定是否调用对应的
Authenticator
进行认证。
Authenticator
和
Filter
对比:
特性 |
Authenticator |
Filter(Java EE 标准) |
所属层级 |
Servlet 容器层 |
Web 应用层 |
启用方式 |
web.xml 中配置 <login-config> |
web.xml 或注解 @WebFilter |
认证触发时机 |
容器处理请求初期,早于 Filter 执行 |
请求进入应用后、在 Servlet 执行前执行 |
03
路由转发机制
RequestDispatcher
是
Java Servlet API
中的一个接口,内部有
forward
和
include
两个方法:
它的主要作用是:
在服务器内部将一个请求跳转(forward)或包含(include)到另一个资源(如 Servlet、JSP、HTML)中进行处理。
区别于重定向,它不会让客户端发起新的请求,属于服务器端的“内部转发”机制。
两者的区别在于
forward
由目标资源直接生成响应,
include
后会将目标资源生成的响应包含在当前的
Servlet
响应中一起发送给客户端。
04
认证绕过
在转发到目标资源前,需要根据
url-pattern
和
DispatcherType
类型匹配重新组装
FilterChain
以便于对请求进行预处理。
Tomcat
中只经过
FilterChain
的处理,并不会经过比
Filter
更底层的
valve
等处理。
Jetty
中
checkSecurity
对于
forward
和
include
类型的请求默认返回
false
,所以会直接调用
handler.handle
,导致根本不用过
Authenticator
校验。
ASYNC
和
REQUEST
才会为
true
,从而进入
if
分支进行看是否需要
Authenticator
认证校验。
因此存在两种路由转发导致的权限认证绕过的模式。
Filter类型认证绕过
Filter
的
dispatcher
选项是经常被开发忽略的点,常常是不配置,或者只配置
REQUEST
方式,这就导致存在安全隐患。当同时满足如下条件时则可以权限认证绕过:
1. 权限认证是在
Filter
进行,且
dispatcher
选项中未配置
FORWARD
或者
INCLUDE
;
- 存在未授权的代码通过未配置选项对应的
forward
或者
include
方法向某个
url
转发请求且
url
可控。
Authenticator等更底层的权限认证绕过
-
权限认证使用比
Filter
等更底层的认证方式,如
Tomcat
的
Valve
或者
Jetty
的
Authenticator
; -
存在未授权的代码通过
forward
或者
include
方法向某个
url
转发请求且
url
可控。
一个Filter类型的小例子
DspServlet
代码如下,
service
方法使用
req.getRequestDispatcher
方法根据
url
获取
RequestDispatcher
对象后调用
forward
进行路由转发,
url
可控。
AdminServlet
代码如下:
当直接访问
/admin
时,会被
AuthFilter
拦截:
但是通过
DspServlet
路由转发的方式访问则能绕过
AuthFilter
拦截,此时从
DspServlet#service
到
AdminServlet#service
的调用栈中可以看到不经过
AuthFilter#Filter
。
根据上述例子可知,路由转发时需要先根据
url
匹配某个
Servlet-mapping
的
url-pattern
的获取
javax.Servlet.RequestDispatcher
,那么如何获取呢?通常有如下几种常用的方式:
– javax.servlet.ServletRequest#getRequestDispatcher
-
javax.servlet.ServletContext#getRequestDispatcher
-
javax.servlet.ServletContext#getNamedDispatcher
-
jsp中运行时包含和转发的形式:
在
Tomcat
跟踪调用流程就能发现:
– javax.Servle
t.ServletRequest#getRequestDispatcher
最终还是通过调用
javax.Servlet.ServletContext#getRequestDispatcher
去获取
RequestDispatcher
。
- jsp
的运行时包含和转发的形式其实是调用
javax.Servlet.ServletRequest#getRequestDispatcher
方法去完成的
接下来看
Tomcat
中对
path
进行处理的关键方法:
org.apache.catalina.connector.Request#getRequestDispatcher
中判断
path
中有
,则直接截断。
org.apache.catalina.core.ApplicationContext#getRequestDispatcher
中去除
path
中的参数部分后调用
stripPathParams
和
normalize
进行处理:
org.apache.catalina.core.ApplicationContext#stripPathParams
的作用就是清除
path
中
;xxx/
(不包括
/
)的部分:
org.apache.Tomcat.util.http.RequestUtil#normalize
方法的作用就是如果
path
以
/.
或者
/..
结尾,则在末尾添加
/
再对
//
、
/../
、
/./
做规范化处理:
org.apache.catalina.core.ApplicationContext#getNamedDispatcher
中通过
name
获取当前
context
的子容器:
在
Tomcat
的中结构如下,所以
context
的子容器是
Wrapper
,每个
Wrapper
负责管理一个具体的
Servlet
类的生命周期。
Wrapper
的
name
对应
Servlet-name
:
根据上述分析可知:
– getNamedDispatcher
是根据
Servlet-name
进行转发的。
- 其他
getRequestDispatcher
是通过对一个
path
经过处理后能匹配一个
Servlet-mapping
的
url-pattern
进行转发的。
Request
类和
ApplicationContext
类的处理规则表如下:
原始path |
Request |
ApplicationContext |
---|---|---|
/a/./admin#xxx |
/a/./admin |
/a/admin |
/a/./admin;yyy#xxx |
/a/./admin;yyy |
/a/admin |
/a;xxx/admin.jsp;yyy |
/a;xxx/admin.jsp;yyy |
/a/admin.jsp |
/a/../admin;yyy#xxx |
/a/../admin;yyy |
/admin |
/a/./admin/.. |
/a/./admin/.. |
/a |
/a/./admin/. |
/a/./admin/. |
/a/admin |
/a;xxx/admin.jsp |
/a;xxx/admin.jsp |
/a/admin.jsp |
/abc;xxx/yy |
/abc;xxx/yy |
/abc/yy |
三
漏洞分析
01
CVE-2022-31692 Spring Security认证绕过漏洞
5.7.0 <Spring Security<5.7.5和 5.6.0 <Spring Security<5.6.9
版本,可以通过forward或include的dispatcher类型绕过授权规则。
如果一个应用的
spring security
配置如下,定义了一个有
ADMIN
角色的用户,并配置
/admin
需要有
ADMIN
角色才能访问。
对于如下
controller
,当不登录或者用户没权限时访问
/admin
会提示未授权,访问
/dsp
时却能访问到
/admin
资源:
其核心原因是进行权限认证关键类
AuthorizationFilter
继承自
OncePerRequestFilter
,而
OncePerRequestFilter
的作用就是保证其子类在每次请求中只执行一次,所以当访问
/dsp
时
AuthorizationFilter
已经触发一次,
forward:/admin
时将不再触发认证流程导致认证绕过。
02
CVE-2022-40664 Apache Shiro身份认证绕过漏洞
Apache Shiro<1.10.0
时,通过RequestDispatcher include或forward可以造成身份验证绕过漏洞。
一个应用的
Shiro
配置部分代码如下,指定
/admin
需要认证,
/dsp
不需要:
当不登录时访问
/admin
会提示未授权,访问
/dsp
时却能访问到
/admin
资源:
该漏洞核心原因和
Spring security
类似,当
Forward
时
request
已经有
alreadyFilteredAttributeName
标志了,所以就不进入
Shiro
内部
Filter
链检查当前
url
是否需要认证,所以导致认证绕过。
03
CVE-2021-44515 ManageEngine Desktop Central身份认证绕过漏洞
ManageEngine Desktop Central<10.1.2127.18
容易受到身份验证绕过攻击,导致在服务器上远程执行代码。
该漏洞的关键点在于
StateFilter
的路由转发。
web.xml
的配置如下:
doFilter
方法中获取
path
后通过
request.getRequestDispatcher(path).forward
进行转发。
getForwardPath
中截取
requestURI
的一部分返回,因此是可控的。
ChangeAmazonPasswordServlet
可以直接修改用户密码。
在
web.xml
配置如下:
同时在
中配置
/changeDefaultAmazonPassword
资源不限制角色但需要登录才能访问,认证是在
CUSTOMFORM856
中做的。
CUSTOMFORM856
是自定义的
Authenticator
,以
valve
的形式生效于
Filter
之前。
中没有配置
/STATE_ID/*
资源,不会经过
CUSTOMFORM856
认证校验,
Forward
方式转发到
/changeDefaultAmazonPassword
只会经过相应的
Filter
,所以可以权限认证绕过。对应的部分调用栈如下:
04
CVE-2024-33535 zimbra本地文件包含漏洞
Zimbra Collaboration(ZCS)9.0和10.0中发现了一个问题。该漏洞涉及web应用程序中未经身份验证的本地文件包含(LFI),具体影响了packages参数的处理。攻击者可以利用此漏洞在没有身份验证的情况下包含任意本地文件,从而可能导致对敏感信息的未经授权的访问。该漏洞仅限于特定目录中的文件。
该漏洞是由于
/public/admin.jsp
文件使用
标签进行动态包含
page
,其中
pname
可控,虽然
zimbra
的
Servlet
容器是
Jetty
,但也可以
../
目录穿越和
;
截断
psuffix
,从而达到访问非
jsp
类型的
url
。
web.xml
中配置的
/downloads/*
路径会被
org.eclipse.Jetty.Servlet.DefaultServlet
处理,但需要经过
ZimbraAuth
的认证。
Jetty-env.xml
使用
ZimbraAuthenticatorFactory
创建自定义认证器
ZimbraAuthenticator
,只对某些路径(如
/downloads/*
)启用认证。
当不提供用户凭证访问时会被
zimbra
自定义的
ZimbraAuthenticator
拦截,提示未授权。
当通过
public/admin.jsp
路由转发时则会绕过
ZimbraAuthenticator
拦截。
四
总 结
本文介绍了
Java
中
Filter
机制、
Tomcat
以及
Jetty
中的
Authenticator
机制,深入剖析了路由转发导致权限认证绕过的原理,并分析了
Tomcat
中对于要转发的
url
的处理特性,举了几个CVE的例子对该漏洞模式进行详细说明。
五
参考链接
【版权说明】
【版权说明】
本作品著作权归Mmuz
所有
未经作者同意,不得转载
Mmuz
天工实验室安全研究员
专注于代码审计、Web安全
往期回顾
01
02
03
04
每周三更新一篇技术文章 点击关注我们吧!