截至发稿日期 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_nonce
和user_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_code
和start_email_validation
对其进行了调用
选择start_email_validation
跟踪 可以看到set_profile_email
和set_as_email
方法
搜索可以看到其路由分别为save_default_method_email
和save_default_method_email_profile
由此我们掌握了创建token的方法
其次搜索validate_email_setup的路由为validate_email_setup
由此可以确定漏洞利用方式
- 通过save_default_method_email创建一个token
- 通过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 );
}
因此我们可以得到如下结论
- 需要开启email作为2fa的手段
- token要跑几千万遍 或者wp-config泄露后在本地跑
- token需要在15分钟内跑完 否则需要重新生成
这大概就是其没有评分10分的原因
展望未来
官方已经发布了最新版 可以直接更新
但顺带一提的是 鉴权但没有实现流程中止的代码不在少数 网络安全 我辈仍需努力