drupal 7 ajax,【漏洞分析】CVE-2018-7600 Drupal 7.x 版本代码执行
閱讀:
8,003
CVE-2018-7600影響范圍包括了Drupal 6.x,7.x,8.x版本,前幾天8.x版本的PoC出來之后大家都趕緊分析了一波,然后熱度似乎慢慢退去了。兩天前Drupalgeddon2項(xiàng)目更新了7.x版本的exp,實(shí)際環(huán)境也出現(xiàn)了利用,下面就簡單來看一下
0x01 概述
CVE-2018-7600影響范圍包括了Drupal 6.x,7.x,8.x版本,前幾天8.x版本的PoC出來之后大家都趕緊分析了一波,然后熱度似乎慢慢退去了。兩天前Drupalgeddon2項(xiàng)目更新了7.x版本的exp,實(shí)際環(huán)境也出現(xiàn)了利用,下面就簡單來看一下
看到項(xiàng)目上這樣寫
Drupal < 7.58 ~ user/password URL, attacking triggering_element_name form & #post_render parameter, using PHP’s passthru function
提示了問題出在user/password路徑下,通過#post_render傳遞惡意參數(shù),問題出現(xiàn)在triggering_element_name表單處理下
0x02 漏洞分析
我們從三個(gè)問題入手,為什么PoC發(fā)了兩個(gè)包,第二次請(qǐng)求為什么要帶上一個(gè)form_build_id,以及為什么選擇user/password這個(gè)入口
先分析第一個(gè)post,照例還是先看一下Drupal 7的表單處理流程,跟8版本不太一樣,但是入口還是相似的。
根據(jù)文檔描述,當(dāng)我們提交一個(gè)表單(例如找回密碼)時(shí),系統(tǒng)會(huì)通過form_builder()方法創(chuàng)建一個(gè)form
一系列預(yù)處理后,會(huì)由drupal_build_form
()方法創(chuàng)建一個(gè)表單,在第386行調(diào)用drupal_process_form()方法,
跟進(jìn)drupal_process_form()方法,這時(shí)候默認(rèn)的$form_state['submitted']為false
不滿足if條件,$form_state['submitted']被設(shè)置為true
于是進(jìn)入這個(gè)分支,最終被drupal_redirect_form重定向
我們的目的是要讓系統(tǒng)緩存一個(gè)form_build_id,以便后面拿出來用。要想form被緩存,就得想辦法讓if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild'])不成立,也就是說要使$form_state['submitted']為false
從而進(jìn)入下面的drupal_rebuild_form
那么如何讓$form_state['submitted']為false呢?
在includes/form.inc第886行
$form = form_builder($form_id, $form, $form_state);
跟進(jìn)form_builder方法,第1987行
當(dāng)$form_state['triggering_element']['#executes_submit_callback']存在值的時(shí)候就為true,那么我們就想辦法讓這個(gè)值為空
往上看第1972行
如果沒有設(shè)置$form_state['triggering_element'],那么$form_state['triggering_element']就設(shè)置為第一個(gè)button的值,所以正常傳遞表單的時(shí)候$form_state['triggering_element']['#executes_submit_callback']就總會(huì)有值
現(xiàn)在問題來了,如何構(gòu)造一個(gè)form能夠確保$form_state['triggering_element']['#executes_submit_callback']為空或者說不存在這個(gè)數(shù)組呢?
我們注意到第1864行
_form_builder_handle_input_element()方法對(duì)表單先進(jìn)行了處理,跟進(jìn)去看一下
第2144行
這里$form_state['triggering_element']被設(shè)置為$element,前提是滿足_form_element_triggered_scripted_submission()方法,繼續(xù)跟入
第2180行
這個(gè)方法的意思是說如果_triggering_element_value和$element的鍵值都相等的話,返回true
$form_state['triggering_element']賦值為$element,其中不含['#executes_submit_callback'],一開始的條件就成立了
根據(jù)PoC,我們傳入_triggering_element_name=name
看到進(jìn)入這個(gè)分支,進(jìn)入form_set_cache()方法
數(shù)據(jù)庫中插入緩存form_build_id
成功寫入緩存
接下去來看一下這個(gè)緩存有什么用
分析PoC的第二個(gè)包,請(qǐng)求參數(shù)是這樣q=file/ajax/name/%23value/form_build_id
form_build_id即我們上一個(gè)寫入數(shù)據(jù)庫的緩存表單
首先請(qǐng)求會(huì)進(jìn)入includes/menu.inc的menu_get_item()方法,
$path即我們傳進(jìn)去的q參數(shù),經(jīng)過一系列處理傳給menu_get_ancestors()方法,該方法把path重新組合成一堆router,也就是Drupal處理路由到具體url的傳參方式,最終被db_query_range()帶入數(shù)據(jù)庫查詢
我們關(guān)注查詢結(jié)果$router_item的page_callback值,因?yàn)檫@個(gè)值最終會(huì)作為參數(shù)被帶入call_user_func_array()
到這里就跟8版本的情況有點(diǎn)類似了
跟入回調(diào)函數(shù)file_ajax_upload()
還是一樣,把$form_parents完整取出賦值給$form,加上一些前綴后綴后最終進(jìn)入drupal_render()方法
最終得到執(zhí)行
到目前為止我們分析清楚了為什么PoC要發(fā)兩次包,以及第二次請(qǐng)求為什么要帶上一個(gè)form_build_id,現(xiàn)在來想一想為什么要請(qǐng)求user/password這個(gè)路徑呢?
在user這個(gè)module下的user_pass()方法
看到這里是不是感覺跟8版本很相似,#default_value從get的name參數(shù)里取值,而name可以作為數(shù)組傳入,它的屬性在下面正好可以被利用,一個(gè)巧妙的利用鏈就串起來了。
0x03 總結(jié)
Drupal 7.x的利用比8.x要復(fù)雜一些,但觸發(fā)點(diǎn)和一開始的風(fēng)險(xiǎn)因素還是類似的,一是接收參數(shù)過濾不當(dāng),而是可控參數(shù)進(jìn)入危險(xiǎn)方法。官方補(bǔ)丁把入口處的#全給過濾了,簡單粗暴又有效,估計(jì)再利用框架本身的特性想傳遞進(jìn)一些數(shù)組或元素就很難了。
0x04 參考
https://github.com/dreadlocked/Drupalgeddon2
https://research.checkpoint.com/uncovering-drupalgeddon-2/
總結(jié)
以上是生活随笔為你收集整理的drupal 7 ajax,【漏洞分析】CVE-2018-7600 Drupal 7.x 版本代码执行的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MT【91】空间余弦定理
- 下一篇: 计算机专业和机械自动化哪个好,自动化和机