CVE-2019-17671:如何查看WordPress未授权文章

来源:岁月联盟 编辑:猪蛋儿 时间:2020-01-29


近期,WordPress发布了最新的v5.2.4版本,并修复了很多安全漏洞。在这篇文章中,我们将对其中的一个漏洞CVE-2019-17671进行分析。
信息收集
该漏洞由安全专家J.D.Grimes发现并上报,并且披露了如何利用该漏洞查看未授权文章的方法。但是目前我还找不到任何相关的PoC,因此首先我们需要尽可能地收集关于该漏洞的信息。首先,我查看了不同安全厂商关于该漏洞的声明,大部分厂商都引用了相同的一句话:“该漏洞也许可以允许他人查看WordPress中未授权的文章”。
参考资料
1、https://blog.wpscan.org/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
2、https://blog.wpsec.com/wordpress-5-2-4-security-release/
3、https://www.reddit.com/r/netsec/comments/di9kf2/wordpress_524_security_release_breakdown/f3vbuyh/
根据收集到的信息,我在WordPress的SVN仓库/GitHub库中,找到了5.2-branch分支,点击最近的commit,然后查看提到了“unauthenticated posts”或者“viewing posts”的相关commit。此时,我找到了一个相关的commit:f82ed753cf00329a5e41f2cb6dc521085136f308。
补丁分析
这一个commit只修改了两行代码,删除了static关键字,并修改了部分if条件语句:

根据我的猜想,被删除的static关键字跟这个漏洞有着直接的关系。wp-includes/class-wp-query.php的第731行代码开始包含parse_query函数了,而该函数可以过滤并解析传入的所有查询参数($_GET)。
从第696行到第922行的代码可以根据给定的参数来设置$this->is_single、$this->is_attachment以及$this->is_page。这些条件分支都基于else if实现,但其中只有一个比较关键:
// If year, month, day, hour, minute, and second are set, a single
    // post is being queried.
    } elseif ( '' != $qv['static'] || '' != $qv['pagename'] || ! empty( $qv['page_id'] ) ) {
    $this->is_page   = true;
    $this->is_single = false;
    } else {
    // Look for archive queries. Dates, categories, authors, search, post type archives.
我们肯定不是想设置attachment、name、p或者hour之类的参数,因为这些参数可以绕过代码中的条件分支。但是我们又不能直接设置pagename或page_id,因为我们并不知道这些参数的值,而且些参数将可能导致访问控制检查失效。
这里,我们需要在参数列表中使用static=1。研究了半天之后,我找到了get_posts()函数,而这个函数可以使用已解析的参数来查询数据库内容:
public function get_posts() {
    global $wpdb;
    $this->parse_query();
    [..]
在多个位置使用var_dump调试后,我找到了下列代码:
// Check post status to determine if post should be displayed.
            if ( ! empty( $this->posts ) && ( $this->is_single || $this->is_page ) ) {
                $status = get_post_status( $this->posts[0] );
                if ( 'attachment' === $this->posts[0]->post_type && 0 === (int) $this->posts[0]->post_parent ) {
                    $this->is_page       = false;
                    $this->is_single     = true;
                    $this->is_attachment = true;
                }
                $post_status_obj = get_post_status_object( $status );
                //PoC: Let's see what we have
                //var_dump($q_status);
                //var_dump($post_status_obj);
                // If the post_status was specifically requested, let it pass through.
                if ( ! $post_status_obj->public && ! in_array( $status, $q_status ) ) {
                    //var_dump("PoC: Incorrect status! :-/");

[1] [2] [3] [4]  下一页