前沿拓展:
stream.sys
下面應(yīng)該可ut沒有end這個符plete!\r" % i )
sys.stdout.flush()
體會一下,將上面的"\r"拿掉試試看,是不是不換行而直接輸出了?明白了么。很長一段時間內(nèi)python都會停留在2.x的時代。
作者:overXsky
預(yù)估稿費(fèi):200RMB
投稿方式:發(fā)送郵件至linwei##,或登陸網(wǎng)頁版在線投稿
前言
現(xiàn)代圖形驅(qū)動程序是十分復(fù)雜的,它提供了大量有希望被利用的攻擊面,可以使用具有訪問GPU權(quán)限的進(jìn)程(比如Chrome的GPU進(jìn)程)進(jìn)行提權(quán)和沙箱逃逸。在這篇文章中,你們將看到如何攻擊NVIDIA內(nèi)核模式的Windows驅(qū)動程序,以及在此期間我發(fā)現(xiàn)的一些bug。我的這項研究是Project Zero的一個20%項目的一部分,在此期間我總共發(fā)現(xiàn)了16個漏洞。
內(nèi)核WDDM接口
圖形驅(qū)動程序的內(nèi)核模式組件被稱為顯示微端口驅(qū)動程序。微軟的官方文檔為我們提供了一個很好的結(jié)構(gòu)圖,小編綜合來說了各個組件之間的關(guān)系:
在顯示微端口驅(qū)動程序 的DriverEntry()函數(shù)中,DRIVER_INITIALIZATION_DATA結(jié)構(gòu)被由廠商實(shí)現(xiàn)的函數(shù)(實(shí)際上與硬件進(jìn)行交互)的回調(diào)進(jìn)行填充,該函數(shù)通過DxgkInitialize()傳遞給dxgkrnl.sys(DirectX的子系統(tǒng))。這些回調(diào)要么由DirectX內(nèi)核子系統(tǒng)調(diào)用,要么在某些情況下直接從用戶模式代碼調(diào)用。
DxgkDdiEscape
一個眾所周知的潛在漏洞的入口點(diǎn)是 DxgkDdiEscape 接口。它可以直接在用戶模式下被調(diào)用,并且可以接受任意數(shù)據(jù),該數(shù)據(jù)以廠商指定的方式(本質(zhì)上是IOCTL)解析和處理。在后文中,我們將使用術(shù)語“逃逸”來表示由DxgkDdiEscape 函數(shù)支持的特定命令。
截止寫作時,NVIDIA有著數(shù)量驚人的400多個逃逸,所以這里也是我花費(fèi)了絕大部分時間的地方(這些逃逸中的絕大多數(shù)是否有必要處在內(nèi)核空間中是一個問題):
NVIDIA為每一個逃逸都單獨(dú)實(shí)現(xiàn)了其私有數(shù)據(jù)(DXGKARG_ESCAPE 結(jié)構(gòu)體中的pPrivateDriverData),格式為“頭部+數(shù)據(jù)”。頭部格式如下:
這些逃逸由32位代碼(上面NvEscapeCodeInfo結(jié)構(gòu)體的第一個成員)標(biāo)識,并根據(jù)它們的最高有效字節(jié)進(jìn)行分組(從1到9)。
在處理每個逃逸代碼之前都會做一些驗(yàn)證。具體來說,每個 NvEscapeCodeInfo 應(yīng)當(dāng)包含頭部后面的逃逸數(shù)據(jù)的預(yù)期大小。這將根據(jù)NvEscapeHeader中的大小來驗(yàn)證,NvEscapeHeader自身又通過傳遞給 DxgkDdiEscape的PrivateDriverDataSize字段進(jìn)行驗(yàn)證。但是,預(yù)期大小有時可能為0(通常當(dāng)逃逸數(shù)據(jù)為可變大小時),這意味著逃逸處理程序負(fù)責(zé)進(jìn)行自身的驗(yàn)證。這將導(dǎo)致一些bug(1,2)。
在逃逸處理程序中發(fā)現(xiàn)的大多數(shù)漏洞(總共13個)都是些非?;镜腷ug,例如盲目地向用戶提供的指針進(jìn)行寫入**作,向用戶模式公開未初始化的內(nèi)核內(nèi)存以及不正確的邊界檢查。還有許多我發(fā)現(xiàn)的問題(例如OOB讀?。]有報告出去,因?yàn)樗鼈兯坪鯖]有可以利用的地方。
DxgkDdiSubmitBufferVirtual
另一個有趣的入口點(diǎn)是DxgkDdiSubmitBufferVirtual函數(shù),它首次在Windows 10和WDDM 2.0中被引入,主要用來支持GPU虛擬內(nèi)存(而舊的 DxgkDdiSubmitBuffer / DxgkDdiRender 函數(shù)已被棄用)。這個函數(shù)相當(dāng)復(fù)雜,并且還接受來自用戶模式驅(qū)動程序提交的每一個由廠商特定的數(shù)據(jù)。我在這里找到了一個bug。
其他
還有一些其他WDDM函數(shù)接受廠商特定的數(shù)據(jù),但快速瀏覽后沒有發(fā)現(xiàn)任何有趣的東西。
暴露的設(shè)備
更多有趣的Bug
我發(fā)現(xiàn)的大多數(shù)bug是通過手動逆向和分析得到的,并且使用了一些自定義的IDA腳本。我還寫了一個模糊工具。最終結(jié)果成功得有點(diǎn)令人驚訝,這也說明了這些bug的簡單性。
雖然大多數(shù)bug相當(dāng)無聊(缺乏驗(yàn)證之類的簡單案例),但也有一些比較有意思。
NvStreamKms
此驅(qū)動程序使用 PsSetCreateProcessNotifyRoutineEx 函數(shù)注冊進(jìn)程創(chuàng)建通知回調(diào)。該回調(diào)檢查系統(tǒng)上創(chuàng)建的新進(jìn)程是否和先前通過發(fā)送IOCTL設(shè)置的映像名稱相匹配。
這個創(chuàng)建通知的例程包含一個bug:
(簡化的反編譯輸出)
此例程通過向后搜索反斜杠(”)的方法從PS_CREATE_NOTIFY_INFO的ImageFileName 成員中提取映像名稱,第二使用 wcscpy_s 將其**到堆棧緩沖區(qū)(Dst),但傳遞的長度是計算出的名稱長度,而不是目標(biāo)緩沖區(qū)的長度。
即使 Dst 是大小固定的緩沖區(qū),這也不能被視為一個直接溢出。因?yàn)樗拇笮〈笥?55個wchar長度,并且對于大多數(shù)Windows文件系統(tǒng)路徑組件來說其不能超過255個字符。而因?yàn)镮mageFileName 是規(guī)范化的路徑,所以掃描反斜杠在大多數(shù)情況下也是有效的。
然而,上述規(guī)則可以通過如下方式繞過:對于一個符合通用命名規(guī)約(UNC)的路徑,其規(guī)范化后保持以正斜杠(’/’)作為路徑分隔符(感謝James Forshaw向我指出這一點(diǎn))。這便意味著我們可以得到一個“aaa / bbb / ccc / …”形式的文件名從而引發(fā)溢出。
例如:
另一個有趣的關(guān)注點(diǎn)是,跟隨受損副本的wcslwr實(shí)際上并不限制溢出的內(nèi)容(唯一的要求是有效的UTF-16編碼)。因?yàn)橛嬎愕膄ilename_length不包含null終止符,所以wcscpy_s 會認(rèn)為目的地太小,第二以在開始處寫入null字節(jié)的方式來清除目的地字符串(發(fā)生在內(nèi)容**到 filename_length 字節(jié)之后,因此溢出仍然發(fā)生)。這意味著 wcslwr是無用的,因?yàn)閷?wcscpy_s的調(diào)用和一部分的代碼從來沒有工作過。
利用這個漏洞就不那么復(fù)雜了,因?yàn)轵?qū)動程序沒有使用堆棧cookie編譯過。在以前的漏洞中附加過一個本地特權(quán)提升漏洞利用程序,它配置了一個偽造的WebDAV服務(wù)器來利用漏洞(ROP,從主堆棧到用戶緩沖區(qū),再次ROP來分配 讀寫執(zhí)行內(nèi)存,用來存放shellcode并跳轉(zhuǎn)進(jìn)去)。
UVMLiteController中錯誤的驗(yàn)證
NVIDIA的驅(qū)動程序還在 \. UVMLiteController路徑中暴露了一個可以由任何用戶打開的設(shè)備(包括從沙箱中的Chrome GPU進(jìn)程)。該設(shè)備的IOCTL處理程序直接將結(jié)果寫入Irp->UserBuffer中,作為將要傳遞給 DeviceIoControl 的輸出指針 (微軟的文檔中指出不要這樣做)。IO控制代碼指定使用METHOD_BUFFERED,這意味著在Windows內(nèi)核檢查地址提供的范圍并將其傳遞給驅(qū)動器之前,用戶具有寫**作的權(quán)限。
然而,這些處理程序還缺少對輸出緩沖區(qū)的邊界檢查,這意味著用戶模式上下文可以通過任何任意地址傳遞值為0的長度(可以繞過ProbeForWrite的檢查), 這樣做的結(jié)果是創(chuàng)造出一個受限的Write-what-where情景(這里的“what“僅限于一些特定的值:包括32位0xffff,32位0x1f,32位0和8位0)。
在原始問題中附加了簡單的提權(quán)漏洞利用 。
遠(yuǎn)程攻擊途徑?
考慮到已發(fā)現(xiàn)的bug數(shù)量如此之眾,我做了一個調(diào)查,是否可以在不必第一破壞沙盒進(jìn)程的前提下,完全從遠(yuǎn)程環(huán)境中訪問其中任意一個bug(例如通過瀏覽器中的WebGL或通過視頻加速)。
幸運(yùn)的是結(jié)果似乎并非如此。但這并不令人驚訝,因?yàn)檫@里的易受攻擊的API是非常底層的,只有經(jīng)過許多層才能訪問得到(對于Chrome而言,需要經(jīng)歷libANGLE -> Direct3D運(yùn)行時和用戶模式驅(qū)動程序 ->內(nèi)核模式驅(qū)動程序),并且通常需要在用戶模式驅(qū)動程序中構(gòu)造有效的參數(shù)才能調(diào)用。
NVIDIA的回應(yīng)
發(fā)現(xiàn)的bug的性質(zhì)表明NVIDIA仍有很多工作要做。他們的驅(qū)動程序包含的很多可能不必出現(xiàn)在內(nèi)核中的代碼,而發(fā)現(xiàn)的大多數(shù)錯誤是非?;镜腻e誤。事到如今,他們的驅(qū)動程序(NvStreamKms.sys)仍然缺乏非?;镜木徑獯胧ǘ褩ookie)。
不過,他們的反應(yīng)倒是快速且積極的。大多數(shù)bug在截止日期之前已經(jīng)修復(fù)好了,并且他們自己內(nèi)部也在做一些尋找bug的工作。他們還表示,他們一直在努力重構(gòu)他們內(nèi)核驅(qū)動程序的安全性,但還沒有準(zhǔn)備好分享任何具體的細(xì)節(jié)。
時間線
補(bǔ)丁間隔
NVIDIA的第一個補(bǔ)丁,其中包括我報告的6個bug的修復(fù),但是沒有在公告中詳細(xì)說明(發(fā)布說明稱作“安全更新“)。他們原本計劃在補(bǔ)丁發(fā)布后一個月再公布詳細(xì)信息。我們注意到了這一點(diǎn)并告訴他們這樣做并不恰當(dāng),因?yàn)楹诳涂梢酝ㄟ^逆向補(bǔ)丁來找到之前的漏洞,而當(dāng)大眾意識到這些漏洞細(xì)節(jié)的時候已經(jīng)晚了。
雖然前6個bug修復(fù)后在30多天內(nèi)都沒有發(fā)布修復(fù)的詳細(xì)信息,但剩余的8個bug的修復(fù)補(bǔ)丁發(fā)布后5天內(nèi)就發(fā)布了細(xì)節(jié)公告。看上去NVIDIA也一直在嘗試減少這種差距,但是就最近的公告來看兩者的發(fā)布仍有很大的不一致性。
結(jié)論
鑒于內(nèi)核中的圖形驅(qū)動程序所暴露出來的巨大攻擊面,以及第三方廠商的低質(zhì)量代碼,它似乎是挖掘沙箱逃逸和特權(quán)提升漏洞的一個非常豐富的目標(biāo)。GPU廠商應(yīng)該盡快將其驅(qū)動代碼從內(nèi)核中轉(zhuǎn)移出去,從而縮小攻擊面使得這種情況得以限制。
拓展知識:
原創(chuàng)文章,作者:九賢生活小編,如若轉(zhuǎn)載,請注明出處:http://m.xiesong.cn/43676.html