作者:綠盟科技
來源:http://blog.nsfocus.net/drupal8-cve-2017-6926/

近期,著名的Drupal CMS網站爆出7個漏洞,其中1個嚴重漏洞CVE-2017-6926,具有發表評論權限的用戶可以查看他們無權訪問的內容和評論,并且還可以為該內容添加評論。綠盟科技于上周發布了《Drupal下周將發布重要安全補丁威脅預警通告》

本篇文章對Drupal 8 – CVE-2017-6926漏洞進行了詳細分析。

CVE-2017-6926 漏洞詳情

先看下drupal官網的通告:

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

{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兩種可能出現的值。

我們在后臺下斷,并且構造一個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這個連接試試:

$test仍然為true。

當時筆者就是掉到這個坑里了,明明這條評論設置為不公開,為什么不同用戶訪問,$entity->access(‘view’)都是true呢?

后來證明了,其實這里的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/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

可以看到,雖然文章不公開,仍然可以看到不公開文章的評論,并且還能對評論進行回復。

再看下后臺下的斷點:

此時的$entity->access(‘view’)變成了false

利用驗證

假設id為{node_id}的文章被作者設置為不公開,想查看/回復此文章的id為{comment_id}的評論。

訪問http://x.x.x.x/comment/reply/node/{node_id}/comment/{comment_id}

漏洞修復

升級Drupal至最新版本


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/559/