细说CVE-2024-10924
hundan 发表于 浙江 漏洞分析 702浏览 · 2024-11-19 10:08

截至发稿日期 github上的都是fake poc

btw cve看多了就发现了很多看起来评分高实际上用起来 like shit 的漏洞 这也是为什么很多洞没有10分

环境

https://wordpress.org/plugins/really-simple-ssl/really-simple-ssl.9.0.0.zip

成因分析

问题点出在security/wordpress/two-fa/class-rsssl-two-factor-on-board-api.php:164

根据https://plugins.trac.wordpress.org/changeset/3188431/really-simple-ssl我们也可以发现,主要更改的是email部分

public function validate_email_setup(WP_REST_Request $request ): WP_REST_Response {
        $parameters = new Rsssl_Request_Parameters( $request );
        $user = $this->check_login_and_get_user( $parameters->user_id, $parameters->login_nonce );
        // Check if the provider.
        if ( 'email' !== $parameters->provider ) {
            return new WP_REST_Response( array( 'error' => 'Invalid provider' ), 401 );
        }
        if ( !Rsssl_Two_Factor_Email::get_instance()->validate_token( $parameters->user_id, self::sanitize_token($parameters->token) ) ) {
            // we reset all the settings.
            Rsssl_Two_Factor_Email::set_user_status( $parameters->user_id, 'open' );
            Rsssl_Two_Factor_Totp::set_user_status( $parameters->user_id, 'open' );

            // we logout the user
            wp_logout();

            return new WP_REST_Response( array( 'error' =>  __('Code was was invalid, try "Resend Code"', 'really-simple.ssl-pro') ), 401 );
        }

        Rsssl_Two_Factor_Email::set_user_status( $parameters->user_id, 'active' );
        // Mark all other statuses as inactive.
        self::set_other_providers_inactive( $parameters->user_id, 'email' );

        return $this->authenticate_and_redirect( $parameters->user_id, $parameters->redirect_to );
    }

如上所言 validate_email_setup会进行一次check_login_and_get_user,但是注意check_login_and_get_user方法如下

private function check_login_and_get_user( int $user_id, string $login_nonce ) {
        if ( ! Rsssl_Two_Fa_Authentication::verify_login_nonce( $user_id, $login_nonce ) ) {
            return new WP_REST_Response( array( 'error' => 'Invalid login nonce' ), 403 );
        }

        /**
         * Get the user by the user ID.
         *
         * @var WP_User $user
         */
        $user = get_user_by( 'id', $user_id );
        return $user;
    }

如我们所见 check_login_and_get_user 验证user_id和login_nonce的关联正确性 而且并不直接退出执行流程

而类似的 verify_2fa_code_totp verify_2fa_code_totp skip_onboarding 方法虽然同样给出了authenticate_and_redirect 或者没有在用户验证失败后同样没有推出流程 但是由于其进行调用check_login_and_get_user后使用了$user->id 而由于实际上login_nonceuser_id不匹配 导致未获取到用户 这将导致authenticate_and_redirect第一个参数为 null 所以这也就是为什么只能使用validate_email_setup方法 更具体的原因不再赘述 有兴趣可以翻阅源码

回到validate_email_setup 这步使用check_login_and_get_user验证了之后 即使是错误的 也并没有退出流程 也没用使用到其方法返回的$user 这就导致验证失效

利用分析

看到

if ( !Rsssl_Two_Factor_Email::get_instance()->validate_token( $parameters->user_id, self::sanitize_token($parameters->token) ) ) {

我们需要提供token 而搜索validate_token所在的class 可以看到token的生成来自于security/wordpress/two-fa/class-rsssl-two-factor-email.php:102

public function generate_token( int $user_id ): string {
        $token = self::get_code();

        update_user_meta( $user_id, self::RSSSL_TOKEN_META_KEY_TIMESTAMP, time() );
        update_user_meta( $user_id, self::RSSSL_TOKEN_META_KEY, wp_hash( $token ) );

        return $token;
    }

其中get_code为生成一个8位数的数字 不再赘述

跟踪generate_token调用即可看到resend_email_codestart_email_validation对其进行了调用

选择start_email_validation跟踪 可以看到set_profile_emailset_as_email方法

搜索可以看到其路由分别为save_default_method_emailsave_default_method_email_profile

由此我们掌握了创建token的方法

其次搜索validate_email_setup的路由为validate_email_setup

由此可以确定漏洞利用方式

  1. 通过save_default_method_email创建一个token
  2. 通过validate_email_setup进行8位数字的token验证

by the way 在另一个方面 可以看到 其使用了 wp_hash( $token ) 这个函数使用md5+salt, 其salt来自于多个地方的fallback 但是基本上可以认为来自于wp-config.php的定义 如 NONCE_SALT

set_as_email方法实际上会将其hash后的token返回到页面 因此如果存在wp-config.php泄漏的话可以将返回的token放在本地跑hash

关于利用限制

除了上面所述 我们还可以跟踪到一个代码 具体调用过程不再赘述 总之其存在一个token有效期 为15分钟

public function user_token_ttl( int $user_id ): int {
        $token_ttl = 15 * MINUTE_IN_SECONDS;

        /**
         * Number of seconds the token is considered valid
         * after the generation.
         *
         * @param integer $token_ttl Token time-to-live in seconds.
         * @param integer $user_id User ID.
         */
        return (int) apply_filters( 'rsssl_two_factor_token_ttl', $token_ttl, $user_id );
    }

因此我们可以得到如下结论

  1. 需要开启email作为2fa的手段
  2. token要跑几千万遍 或者wp-config泄露后在本地跑
  3. token需要在15分钟内跑完 否则需要重新生成

这大概就是其没有评分10分的原因

展望未来

官方已经发布了最新版 可以直接更新
但顺带一提的是 鉴权但没有实现流程中止的代码不在少数 网络安全 我辈仍需努力

1 条评论
某人
表情
可输入 255