为了整个思路更加清晰,我要需要提前了解的写在前面。后续的调试环境不至于思路飞来打去
FirewalledRequest
HttpFirewall是Spring Security提供的Http防火墙,它可以用于拒绝潜在的危险请求或者包装这些请求进而控制其行为。通过HttpFirewall可以对各种非法请求提前进行拦截并处理,降低损失。代码层面,HttpFirewall被注入到FilterChainProxy中,并在Spring Security过滤器链执行之前被触发。
HttpFirewall一共有两个实现类。
DefaultHttpFirewall:虽然名字包含Default,但这并不是框架默认的Http防火墙,它只是一个检查相对宽松的防火墙。
StrictHttpFirewall:这是一个检查严格的Http防火墙,也是框架默认使用的Http防火墙。
DelegatingFilterProxyRegistrationBean
返回一个DelegatingFilterProxy,并注入到servletContext中
DelegatingFilterProxy
看上面的截图,可以理解为拿出来自己持有的bean,然后调用dofilter()方法,所以其的bean也是filter。
靶场
https://github.com/XuCcc/VulEnv/tree/master/springboot/cve_2022_22978
参考,师傅总结的感觉很细,建议可以好好看看
https://www.cnblogs.com/nice0e3/p/16798843.html#springsecurit%E4%BD%BF%E7%94%A8
https://xie.infoq.cn/article/2dd97f17e618c55976d9dcf4c
https://blog.csdn.net/yang131peng/article/details/118519468
安全框架,简单说是对访问权限进行控制,应用的安全性包括用户认证(Authentication)
和用户授权(Authorization)
两个部分。
-
用户认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证过程。
-
用户授权:验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。
一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
绕过antMatchers
http.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/admin").authenticated()
)
不是/**,**的话,就判断字符串是否包含{}?和/**结尾,并且*第一个顺序需要在倒数第二个位置。否则就会硬匹配传入的字符串。所以后面加个/等路径只要硬匹配不到就可以进行绕过。
修复
根据代码来看的话,可以在后面加/** 进行修复。当/**结尾,会正对/**前面的路径做同统一拦截。举例子/admin/**,则/admin后面的所有路径都会做拦截。
绕过regexMatchers
和上面的匹配不同没有/**做后续路由捕获,可以看到regexMatchers走的默认正则匹配。
绕过的话正常正则绕过就行。
CVE-2022-22978
影响版本
Spring Security 5.5.x < 5.5.7
Spring Security 5.6.x < 5.6.4
代码用靶场代码即可
调用栈
admin:22, WebController (person.xu.vulEnv)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
doInvoke:205, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:150, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:117, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:895, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:808, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1067, DispatcherServlet (org.springframework.web.servlet)
doService:963, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:327, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:58, AuthorizationFilter (org.springframework.security.web.access.intercept)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:122, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:116, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:126, SessionManagementFilter (org.springframework.security.web.session)
doFilter:81, SessionManagementFilter (org.springframework.security.web.session)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:109, AnonymousAuthenticationFilter (org.springframework.security.web.authentication)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:149, SecurityContextHolderAwareRequestFilter (org.springframework.security.web.servletapi)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:63, RequestCacheAwareFilter (org.springframework.security.web.savedrequest)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:150, BasicAuthenticationFilter (org.springframework.security.web.authentication.www)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:58, DefaultLogoutPageGeneratingFilter (org.springframework.security.web.authentication.ui)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:237, DefaultLoginPageGeneratingFilter (org.springframework.security.web.authentication.ui)
doFilter:223, DefaultLoginPageGeneratingFilter (org.springframework.security.web.authentication.ui)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:219, AbstractAuthenticationProcessingFilter (org.springframework.security.web.authentication)
doFilter:213, AbstractAuthenticationProcessingFilter (org.springframework.security.web.authentication)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:103, LogoutFilter (org.springframework.security.web.authentication.logout)
doFilter:89, LogoutFilter (org.springframework.security.web.authentication.logout)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:117, CsrfFilter (org.springframework.security.web.csrf)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doHeadersAfter:90, HeaderWriterFilter (org.springframework.security.web.header)
doFilterInternal:75, HeaderWriterFilter (org.springframework.security.web.header)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:110, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:80, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:55, WebAsyncManagerIntegrationFilter (org.springframework.security.web.context.request.async)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
doFilter:336, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:211, FilterChainProxy (org.springframework.security.web)
doFilter:183, FilterChainProxy (org.springframework.security.web)
invokeDelegate:354, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:267, DelegatingFilterProxy (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:100, RequestContextFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, FormContentFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:117, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1743, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:750, Thread (java.lang)
我们稍微分析一些spring的filter
可以看到是StandardWrapperValve#invoke中定义ApplicationFilterChain
其调用doFilter走入filter流程
如下,这里我们主要关注ApplicationFilterConfig[name=springSecurityFilterChain, filterClass=org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1]
继续走回上面的springSecurityFilterChain,发现FilterChain有如下的15和filter,然后在invokeDelegate()进行过滤链的方法调用。delegate.doFilter(request, response, filterChain);
FilterChainProxy#doFilterInternal
这里调用了httpfirewall的getFirewalledRequest()
这里能看到对一些请求 method,url等做了校验。我们需要绕过健全,这一块也是我们必须要关注的
(1)method,允许一下的7种
(2)url
encodedUrlBlocklist = {HashSet@7373} size = 18
0 = "//"
1 = ""
2 = "%2F%2f"
3 = "%2F%2F"
4 = "%00"
5 = "%25"
6 = "%2f%2f"
7 = "%2f%2F"
8 = "%5c"
9 = "%5C"
10 = "%3b"
11 = "%3B"
12 = "%2e"
13 = "%2E"
14 = "%2f"
15 = "%2F"
16 = ";"
17 = ""
decodedUrlBlocklist = {HashSet@7374} size = 16
0 = "//"
1 = ""
2 = "%2F%2f"
3 = "%2F%2F"
4 = "%00"
5 = "%"
6 = "%2f%2f"
7 = "%2f%2F"
8 = "%5c"
9 = "%5C"
10 = "%3b"
11 = "%3B"
12 = "%2f"
13 = "%2F"
14 = ";"
15 = ""
当url包含上诉字符会报错,导致验证不通过。
同时需要满足字符不在以下的条件,否则也会返回false
!
"
&
'
(
)
*
+
,
-
.
/
:
;
<
=
?
@
[
]
^
_
`
{
|
}
~
不允许出现”.”, “/./“ “/.”
上诉
验证全部通过,返回
StrictFirewalledRequest对象
后续就是调用virtualFilterChain.doFilter(firewallRequest, firewallResponse);调用之前截图的filter,不再过过多分析了。后续的调用就是前面我们分析的不同的匹配函数绕过。在java的Pattern.compile的.*规则正则匹配中。默认是不会匹配回车换行的。所以导致了绕过了spring路径的校验,在securty的正则规则中也能正则绕过。
fuzz了一下,也确实是如下的结果
原文始发于微信公众号(e0m安全屋):Java安全之Spring Security
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论