當前位置:
首頁 > 新聞 > necp_client_action系統調用中的堆溢出漏洞分析

necp_client_action系統調用中的堆溢出漏洞分析

本文是對fuzzing macOS necp_client_action系統調用時發現的堆溢出漏洞的分析。necp_client_action系統調用是網路擴展控制策略(NECP)內核子系統的一部分。此漏洞首先在XNU內核版本4570.1.46中找到,並在10.13.4內核更新(版本4570.51.1)中進行了修補。執行該漏洞會導致堆溢出,該溢出可能會變成信息泄漏並最終在內核中執行任意代碼。

可以在我們的NotQuite0DayFriday存儲庫中找到這篇文章和代碼:https://github.com/grimm-co/NotQuite0DayFriday/tree/master/2018.04.06-macos。

受影響的版本:XNU內核版本4570.1.46及更高版本,直到4570.51.1。

發現環境:運行定製版本的XNU 4570.1.46的VMware Fusion中的macOS High Sierra

$ clang poc.c -o poc

$ ./poc

$ clang leak.c -o leak

$ ./leak

$ clang uaf.c -o uaf

$ ./uaf

在開發necp_client_action的系統調用fuzzer期間,我們研究了NECP子系統,也因此追蹤到了一條因未檢查的用戶提供的長度導致memcpy的代碼路徑。

通過查看bsd / net / necp_client.c中的necp_client_action函數,我們可以看到它僅僅是幾種不同操作的包裝。當將necp_client_action的操作參數設置為NECP_CLIENT_UPDATE_CACHE時,會發生易受攻擊的情況,然後necp_client_action調用函數necp_client_update_cache。necp_client_update_cache函數被用於管理連接的TCP相關信息。因此,如果未使用為其分配有效連接的NECP客戶端UUID調用該函數,則該函數將會出錯。在檢查UUID後,此函數將用戶提供的necp_cache_buffer結構體複製到堆棧中。這個結構如下所示:

typedef struct necp_cache_buffer {

u_int8_t necp_cache_buf_type; // NECP_CLIENT_CACHE_TYPE_*

u_int8_t necp_cache_buf_ver; // NECP_CLIENT_CACHE_TYPE_*_VER

u_int32_t necp_cache_buf_size;

mach_vm_address_t necp_cache_buf_addr;

} necp_cache_buffer;

如果buf_type和buf_ver分別為NECP_CLIENT_CACHE_TYPE_TFO和NECP_CLIENT_CACHE_TYPE_TFO_VER_1,則necp_client_update_cache將嘗試從用戶讀取necp_tcp_tfo_cache結構並使用它調用tcp_heuristics_tfo_update。下面顯示的這個結構是值得注意的,因為它包含一個用戶控制的緩衝區,長度最多為0x10個字元,長度為緩衝區。

typedef struct necp_tcp_tfo_cache {

u_int8_t necp_tcp_tfo_cookie[NECP_TFO_COOKIE_LEN_MAX];

u_int8_t necp_tcp_tfo_cookie_len;

u_int8_t necp_tcp_tfo_heuristics_success:1;

u_int8_t necp_tcp_tfo_heuristics_loss:1;

u_int8_t necp_tcp_tfo_heuristics_middlebox:1;

u_int8_t necp_tcp_tfo_heuristics_success_req:1;

u_int8_t necp_tcp_tfo_heuristics_loss_req:1;

u_int8_t necp_tcp_tfo_heuristics_rst_data:1;

u_int8_t necp_tcp_tfo_heuristics_rst_req:1;

} necp_tcp_tfo_cache;

tcp_heuristics_tfo_update(在bsd / netinet / tcp_cache.c中)通過以下幾行代碼處理necp_tcp_tfo_cache結構:

if (necp_buffer->necp_tcp_tfo_cookie_len != 0) {

tcp_cache_set_cookie_common(&tcks,

necp_buffer->necp_tcp_tfo_cookie,

necp_buffer->necp_tcp_tfo_cookie_len);

}

如果cookie長度不為零,它將調用tcp_cache_set_cookie_common,如下所示。該函數查找與當前連接關聯的tcp_cache結構並將該cookie複製到該結構中。

