Wordpress4.2.3提權與SQL隱碼攻擊漏洞(CVE-2015-5623)分析

wyzsk發表於2020-08-19
作者: 騰訊安全中心 · 2015/08/18 11:18

這是這幾天一直關注的漏洞了,wordpress上個禮拜釋出的4.2.4版本,其中提到修補了可能存在的SQL漏洞和多個XSS。 Check point也很快發出了分析,我也來分析與復現一下最新的這個漏洞。

0x01 GP混用造成的越權漏洞


首先,說明一下背景。wordpress中使用者許可權分為訂閱者、投稿者、作者、編輯和管理員。

許可權最低的是訂閱者,訂閱者只有訂閱文章的許可權,wordpress開啟註冊後預設註冊的使用者就是訂閱者。國內很多知名網站,如Freebuf,使用者註冊後身份即為“訂閱者”。

我們先看到一個提權漏洞,透過這個提權漏洞,我們作為一個訂閱者,可以越權在資料庫裡插入一篇文章。

Wordpress檢查使用者許可權是呼叫current_user_can函式,我們看到這個函式:

enter image description here

呼叫的has_cap方法,跟進

enter image description here

再次跟進map_meta_cap函式:

enter image description here

可以見到,這個函式是真正檢查許可權的。出錯誤的程式碼在檢查’edit_post’’edit_page’的部分:

enter image description here

可見,這裡當$post不存在的時候,直接breakswitch邏輯了,後面所有檢查的程式碼都沒有執行。

$post是要編輯的文章的ID,也就是說,如果我要編輯一篇不存在的文章,這裡不檢查許可權直接返回。

正常情況下是沒有問題的,因為不存在的文章也沒有編輯一說了。

我們再看到後臺編輯文章的部分:/wp-admin/post.php

enter image description here

這裡首先獲取$_GET[‘post’],找不到才獲取$_POST[‘post_ID’],也就是可以說此時的$post_ID是來自GET的。

但我們後面呼叫current_user_can函式時傳入的post_ID卻是來自POST的:

enter image description here

這裡就是一個邏輯問題,當我們在GET引數中傳入正確的postid(這樣在get_post的時候不會產生錯誤),而在POST引數中傳入一個不存在的postid,那麼就能夠繞過檢查edit_post許可權的步驟。

但是這個邏輯錯誤暫時不能造成嚴重的危害,因為實際上編輯文章的程式碼在edit_post函式中,而這個函式取的post_ID來自$_POST

0x02獲取_wpnonce繞過CSRF防禦


wordpress對於CSRF漏洞的防禦措施是使用_wpnonce(也就是token),而且它的token很嚴格,不同的操作有不同的token。

比如我們這裡,如果想呼叫edit_post函式,需要經過以下邏輯:

enter image description here

check_admin_referer就是檢查_wpnonce的函式,當$post_type==’postajaxpost’的時候,此時_wpnonce的名字就是“add-postajaxpost”

那麼怎麼獲取名字為”add-postajaxpost”_wpnonce呢?

看到上面一點的位置:

enter image description here

有個post-quickdraft-save操作。這個操作是用來臨時儲存草稿的,只要使用者訪問這個操作,就會在資料庫post表中插入一個statusauto-draft的新文章。

如上圖畫出來的步驟,因為我們不知道名字為”add-post”_wpnonce,所以進入到wp_dashboard_quick_press函式,跟進:

enter image description here

見上圖,很幸運的是,在這個函式中wordpress居然自己把此時的_wpnonce輸出在表單裡了。

所以,只要我們訪問一次post-quickdraft-save,就可以獲得add-post_wpnonce,從而繞過check_admin_referer函式。

0x03 競爭漏洞導致的邏輯漏洞


這一節實際上是這個提權洞的真正核心,在我們拿到_wpnonce後,進入edit_post函式。

我們目的是去update一篇文章,但剛才0x01中說到,如果要繞過許可權檢查的函式,需要傳入一個“不存在”的文章id。那麼即使可以執行update,我們也不可能修改已經存在的文章呀?

這裡實際上涉及到一個由競爭造成的邏輯漏洞。看到edit_post函式程式碼:

enter image description here

上面兩個圖應該很直觀了。在0x01中說到的current_user_can被繞過以後,到最終執行update語句中間,這一段程式碼的執行時間是真空的。

比如我們傳入的tax_input=1,2,3,4…10000,那麼實際上那條查詢語句就要執行10000次,這是需要執行很長時間的。(在我自己的虛擬機器上測試,執行10000次這條語句,大概需要5~10秒左右)

那麼假設在這段時間內,有新插入的文章,那麼我們之前那個“不存在”的id,不就可能可以存在了嗎(只需要把id設定為最新一篇文章id+1)? 但有個問題是,我們怎麼在這段時間內插入一篇新的文章?因為在0x02中為了獲取_wpnonce,已經執行過post-quickdraft-save了。執行post-quickdraft-save可以在資料庫插入一篇statusauto-draft的文章,但每個使用者最多隻會插入一篇文章。

