在Pwn2own 2017 比賽中,蘋果的macOS Sierra 和 Safari 10 成為被攻擊最多的目標(biāo)之一。在此次比賽過程中,盡管有多支戰(zhàn)隊成功/半成功地完成了對macOS + Safari目標(biāo)的攻破,然而360安全戰(zhàn)隊使用的漏洞數(shù)量最少,而且也是唯一一個通過內(nèi)核漏洞實現(xiàn)沙盒逃逸和提權(quán),并完全控制macOS操作系統(tǒng)內(nèi)核的戰(zhàn)隊。在這篇技術(shù)分享中,我們將介紹我們所利用的macOS內(nèi)核漏洞的原理和發(fā)現(xiàn)細節(jié)。
在Pwn2own 2017中,為了完全攻破macOS Sierra + Safari目標(biāo),徹底控制操作系統(tǒng)內(nèi)核,360安全戰(zhàn)隊使用了兩個安全漏洞: 一個Safari遠程代碼執(zhí)行漏洞(CVE-2017-2544)和一個macOS內(nèi)核權(quán)限提升漏洞(CVE-2017-2545)。CVE-2017-2545是存在于macOS IOGraphics組件中的安全漏洞。
從互聯(lián)網(wǎng)上可循的源碼歷史來看,該漏洞最早在1992年移植自Joe Pasqua的代碼,因此這個漏洞已經(jīng)在蘋果操作系統(tǒng)中存在了超過25年,幾乎影響蘋果電腦的所有歷史版本,同時這又是可以無視沙盒的限制,直接從沙盒中攻入內(nèi)核的漏洞。
在我們3月比賽中獎漏洞負責(zé)任報告給蘋果公司后,蘋果已經(jīng)在5月15日發(fā)布的macOS Sierra 10.12.5中修復(fù)了該漏洞。
尋找瀏覽器可訪問的內(nèi)核驅(qū)動
和Windows系統(tǒng)一樣,Safari的瀏覽器沙盒限制了沙盒內(nèi)進程可訪問的內(nèi)核驅(qū)動,以減小內(nèi)核攻擊面對沙盒逃逸攻擊的影響,因此我們進行的第一步研究就是尋找在瀏覽器沙盒內(nèi)可訪問的內(nèi)核驅(qū)動接口。
在macOS 上,系統(tǒng)根據(jù)下面兩個沙盒規(guī)則文件定義了Safari瀏覽器的權(quán)限范圍。
/System/ Library/Sandbox/Profiles/system.sb
/System/Library/Frameworks/WebKit.framework/Versions/A/Resources/com.apple.WebProcess.sb
我們進一步關(guān)注Safari瀏覽器能夠訪問的內(nèi)核驅(qū)動種類。在system.sb文件中,我們發(fā)現(xiàn)這樣一個規(guī)則:
(allow iokit-open (iokit-registry-entry-class “IOFramebufferSharedUserClient”))
這個規(guī)則說明Safari瀏覽器可以打開IOFramebufferSharedUserClient這個驅(qū)動接口。IOFramebufferSharedUserClient是IOGraphic內(nèi)核組件向用戶態(tài)提供的接口。IOGraphic是macOS上的核心基礎(chǔ)驅(qū)動,負責(zé)圖形圖像處理任務(wù),10.12.4版本上對應(yīng)的IOGraphic源碼包在:https://opensource.apple.com/source/IOGraphics/IOGraphics-514.10/ 。既然IOGraphic相關(guān)代碼是開源的,那么在下一步,我們就對IOGraphic進行了代碼審計。
攻擊面
IOFramebufferSharedUserClient 繼承于IOUserClient。用戶態(tài)可以通過匹配名“IOFramebuff”的IOService, 然后調(diào)用IOServiceOpen函數(shù)獲IOFramebufferSharedUserClient對象的端口。
在獲取一個IOUserClient對象port后,我們通過用戶態(tài)API IOConnectCallMethod可以觸發(fā)內(nèi)核執(zhí)行這個對象的 ::externalMethod接口; 通過用戶態(tài)API IOConnectMapMemory可以觸發(fā)內(nèi)核執(zhí)行這個對象的 ::clientMemoryForType接口; 通過用戶態(tài)API IOConnectSetNotificationPort可以觸發(fā)內(nèi)核執(zhí)行這個對象的 ::registerNotificationPort接口。
實際上IOFramebufferSharedUserClient提供的用戶態(tài)接口很少,其中函數(shù)IOFramebufferSharedUserClient::getNotificationSemaphore 引起了我們關(guān)注。在IOKit.framework中,實際上有個未導(dǎo)出的函數(shù)io_connect_get_notification_semaphore, 通過這個API,我們可以觸發(fā)內(nèi)核執(zhí)行相應(yīng)IOUserClient對象的 ::getNotificationSemaphore接口。
漏洞:getNotificationSemaphore UAF
我們參考IOFramebufferSharedUserClient::getNotificationSemaphore的接口代碼
接口也很簡單,代碼如下:
IOReturn IOFramebufferSharedUserClient::getNotificationSemaphore(
UInt32 interruptType, semaphore_t * semaphore )
{
return (owner->getNotificationSemaphore(interruptType, semaphore));
}
由此可見, IOFramebufferSharedUserClient::getNotificationSemaphore直接調(diào)用的是它的所有者 (也就是IOFramebuffer實例)的getNotificationSemaphore接口。
OFramebuffer::getNotificationSemaphore代碼如下:
IOReturn IOFramebuffer::getNotificationSemaphore(
IOSelect interruptType, semaphore_t * semaphore )
{
kern_return_t kr;
semaphore_t sema;
if (interruptType != kIOFBVBLInterruptType)
return (kIOReturnUnsupported);
if (!haveVBLService)
return (kIOReturnNoResources);
if (MACH_PORT_NULL == vblSemaphore)
{
kr = semaphore_create(kernel_task, &sema, SYNC_POLICY_FIFO, 0);
if (kr == KERN_SUCCESS)
vblSemaphore = sema;
}
else
kr = KERN_SUCCESS;
if (kr == KERN_SUCCESS)
*semaphore = vblSemaphore;
return (kr);
}
通過上面的代碼大家可以看出來,vblSemaphore是一個全局對象成員。vblSemaphore初始值為0。這個函數(shù)第一次執(zhí)行后,內(nèi)核調(diào)用semaphore_create,創(chuàng)建一個信號量,將其賦予vblSemaphore。后面這個函數(shù)再次執(zhí)行時就會直接返回vblSemaphore。
問題在于,用戶態(tài)調(diào)用io_connect_get_notification_semaphore獲取信號量后,可以銷毀該信號量。此時,內(nèi)核中vblSemaphore仍指向一個已經(jīng)銷毀釋放的信號量對象。
當(dāng)用戶態(tài)繼續(xù)調(diào)用io_connect_get_notification_semaphore獲取vblSemaphore并使用該信號量時,就會觸發(fā)UAF(釋放后使用)的情況。
總結(jié)
IOUserClient框架提供了大量接口給用戶態(tài)程序。由于歷史原因,IOFramebufferSharedUserClient仍然保留一個罕見的接口。盡管用戶態(tài)的IOKit.framework中沒有導(dǎo)出相應(yīng)的API,這個接口仍然可以調(diào)用,我們可以把內(nèi)核中 IOFramebuffer::getNotificationSemaphore的UAF問題,轉(zhuǎn)化為內(nèi)核地址信息泄漏和任意代碼執(zhí)行,實現(xiàn)瀏覽器的沙盒逃逸和權(quán)限提升。
|