static void tcp_cache_set_cookie_common(struct tcp_cache_key_src

*tcks, u_char *cookie, u_int8_t len)

{

struct tcp_cache_head *head;

struct tcp_cache *tpcache;

/* Call lookup/create function */

tpcache = tcp_getcache_with_lock(tcks, 1, &head);

if (tpcache == NULL)

return;

tpcache->tc_tfo_cookie_len = len;

memcpy(tpcache->tc_tfo_cookie, cookie, len);

tcp_cache_unlock(head);

}

但是,tc_tfo_cookie長度至多為0x10位元組。因此,如果攻擊者指定的參數大於0x10位元組,則memcpy會溢出堆上的tcp_cache結構並將數據寫入該堆。由於源cookie位於堆棧上,因此將會溢出堆棧。tcp_getcache_with_lock函數會嘗試為與連接關聯的本地和遠程主機查找現有的tcp_cache結構。如果找不到,則會創建一個新的。但是,將會創建的tcp_cache結構數量有限制。

漏洞修復

那麼這個bug是如何修復的?雖然XNU內核的源代碼沒有被更新以反映最新的可用XNU內核二進位文件,但我們可以通過反彙編以了解修復。在地址0xFFFFFF800060DE59的4570.51.1內核中,添加了以下指令:

cmp ebx,0x10

mov edx,0x10

cmovb edx,ebx

...

將edx位元組從necp_tcp_tfo_cookie(rsi)複製到tpcache(rdi),此程序集將necp_tcp_tfo_cookie_len參數與0x10進行比較,並且只有在它小於0x10時,該值才會用於以下memcpy中。因此,任何寫入超過0x10位元組的嘗試都會導致寫入0x10位元組。

堆分析

這個bug允許在內核堆中發生堆溢出。更具體地說,溢出的tcp_cache結構位於kalloc.80區域中。因此,任何剝削嘗試自然會將tcp_cache結構溢出到kalloc.80區域中的另一個分配中。我們試著通過尋找kalloc.80分配來開始我們的分析,雖然kalloc.80分配的數量非常有限,但我們還是找到了三個有用的分配。

第一次分配由necp_set_socket_attributes函數(在bsd / net / necp_client.c中)完成。該功能允許用戶標記具有域和帳戶屬性的套接字。這些字元串可以是任意大小,並且可以在用戶需要時創建,讀取和釋放。這些分配非常適合閱讀任何泄漏的內容。此外,它們也可以用來修飾內核堆。但是,每個套接字只能創建兩個NECP屬性字元串。因此,NECP屬性字元串的總數受開放文件描述符的限制。

找到的第二個分配是IO矢量元數據結構(struct uio,在bsd / sys / uio_internal.h中定義)。該結構用於在內核中執行分散/收集內存操作。當通過uio_create創建uio結構體(在bsd / kern / kern_subr.c中)時,IO向量信息數組將立即存儲在uio結構體之後。因此,通過調整存儲在uio結構中的IO向量的數量,我們可以控制大小。通過創建一個具有單個IO矢量的uio結構,uio結構將在kalloc.80區域中聲明。這些結構可以通過使用recvmsg_x和sendmsg_x系統調用來聲明和釋放。這個結構對堆修飾比NECP分配字元串更有用,因為每個線程可以創建一個結構。

第三個分配用於POSIX共享內存子系統(在bsd / kern / posix_shm.c中)。該子系統允許進程設置在兩個進程中映射的共享內存區域。這些共享內存區域使用pshminfo結構進行跟蹤,如下所示。由於結構中早期的pshm_usecount引用計數器,此結構在開發過程中將很有用。

#define PSHMNAMLEN 31 /* maximum name segment length we bother with */

struct pshminfo {

unsigned int pshm_flags;

unsigned int pshm_usecount;

off_t pshm_length;

mode_t pshm_mode;

uid_t pshm_uid;

gid_t pshm_gid;

char pshm_name[PSHMNAMLEN + 1]; /* segment name */

struct pshmobj *pshm_memobjects;

struct label* pshm_label;

};

產生信息泄漏

