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(鏈接:https://bugs.chromium.org/p/project-zero/issues/detail?id=221)分配它,並調用它的::initWithTask方法。::initWithTask的默認執行流也不對owningTask做任何處理。
查看代碼到此,似乎默認情況下owningTask並不會保有對userclient的引用(這會避免userclient對任務進行引用,形成循環引用,事實卻完全相反;如果userclient想要保持對owningTask的引用,它必須對owningTask進行引用——不存在隱式的所屬關係。
譯者:ruanbonan
原文鏈接:https://googleprojectzero.blogspot.kr/2016/10/taskt-considered-harmful.html
原文作者:lan Beer,Project Zero
微信公眾號:看雪iOS安全小組 我們的微博:http://weibo.com/pediyiosteam
我們的知乎:http://zhihu.com/people/pediyiosteam
加入我們:看雪iOS安全小組成員募集中:http://bbs.pediy.com/showthread.php?t=212949
n[看雪iOS安全小組]置頂嚮導集合貼: http://bbs.pediy.com/showthread.php?t=212685推薦閱讀:
TAG:iOS |