乐趣区

关于java:简单看下最近的Spring-SecruritySpring漏洞CVE202422234CVE202422243

最近的这两个 cve 我看国内很多情报将其评为高危,所以想着去看看原理,看完发现都比较简单,利用要求的场景也绝对无限(特地是第一个),所以就轻易看下就行了

Spring Security 用户认证绕过(CVE-2024-22234)

先看下官网的布告(https://spring.io/security/cve-2024-22234)

In Spring Security, versions 6.1.x prior to 6.1.7 and versions 6.2.x prior to 6.2.2, an application is vulnerable to broken access control when it directly uses the AuthenticationTrustResolver.isFullyAuthenticated(Authentication) method.

Specifically, an application is vulnerable if:

  • The application uses AuthenticationTrustResolver.isFullyAuthenticated(Authentication) directly and a null authentication parameter is passed to it resulting in an erroneous true return value.

An application is not vulnerable if any of the following is true:

  • The application does not use AuthenticationTrustResolver.isFullyAuthenticated(Authentication) directly.
  • The application does not pass null to AuthenticationTrustResolver.isFullyAuthenticated
  • The application only uses isFullyAuthenticated via Method Security or HTTP Request Security

大略意思是间接调用`AuthenticationTrustResolver.isFullyAuthenticated(Authentication),若 Authentication 为 null,则办法会永远返回真,从而产生一些与预期相同的后果。

AuthenticationTrustResolver 接口中的 isFullyAuthenticated 办法用于查看 Authentication 对象是否齐全通过身份验证,即是否不是匿名用户。在 Spring Security 中,能够应用这个办法来确定用户是否曾经进行了残缺的身份验证。

影响版本为:

  • 6.1.0 to 6.1.6
  • 6.2.0 to 6.2.1

环境搭建

引入 pom,理论调用:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

减少下明码验证和 /index 的无鉴权的配置(交给利用手动配置)

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((requests) -> requests
                        .requestMatchers("/", "/index").permitAll()  // 端点 /、/index 无需鉴权,交给利用间接管制
                        .anyRequest().authenticated()
                )
                .formLogin((form) -> form
                        .loginPage("/login")
                        .permitAll())
                .logout((logout) -> logout.permitAll());

        return http.build();}

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user =
                User.withDefaultPasswordEncoder()
                        .username("user")
                        .password("password")
                        .roles("USER")
                        .build();

        return new InMemoryUserDetailsManager(user);
    }
}

新增控制器并配置须要用户手动输出明码(isFullyAuthenticated)后能力拜访的逻辑:

    @GetMapping("/index")
    @ResponseBody
    public String index(){

        // CVE-2024-22234

        // 获取以后的认证对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(authentication);

        // 创立 AuthenticationTrustResolver 实例
        AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

        // 应用 isFullyAuthenticated 办法查看是否齐全通过身份验证
        boolean fullyAuthenticated = trustResolver.isFullyAuthenticated(authentication);  // 传递 null 返回即为 true
        String msg = "";
        if (fullyAuthenticated) {msg = "用户已齐全通过身份验证";} else {msg = "用户可能是匿名用户或者仅局部通过身份验证";}
        return msg;
    }

复现

失常状况下,如果没有通过认证,返回的页面为:

进入登录页面失常登录后

返回的页面为:

如果开发在某些状况,比方手动革除 SecurityContextHolder 中的 Authentication 信息或通过 异步解决 导致在异步线程中没有可用的信息 getAuthentication() 返回null, 则会导致认证校验的生效,咱们这里为了复现就手动置为 null,

boolean fullyAuthenticated = trustResolver.isFullyAuthenticated(null);

重启利用,在不登陆的状况下,从新拜访/index,发现 isFullyAuthenticated 曾经间接返回了 true。拜访鉴权后的页面

修复

修复形式也比较简单在isFullyAuthenticated 中减少了对 authentication 对象为空的判断

Spring Framework SSRF or open redirect(CVE-2024-22243)

Applications that use UriComponentsBuilder to parse an externally provided URL (e.g. through a query parameter) AND perform validation checks on the host of the parsed URL may be vulnerable to a open redirect attack or to a SSRF attack if the URL is used after passing validation checks.

这个看官网形容只晓得应用 UriComponentsBuilder 这个办法来做 host 校验,会导致重定向和 ssrf,粗看下源码不晓得是怎么回事,看了下代码更新记录,很简略只是将 uri 匹配中 userinfo 匹配的正则表达式去掉[

pre:

private static final String USERINFO_PATTERN = "([^@\\[/?#]*)";

now:

private static final String USERINFO_PATTERN = "([^@/?#]*)";

环境搭建

这里假如存在一个场景,后端会将用户输出的 url 交给 UriComponentsBuilder 进行验证,通过后进行失常的拜访,后端有个简略的黑名单 host 判断(evil.com):

String url = "http://xxx.com";
UriComponents uriComponents = UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build();
String host = uriComponents.getHost();
System.out.println("userinfo:" + uriComponents.getUserInfo());
System.out.println("host:" + host);
// 如果 host 为 evil.com 则会被拦挡
if (host != null && host.equals("evil.com")) {System.out.println("403");

}else {System.out.println("pass");
}

简略场景,排除应用 302、ip、rebind 等形式,单纯从 UriComponentsBuilder 来进行绕过有什么方法?

复现

个别状况下咱们晓得绕过 ssrf 会用到@,如果 url 为http://A.com@B.com , 局部的 host 校验库会辨认这个 urlHost 为 A.com, 而浏览器或者 http client 理论会拜访 B.com 利用这种差别就能绕过局部黑名单限度,间接拜访歹意网站。

试下 UriComponentsBuilder 可不可以:

很显著,在这个办法中,间接这么用是不行的,但依据破绽的修复删除的正则表达式符号来看,咱们在 userinfo 最初减少一个[, 测试一下

胜利绕过:

不过这样绕过后大部分状况下不能间接应用原 url 进行拜访,因为 url 中存在[ 会让程序报错:

所以更多利用场景我猜可能是应用 UriComponentsBuilder 取的 host 从新进行 url 拼接来进行拜访

总结

Spring Security 中这个破绽可能对于实战利用不大,因为黑盒测未受权都能测试进去不须要什么用户可控的绕过姿态,相对而言 Spring Framework 这个在实战中对于 url 可控的中央减少xxx[@yyy.com 可能会有奇效。

公众号

家人们,欢送关注我的公众号“硬核平安”,跟我一起学起来!

退出移动版