現在我們有了目標分配設置,可以開始研究利用了。我們的第一個目標是獲取信息泄漏並推導出內核的slide。這是通過用NECP屬性字元串填充kalloc.80區域,釋放每個其他NECP屬性字元串,然後導致溢出來實現的。一旦發生溢出,我們將讀出NECP屬性字元串,查找其內容已更改的字元串。新內容將包含內核堆棧中的位元組。這個信息泄漏在包含的leak.c文件中演示。

利用開發

不幸的是,我們沒有在修復漏洞之前完成漏洞利用。所以這裡我們介紹的是一個包含uaf.c文件的POC。這個POC是不確定的,可能需要幾次運行才能正確執行。由於XNU內核區域分配器的設計,不可能溢出到內核堆元數據中。因此,我們必須嘗試覆蓋另一個內核堆塊的內容。而且,由於堆溢出不能控制寫入的內容,我們必須找到一種方法來創建更強大的利用原語。

如前所述,我們將集中精力開發pshminfo結構。該結構在位元組4中包含一個引用計數器。當SHM區域被刪除時,該引用計數器遞減。如果計數器達到零,則釋放pshminfo結構。通過溢出到pshminfo的引用計數器並破壞它,我們將能夠在發生免費漏洞之後將這個不受控制的堆溢出變為用途。

在免費使用之前,我們必須處理一些實現細節。首先,為了釋放結構,pshm_flags欄位必須設置PSHM_DEFINED(0x2)或PSHM_ALLOCATED標誌(0x4),並且不設置PSHM_INDELETE(0x80)和PSHM_ALLOCATING(0x100)標誌。由於pshm_flags欄位在引用計數器之前,我們也需要將其溢出。

我們需要處理的下一個問題是獲取緊接在tcp_cache結構之後的pshminfo結構。這很容易實現,因為kalloc.80區域相對靜態,並且不被內核中的許多事物使用。但是,我們需要確定在溢出的tcp_cache結構之後緊接著哪個pshminfo結構。由於pshm_flags欄位將被破壞,它很可能無效。此外,如果pshm_flags欄位設置不正確,shm_unlink在調用時將失敗。因此,我們可以通過在每個SHM區域上調用shm_unlink並查找失敗來檢測溢出的SHM區域。

一旦我們確定了溢出的SHM區域,我們接下來需要更正該區域的pshminfo的pshm_flags欄位。不幸的是,我們不能控制堆溢出的內容,所以我們不能直接設置這個值。但是,在我們的測試中,溢出到pshm_flags欄位中的值是未初始化的堆棧數據。因此,每個necp_client_action系統調用的值可能不同。所以,我們的概念驗證不斷觸發堆溢出,直到標誌被設置為可接受的值。

現在我們已經成功設置了一個有效的pshm_flags值並損壞了引用計數,我們可以在空閑後使用該引用。在最近發生的腐敗之前,我們的概念證明會再次打開溢出的SHM區域252。因此,我們很可能能夠比損壞的參考計數器更多的關閉SHM區域。我們的概念驗證關閉了每個SHM區域,並立即創建一個NECP屬性字元串。一旦pshminfo的引用計數器命中0,它將被釋放,並且下一個屬性字元串將被放置在相同的存儲位置。由於內核仍然保持對SHM區域的其他引用,所以我們將在pshminfo結構上的空閑狀態之後使用。為了說明免費後的使用情況,我們的概念驗證列印出有關通過proc_info系統調用獲取的SHM區域的信息。由於SHM區域的pshminfo結構和我們的NECP屬性字元串位於同一個內存區域,因此pshminfo結構將被我們的NECP屬性字元串(0x41)的內容覆蓋。

我們開發計劃中的下一步是設置NECP屬性字元串,以便對剩下涉及到SHM地區的可以免費使用。在釋放的內存中,將分配一個uio結構,我們可以通過NECP屬性字元串控制和讀取佔用該內存區域的字元串。通過讀取和修改uio結構的能力,攻擊應該能夠實現對內核內存的任意讀寫。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 嘶吼RoarTalk 的精彩文章:

趨勢:加密貨幣挖礦活動影響數百萬安卓設備
謹防Windows PowerShell憑證請求提示

TAG:嘶吼RoarTalk |