Drupal Contextual Link RCE
0x00 參考連結
- 官方預警: ofollow,noindex"> https://www. drupal.org/sa-core-2018 -006
- commit:
https:// github.com/drupal/drupa l/commit/c8f0c39ca488cc29ed575b61c0acf45845d8efed#diff-483e0dea92d8d5caf8024c4a621b9ef5R374
0x01 前言
10 月 17 日,Drupal 官方釋出安全更新 SA-CORE-2018-006 ,修復了 5 個安全漏洞,其中包括 2 個高危漏洞和 3 箇中危漏洞。其中 2 個高危漏洞為遠端程式碼執行漏洞。
這裡對其中 Drupal 8 Contextual Links 驗證問題導致遠端程式碼執行的漏洞進行復現。
根據官方漏洞簡要說明得知該漏洞首先需要擁有 Contextual Links
模組的許可權,並且由於對請求的links缺乏很好的驗證導致了rce。

檢視commit內容,懷疑如下內容為漏洞入口

0x02 環境搭建
通過如下命令搭建好drupal 8.5.2版本。

根據許可權要求登入管理賬號新增 Contextual Links
模組許可權,之後退出賬號。

0x03 過程除錯
配置好代理burpsuite後,訪問主頁可抓取到如下請求。

在 core/modules/contextual/src/ContextualController.php
的 render
函式中掛上斷點,並只保留如下 ids
內容: ids[]=block:block=bartik_footer:langcode=en|menu:menu=footer:langcode=en


其中 #contextual_links
將會通過 _contextual_id_to_links($id)
函式獲取 ,函式內容如下:

該函式將會將 $id
的內容通過 |
拆分,並通過 :
分割至變數 $group
, $route_parameters_raw
, $metadata_raw
,之後 $route_parameters_raw
, $metadata_raw
將會經過 parse_str
解析成變數,並最終賦值給 $contextual_links[$group]

這裡將 ids
內容再縮短至: ids[]=block:block=bartik_footer:langcode=en

之後進入 renderRoot

繼續跟進 executeInRenderContext
函式,其中將 $this->render($elements, TRUE)
作為回撥函式



$this->render
最終將會進入 doRender
函式。

之後經過 $this->elementInfo->getInfo
函式。

這裡主要是增加一些新屬性。

其中添加了 #pre_render
將會進入預處理 preRenderLinks

其中跟進 $contextual_links_manager->getContextualLinksArrayByGroup

這裡將會通過 $group_name
在 $this->pluginsByGroup
進行一個匹配搜尋,這裡匹配成功進入後續迴圈體。


但是這裡由於並沒有登入,所有沒有訪問許可權跳出迴圈體,導致 links
變數為空。

從而 $element['#links']
也為空,之後進入 alter
,這裡函式內容過程,主要關注最後的 $function($data, $context1, $context2);


將會依次呼叫 contextual_contextual_links_view_alter
和 views_ui_contextual_links_view_alter
其中 contextual_contextual_links_view_alter
程式碼如下:

可以看到,當存在 $element['#contextual_links']['contextual'])
時,將會將 $element['#contextual_links']['contextual']['metadata']['contextual-views-field-links']
內容賦值給 $element['#links']
,這由於我們的 $element['#contextual_links']['contextual'])
沒有設定,所以 $element['#links']
依舊為空,但是這部分我們是可控的。
之後執行完函式回到之前時,由於 $element['#links']
為空,則設定 $element['#printed'] = TRUE;

這將導致返回空字串內容。


0x04 控制 $element['#links'] 變數
所以我們可通過設定 $element['#contextual_links']['contextual'])
以及 $element['#contextual_links']['contextual']['metadata']['contextual-views-field-links']
來確保 $element['#links']
不為空。
對照 #contextual_links
中的變數進行修改,並且 $element['#contextual_links']['contextual']['metadata']['contextual-views-field-links']
需要為 json
格式,並且由於會通過 :
進行拆分所以 :
需要進行二次編碼。
得到 ids[]=contextual:block=bartik_footer:contextual-views-field-links={"aaa" %253A "bbb"}

成功設定 $element['#links']

0x05 尋找漏洞觸發點
$element['#links']
雖然可控但是距離觸發漏洞還有一定的距離,繼續在 doRender
函式中查詢漏洞觸發點。
除錯過之前drupal drupalgeddon漏洞的同學應該會知道,如果對 $elements
陣列變數可控,可通過設定 $elements['#pre_render']
來觸發 call_user_func
來達到RCE的目的,這裡嚴重懷疑該漏洞也是通過該種方法得以觸發。
繼續除錯,跟進 $this->theme->render

經過一系列繁瑣的操作後將會依次呼叫如下函式,其中 $variables[links]
為我們所控。

跟進其中的 template_preprocess_links
函式,部分內容如下:

其中 $link_element
將通過 $link
變數進行設定,可以看到我們可控制其中的 #title
, #options
, #url
以及 #ajax
變數,這裡由於傳遞的為 {"aaa":"bbb"}
,所以 "bbb"
不為陣列導致報錯。

修改一下傳遞引數 contextual-views-field-links
內容:

成功設定 $link_element
變數。


最後設定給了 $variables['links']

最終進入頁面render過程。

呼叫棧如下:

$context
內容如下:

之後程式碼簡化如下:

首先將 $context["links"]
賦值給 $context['_seq']
,接著遍歷鍵值對並根據是否包含相應的屬性( links
, text_attributes
)進入不同的條件語句中,傳遞不同的屬性值給 escapeFilter
函式。
這裡由於包含 link
屬性,即傳遞 $context["item"]["link"]

之後跟進 escapeFilter
,該函式將通過 $arg
變數型別來返回不同的值。


該程式碼中可以看到,因為 $arg
為陣列, 所以 is_scalar($arg)=false
,使得 $return=NULL
,從而進入最後的 $this->renderer->render($arg)

$this->renderer->render
即為熟悉的 doRender

所以綜上可知 $context["item"]
的屬性將會傳遞給 doRender
函式,只要完全控制屬性值內容即可控制 doRender
函式的 $elements
變數,從而達到RCE的目的,但是這裡傳遞的是 link
屬性,並不完全可控。
回到前面 if
條件判斷,如果不包含 links
屬性的時候將會傳遞 text
屬性,而 text
屬性值內容完全可控,所以可以利用 text
屬性來達到rce的目的。


要想使得沒有 link
屬性在 template_preprocess_links
函式中可以看到,當沒有 url
時,將不會設定 $item['link']

所以只需刪除請求引數 ids[]
中的 url
部分即可,並且將 title
值內容修改為陣列:

得到:

可以看到 item
沒有 link
屬性了。

進入 render
函式

從而控制了 doRender
函式的 $elements
引數內容,之後可通過設定 #pre_render
等屬性達到rce的目的。

0x06 RCE
這裡不提供完整exp,順帶說明一下這裡只是通過commit除錯下來的結果,並不清楚觸發點是否與原漏洞發現者相同。

0x07 漏洞修復狀況
官方通過驗證簽名 `token` 的方式,使得 `token` 錯誤導致無法利用。