check-point的原文中,它提到的方法是,等待一個星期,wordpress會自動將這篇文章刪除,而_wpnonce會多保留一天,這樣在這天我們再次執行post-quickdraft-save又可以插入一篇文章了。

我自己想了一下,其實沒必要這麼麻煩。如果我們能夠再註冊一個身份為訂閱者的賬號,就可以再插入一篇文章了,所以我的POC是不需要等待一個禮拜的。

這三個漏洞組合起來,造成了一個提權漏洞。針對第一篇文章描述的提權漏洞,我寫了一個EXP,執行後訂閱者就可以在垃圾桶內插入一篇文章:

enter image description here

訪問文章編輯頁面可以看到這篇文章:

enter image description here

0x04 untrash文章時造成的SQL隱碼攻擊漏洞


那麼,僅僅是一個這樣的提權漏洞,實際上沒有太大意義。Check-point第二篇文章裡提到了一個因為這個提權漏洞導致的SQL隱碼攻擊。

先說這個注入的原理。

/wp-includes/post.php

enter image description here

如上圖。Wordpress很多地方執行SQL語句使用的預編譯,但僅限於直接接受使用者輸入的地方。而上圖中明顯是一個二次操作,先用get_post_meta函式從資料庫中取出meta,之後以字串拼接的方式插入SQL語句。

這個地方造成一個二次注入。

我們來看看第一次是如何入庫的。首先wp_trash_post是將文章刪除的方法,其中刪除文章後又呼叫wp_trash_post_comments將文章下的評論也刪除了:

enter image description here

跟進wp_trash_post_comments函式:

enter image description here

如上圖,可以看到這個“comment_approved”其實也是從資料庫中取出來的。所以這個注入我稱之為“三次注入”。

那麼我再繼續跟進,看看最早的comment_approved是從哪來的。

實際上看到圖中的SQL語句就大概知道了,這個comment_approvedcomments(評論)表的一個欄位,我分別看了新增評論、修改評論兩個函式,發現修改評論的函式(edit_comment)中,有涉及到這個欄位:

enter image description here

所以,這一連串操作最後造成的結果就是一個SQL隱碼攻擊漏洞。

總結一下1234,整個利用過程如下:

利用快速草稿插入文章->越權編輯文章->插入評論->修改評論(惡意資料入庫)->刪除文章(惡意資料進入另一個庫)->反刪除文章(惡意資料被取出,直接插入SQL語句導致注入)

0x05 原文隱藏的部分與真實利用過程的研究


這裡不得不提到check-point的原文,原文的第二篇全文隻字未提wordpresstoken也就是_wpnonce,但wordpress後臺幾乎所有操作都需要特定的_wpnonce。在第一步中我們透過一處洩露點獲取了“add-postajaxpost”的_wpnonce,但實際上後面的每一步(增加、編輯評論、trash文章、untrash文章)都需要不同的_wpnonce,那麼這些_wpnonce我們怎麼獲得?

經過我的分析,最後實在找不到在訂閱者許可權下怎麼獲得“增加評論”和“反刪除文章”的_wpnonce,而“修改評論”、“刪除文章”的_wpnonce倒是可以在後臺找到。

另外,雖然前臺也可以增加評論,但前臺增加評論會檢查所屬文章是否是草稿、狀態是否是publicprivate,我們沒法給這篇文章以及其派生的預覽文章增加評論。

所以我把基礎賬號的許可權提升一下,提升到可以發表文章與編輯文章的作者許可權,再來對注入漏洞進行復現。

enter image description here

首先發表一篇文章,並在該文下回復一條評論:

enter image description here

我們再來修改這條評論:

enter image description here

在文章編輯頁面找到trash_wpnonce,將該評論所屬的文章丟入垃圾箱:

enter image description here

再找到反刪除的_wpnonce,從垃圾箱裡反刪除這篇文章:

enter image description here

檢視SQL執行記錄,發現已經注入成功,引入單引號:

enter image description here

最後,我來總結一下這個漏洞。

這個漏洞有兩個核心點,一是利用一個競爭條件邏輯錯誤,造成的一個越權漏洞;二是利用一個三次操作,導致最後的SQL隱碼攻擊漏洞。

這兩個核心技術點都是很有代表性的,通篇學習下來,不得不佩服洞主的思路和對wordpress的研究深度。

但我也很遺憾,沒能分析出在最低許可權下怎樣去注入,主要還是_wpnonce的獲取導致漏洞利用上出現了一些問題。

另外,該SQL隱碼攻擊有一個不得不說的雞肋點,在汙染資料第一次入庫的時候,資料庫中儲存這個資料的欄位只有20字元長度,所以導致最後我們的注入點是一個“存在長度限制”的注入點,對於這個長度限制的利用,我也暫時沒有想出更好的方法。

雖然存在長度限制,但因為注入點出現在update語句的評論表中,所以透過這個漏洞,可以將一整個站的評論全部置為0,對於像Freebuf這類社交性質的網站來說危害還是巨大的。

所以,我對這個SQL隱碼攻擊的評價是:利用上(可能)比較雞肋,但思路是絕對一流的,值得學習。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章