標籤:

task_t指針重大風險預報

task_t指針重大風險預報

引言:大家都知道知名義大利天才少年Luca放出來的針對<=10.2版本的yalu越獄使用的是對kernel port的buffer overflow拿到了kernel_task_port,本文對類似的task_t指針做出了針對性的分析,從mach埠背景知識,到IOkit的相關處理,最終如何利用在堆棧上寫出Exploit,最終甚至給蘋果團隊給出了修復漏洞的建議,由淺入深,偏辟入里,值得推薦。

本文分三篇推出,分別是分析篇,Exploit篇,和修復建議篇。

譯者註:

·一些諸如bug,exploit之類的行話選擇性的翻譯,這通常取決於句子的流暢性。

·不確定的地方在括弧中附註了原句

·超鏈接附在括弧內,方便查看

bynruanbonan

分析篇

task_t 指針存在漏洞

由 lan Beer,Project Zero 發布

本文討論了一個存在於驅動 iOS 和 MacOS 的 XNU 內核核心部分的設計問題。蘋果已經發布了兩輪緩解策略,緊隨其後地,昨天又發布了MacOS 10.12.1/iOS 10.1 中的重大重構(Apple havenshipped two iterations of mitigations followed yesterday by a large refactor innMacOS 10.12.1/iOS 10.1)。我們將關注以下內容:這些bug怎樣被利用來進行沙箱逃逸並提升許可權;我們如何繞過每一個緩解策略。每一步都配有一個可用的exploit。

一些關於mach埠的背景知識

mach埠是由內核維護的多發送者-單接收者的消息隊列。某些特別的mach埠。

提供與用戶空間相同的消息傳送API,但是發送給它們的消息會被內核消息處理程序同步處理。從這個意義上來說,發給這些埠的消息與系統調用非常像。

任務埠就是這樣的一個例子。它們處理那些允許發送者操作一個任務的虛擬內存,並能夠訪問它的線程的消息。每一個任務有它自己的任務埠。內核佔用的消息埠使用MIG這個工具來生成序列化代碼。

從底層看IOKit

當在用戶空間創建一個新的IOKit用戶客戶端時,你通常會調用IOKitLib庫的以下方法:

kern_return_t

IOServiceOpen(

io_service_t service,

task_port_t owningTask,

uint32_tn type,

io_connect_t *connect );

IOServiceOpen調用MIG為io_service_open_extended進程間通信方法生成序列化代碼,並把序列化消息發送給已定的IOService埠。mach陷阱mach_msg注意到這個埠被內核佔用,並為該消息調用正確的內核MIG處理程序,而不是把它排在埠消息隊列的隊尾。

這裡傳來的任務埠是owningTask;這個名字在用戶空間和內核代碼中相同。它是引起我注意的第一處地方。OwningTask暗示著一個所屬關係,這可能導致內核擴展開發者相信IOKit實際在這背後維護了一個所屬關係,而這個關係確保userclient的生命周期總由owningTask的生命周期決定。這是一個危險的假設,本篇博客文章是質疑這個假設的結果。讓我們來跟隨代碼流進入內核。下面是一段來自內核裡面的MIG為io_service_open_extended生成的反序列化代碼片段:

mig_internaln novalue _Xio_service_open_extended(

mach_msg_header_tn *InHeadP,

mach_msg_header_tn *OutHeadP)

{

...

owningTask =n convert_port_to_task(In0P->owningTask.n11ame);

RetCoden = is_io_service_open_extended(

service,

owningTask,

In0P->connect_type,

In0P->ndr,

(io_buf_ptr_t)(In0P->properties.address),

In0P->propertiesCnt,n &OutP->result, &connection);

task_deallocate(owningTask);

...

}

內核已經把所有包含在消息中的許可權複製進來,所以In0P->owningTask.name 實際上是指向一個struct ipc_port的指針,而不是用戶態看到的mach埠名。

下面是convert_port_to_task:

task_t

convert_port_to_task(

ipc_port_tn port)

{

task_tn task = TASK_NULL;

ifn (IP_VALID(port)) {

ip_lock(port);

ifn (ip_active(port) &&

ip_kotype(port)n == IKOT_TASK)

{

taskn = (task_t)port->ip_kobject;

assert(taskn != TASK_NULL);

task_reference_internal(task);

}

ip_unlock(port);

}

returnn (task);

}

它檢查了port參數,確保是一個任務埠對象,然後通過調用task_reference來對任務提供引用,返回task_t指針。task_t是對struct task指針的別名,從代碼中可以看出,它是一個引用計數對象。

這裡is_is_service_open_extended僅僅是將owningTask傳給::newUserClient:

resn = service->newUserClient(

owningTask,

(voidn *) owningTask,

connect_type,

propertiesDict,

&client );

newUserClient是一個IOService方法,如果它們想提供多userclient類型,它可以被一個IOService覆蓋。否則默認執行在IOKit記錄中查詢IOService的IOUserClient子類類名,通過IOKit的反射API(鏈接:bugs.chromium.org/p/pro)分配它,並調用它的::initWithTask方法。::initWithTask的默認執行流也不對owningTask做任何處理。

查看代碼到此,似乎默認情況下owningTask並不會保有對userclient的引用(這會避免userclient對任務進行引用,形成循環引用,事實卻完全相反;如果userclient想要保持對owningTask的引用,它必須對owningTask進行引用——不存在隱式的所屬關係。

譯者:ruanbonan

原文鏈接:googleprojectzero.blogspot.kr

原文作者:lan Beer,Project Zero

微信公眾號:看雪iOS安全小組 我們的微博:weibo.com/pediyiosteam

我們的知乎:zhihu.com/people/pediyi

加入我們:看雪iOS安全小組成員募集中:bbs.pediy.com/showthrea

n[看雪iOS安全小組]置頂嚮導集合貼: bbs.pediy.com/showthrea

推薦閱讀:

TAG:iOS |