作者:绿盟科技 来源:<http://blog.nsfocus.net/drupal8-cve-2017-6926/> 近期,著名的Drupal CMS网站爆出7个漏洞,其中1个严重漏洞CVE-2017-6926,具有发表评论权限的用户可以查看他们无权访问的内容和评论,并且还可以为该内容添加评论。绿盟科技于上周发布了[《Drupal下周将发布重要安全补丁威胁预警通告》](http://blog.nsfocus.net/drupal-vulnerability/ "《Drupal下周将发布重要安全补丁威胁预警通告》")。 本篇文章对Drupal 8 – CVE-2017-6926漏洞进行了详细分析。 #### CVE-2017-6926 漏洞详情 先看下drupal官网的通告: <https://www.drupal.org/sa-core-2018-001></https://www.drupal.org/sa-core-2018-001>  有发布评论权限的用户,可以查看他们无权访问的内容和评论。 并且还可以为此内容添加评论。 想要触发这个漏洞,必须启用评论系统,并且攻击者必须有权发布评论。 从漏洞描述来看,问题可能出在评论的权限控制上了。 但是这里有一个坑,笔者当时就掉进去了:这里的权限,指的是文章的权限?还是评论的权限呢?是攻击者可以访问他们不公开的评论呢?还是攻击者可以访问不公开的文章的公开评论呢?下面我会详细的分析这个漏洞,并给出上面问题的答案。 漏洞是在8.4.5上解决的,看一下8.4.5修改的内容:  这里在CommentController.php(评论控制器)里加上了一个对entity实体是否有view权限的判断。 这个好理解,之前没有对entity的权限进行判断,导致不可view的entity也通过了权限检查,从而导致了越权。...
作者:绿盟科技 来源:<http://blog.nsfocus.net/drupal8-cve-2017-6926/> 近期,著名的Drupal CMS网站爆出7个漏洞,其中1个严重漏洞CVE-2017-6926,具有发表评论权限的用户可以查看他们无权访问的内容和评论,并且还可以为该内容添加评论。绿盟科技于上周发布了[《Drupal下周将发布重要安全补丁威胁预警通告》](http://blog.nsfocus.net/drupal-vulnerability/ "《Drupal下周将发布重要安全补丁威胁预警通告》")。 本篇文章对Drupal 8 – CVE-2017-6926漏洞进行了详细分析。 #### CVE-2017-6926 漏洞详情 先看下drupal官网的通告: <https://www.drupal.org/sa-core-2018-001></https://www.drupal.org/sa-core-2018-001>  有发布评论权限的用户,可以查看他们无权访问的内容和评论。 并且还可以为此内容添加评论。 想要触发这个漏洞,必须启用评论系统,并且攻击者必须有权发布评论。 从漏洞描述来看,问题可能出在评论的权限控制上了。 但是这里有一个坑,笔者当时就掉进去了:这里的权限,指的是文章的权限?还是评论的权限呢?是攻击者可以访问他们不公开的评论呢?还是攻击者可以访问不公开的文章的公开评论呢?下面我会详细的分析这个漏洞,并给出上面问题的答案。 漏洞是在8.4.5上解决的,看一下8.4.5修改的内容:  这里在CommentController.php(评论控制器)里加上了一个对entity实体是否有view权限的判断。 这个好理解,之前没有对entity的权限进行判断,导致不可view的entity也通过了权限检查,从而导致了越权。 我们看下关于entity的介绍:  我们再看下漏洞存在的函数 `\core\modules\comment\src\Controller\CommentController.php` ``` public function replyFormAccess(EntityInterface $entity, $field_name, $pid = NULL) { // Check if entity and field exists. $fields = $this->commentManager->getFields($entity->getEntityTypeId()); if (empty($fields[$field_name])) { throw new NotFoundHttpException(); } $account = $this->currentUser(); // Check if the user has the proper permissions. $access = AccessResult::allowedIfHasPermission($account, 'post comments'); $status = $entity->{$field_name}->status; $access = $access->andIf(AccessResult::allowedIf($status == CommentItemInterface::OPEN) ->addCacheableDependency($entity)); ``` 再来看下这个方法的路由 `\core\modules\comment\comment.routing.yml` ``` comment.reply: path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}' defaults: _controller: '\Drupal\comment\Controller\CommentController::getReplyForm' _title: 'Add new comment' pid: ~ requirements: _custom_access: '\Drupal\comment\Controller\CommentController::replyFormAccess' options: parameters: entity: type: entity:{entity_type} ``` 可见replyFormAccess其实是getReplyForm方法的权限检查模块,传入replyFormAccess方法的参数将会是`{entity_type}/{entity}/{field_name}/{pid}` 我们实际测一下,访问这个模块,看看发送的参数是什么样子的: 对kingsguard test评论进行评论:   注意看url:<http://127.0.0.1/d/drupal4/comment/reply/node/1/comment/1></http://127.0.0.1/d/drupal4/comment/reply/node/1/comment/1> {entity_type}:node {entity}:1 {field_name}:comment {pid}:1 现在可以明确了,传入replyFormAccess里的entity类型是node节点类型,接着下断看下entity的数据  在大概知道$entity是什么之后,我们再来看下补丁代码:  可见补丁加了一个判断 ``` andIf(AccessResult::allowedIf($entity->access('view'))); ``` 看下allowedIf方法是怎么实现的: ``` public static function allowedIf($condition) { return $condition ? static::allowed() : static::neutral(); } ``` 可见allowedIf通过传入的参数的True/False来判断是否有权限进行操作。 这样看来,$entity->access(‘view’)只有True/False两种可能出现的值。 我们在后台下断,并且构造一个$test方法值等于$entity->access(‘view’),下断看看$test何时能为False:  首先来找找,关于回复评论处的权限设置: 我们在admin账号下,发表一片名为kingsguard的文章,此文章有一个kingsguard test的评论:  我们将kingsguard test 这条评论的权限编辑成不公开  我们用admin账号回复一下这个评论,后台看下$$test = $entity->access(‘view’);的值:   毫无疑问,$test的值是true 现在用另一个账号登录,也访问<http://127.0.0.1/d/drupal4/comment/reply/node/2/comment/6></http://127.0.0.1/d/drupal4/comment/reply/node/2/comment/6>这个连接试试:  $test仍然为true。 当时笔者就是掉到这个坑里了,明明这条评论设置为不公开,为什么不同用户访问,$entity->access(‘view’)都是true呢? 后来证明了,其实这里的$entity,指的不是评论,而是这篇文章,$entity->access(‘view’)也同样不是单条评论的是否有view权限,而是这篇文章是否有view权限。 其实观察url就可以发现这个问题了: <http://127.0.0.1/d/drupal4/comment/reply/node/1/comment/6></http://127.0.0.1/d/drupal4/comment/reply/node/1/comment/6>(回复第一篇文章的第六个评论) <http://127.0.0.1/d/drupal4/comment/reply/node/2/comment/6></http://127.0.0.1/d/drupal4/comment/reply/node/2/comment/6>(回复第二篇文章的第六个评论) 显然这里的1和2,指的是文章的编号,同时也是entity的编号,那显然entity指的是文章而不是评论。 我们用admin账号编辑kingsguard这篇文章,把published选项的勾去掉:  这时候用admin账号回复kingsguard test这条评论:  对于admin账号来说,$entity->access(‘view’);仍为true 再用其他账号登录:  因为admin账号发布的文章都被admin账号设置为不公开了,所以这里看不到任何文章。 继续用这个账号访问: <http://127.0.0.1/d/drupal4/comment/reply/node/2/comment/6></http://127.0.0.1/d/drupal4/comment/reply/node/2/comment/6>  可以看到,虽然文章不公开,仍然可以看到不公开文章的评论,并且还能对评论进行回复。 再看下后台下的断点:  此时的$entity->access(‘view’)变成了false #### 利用验证 假设id为{node_id}的文章被作者设置为不公开,想查看/回复此文章的id为{comment_id}的评论。 访问<http://x.x.x.x/comment/reply/node/{node_id}/comment/{comment_id}></http://x.x.x.x/comment/reply/node/{node_id}/comment/{comment_id}> #### 漏洞修复 升级Drupal至最新版本