<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/8103

            這是這幾天一直關注的漏洞了,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注入的評價是:利用上(可能)比較雞肋,但思路是絕對一流的,值得學習。

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线