WordPress 5.1.1 CSRF to RCE 跟进分析

-

前言

CSRF to RCE 这个标题是原作者在博文中的写法,其实颇有些标题党的意味。通篇分析下来我们会发现这个漏洞其实就是一个存储型XSS,而RCE是因为获取管理权限后在WordPress后台可以直接编辑php文件写马,也就能实现RCE…

很多地方包括面试会提到XSS与CSRF的区别,其实从这个漏洞就能看出,XSS是实现CSRF的一种方式。可以说两者是不同维度的东西,XSS强调攻击的代码实现,而CSRF着重于攻击达成的结果。

分析

WordPress引入了Nonce安全机制,nonce值与用户的身份以及操作的属性有关,因此不同用户或不同操作都会产生不同的nonce值。由于校验nonce并阻止请求会影响评论处的某些功能,WP选择根据nonce值进行不同的过滤,比较直观的概括出来就是:

  • 管理员权限 + nonce值校验成功 = 无任何过滤
  • 管理员权限 + nonce值校验失败 = wp_filter_post_kses()过滤
  • 普通用户 = wp_filter_kses()过滤

关于nonce检验的关键代码在/wp-includes/comment.php第3240行,wp_handle_comment_submission()函数中:

当创建评论的是具备unfiltered_html属性的管理员且nonce值正确时,不做任何处理;当nonce校验失败后,进入kses_init_filters()函数:

发现nonce校验失败则添加wp_filter_post_kses()过滤器来过滤评论,否则使用wp_filter_kses()过滤。而两者的严格程度不同:

此处可以看到传入的第二参数不同,来看看wp_filter_kses()current_filter()

跟进$wp_current_filter()

//comment.php:1943
function wp_filter_comment( $commentdata ) {
    if ( isset( $commentdata['user_ID'] ) ) {
    ...
    $commentdata['comment_content'] = apply_filters( 'pre_comment_content', $commentdata['comment_content'] );

pre_comment_content会为<a>标签中的rel属性添加nofollow属性值。注意以上这部分说的是wp_filter_kses()函数,而wp_filter_post_kses()函数会完全保留<a>标签中的rel属性,来看wp_filter_post_kses()第二参数为post的情况:

//kses.php:829
function wp_kses_allowed_html( $context = '' ) {
    global $allowedposttags, $allowedtags, $allowedentitynames;

    if ( is_array( $context ) ) {
        return apply_filters( 'wp_kses_allowed_html', $context, 'explicit' );
    }

    switch ( $context ) {
        case 'post':
            /** This filter is documented in wp-includes/kses.php */
            $tags = apply_filters( 'wp_kses_allowed_html', $allowedposttags, $context );

            // 5.0.1 removed the `<form>` tag, allow it if a filter is allowing it's sub-elements `<input>` or `<select>`.
            if ( ! CUSTOM_TAGS && ! isset( $tags['form'] ) && ( isset( $tags['input'] ) || isset( $tags['select'] ) ) ) {
                $tags = $allowedposttags;

                $tags['form'] = array(
                    'action'         => true,
                    'accept'         => true,
                    'accept-charset' => true,
                    'enctype'        => true,
                    'method'         => true,
                    'name'           => true,
                    'target'         => true,
                );

                /** This filter is documented in wp-includes/kses.php */
                $tags = apply_filters( 'wp_kses_allowed_html', $tags, $context );
            }

$allowedposttags<a>标签的rel属性设置为了白名单:

接下来,WordPress为适配SEO会将<a>标签的属性解析成一个关联数组:

之后再将原本的<a>标签拼接起来:

当我们设置<a>标签中的title='XSS " onmouseover=alert(1) id="'时,有$name值为title$value值为XSS " onmouseover=alert(1) id=",拆分到数组中再拼接到$html,则

$html = title="XSS " onmouseover=alert(1) id=""

则返回的是<a title="XSS" onmouseover=alert(1) id="" rel=\"$rel\">

这样也就实现了基于事件的存储型XSS攻击(不论rel属性值是否被修改),我们可以执行任意JS脚本的攻击,对管理员的触发请求进行伪造即完成了原文所谓的CSRF,而伪造管理员身份写php文件也就能成功getshell,完成RCE。

修复

文章目录
  1. 1. 前言
  2. 2. 分析
  3. 3. 修复
|