超級(jí)任天堂游戲模擬器被曝安全漏洞
超級(jí)任天堂(SNES,Super Nintendo Entertainment System)是任天堂全球知名主機(jī)NES(國(guó)內(nèi)稱為小霸王)的后續(xù)主機(jī),主機(jī)采用16位色表現(xiàn),令主機(jī)的畫面表現(xiàn)在當(dāng)時(shí)非常之棒。而作為當(dāng)時(shí)的主機(jī)霸主,任天堂SNES主機(jī)上出現(xiàn)了非常多經(jīng)典的游戲,現(xiàn)在大紅大紫的游戲系列很多都是在SNES上發(fā)跡的。像勇者斗惡龍系列、傳說(shuō)系列、最終幻想系列、超時(shí)空之輪系列等等。當(dāng)年的超級(jí)任天堂憑借全球數(shù)千萬(wàn)臺(tái)的銷量一直占據(jù)著游戲機(jī)市場(chǎng)的鰲頭,在這個(gè)優(yōu)秀的平臺(tái)上,誕生過(guò)無(wú)數(shù)優(yōu)秀的精品游戲,很多作品即使拿到現(xiàn)在與PS2,XBOX的游戲相比在游戲性方面也毫不遜色(其實(shí)是比他們還要好很多)。如果你對(duì)那些令人激動(dòng)的游戲念念不忘的話,而Snes9x可以在你的電腦上網(wǎng)模擬超級(jí)任天堂,讓你再次回到童年。軟件可根據(jù)不同硬件方便的調(diào)節(jié)聲音,圖象等。
上圖中的那個(gè)程序是一個(gè) SNES 模擬器,很有游戲愛(ài)好者都使用過(guò)它。從網(wǎng)上找到這些東西并不難,但我不會(huì)在文章里說(shuō),因?yàn)閺募夹g(shù)上講,這么做屬于傳播盜版。
首先,我拿出舊的外部硬盤,找到了我的SNES9x模擬器的壓縮副本,啟動(dòng)它,就發(fā)現(xiàn)了一個(gè) DLL 劫持漏洞。
Snes9x是款讓你重溫童年游戲的SNES模擬器,能夠在Windows平臺(tái)上運(yùn)行經(jīng)典SNES/SFC游戲的模擬器軟件,支持.smc, .sfc, .fig, 和 .1等格式的游戲,可用鍵盤,也能用手柄進(jìn)行操作,支持鍵位修改。
以 x86 和 x64 架構(gòu)編寫的程序利用動(dòng)態(tài)鏈接庫(kù) (DLL) 在軟件開(kāi)發(fā)過(guò)程中提供靈活性和可移植性。 DLL 基本上是包含可重用代碼、資源和變量的小程序。就其性質(zhì)而言,它們沒(méi)有入口點(diǎn)并且需要父級(jí)可執(zhí)行文件在運(yùn)行時(shí)調(diào)用它們。如果你只有一個(gè) DLL,則可以使用 Rundll32.exe 之類的東西來(lái)運(yùn)行該特定 DLL 的內(nèi)容,而無(wú)需父級(jí)程序。否則,DLL 中的函數(shù)可以由父級(jí)可執(zhí)行文件調(diào)用,使用 LoadLibrary API 調(diào)用將它們動(dòng)態(tài)導(dǎo)入到程序中。
有時(shí)軟件開(kāi)發(fā)并不完全按計(jì)劃進(jìn)行,并且你可能會(huì)經(jīng)常編寫一個(gè)程序來(lái)調(diào)用加載一個(gè)不存在的 DLL?;蛘?,在 SNES 模擬器的情況下,你可能會(huì)遇到相當(dāng)于路徑漏洞的問(wèn)題,這基本上意味著程序嘗試從當(dāng)前工作目錄加載 DLL,然后在別處查找。
以下就是我加載Procmon并運(yùn)行SNES模擬器時(shí)的情況:
在上圖中,SNES 模擬器已放置在我的 FlareVM 主機(jī)的桌面上。當(dāng)程序嘗試加載opengl32.dll時(shí),它首先檢查當(dāng)前工作目錄C:\Users\Husky\Desktop\SNES32bit\。當(dāng)它在這里找不到指定的 DLL 時(shí),它會(huì)轉(zhuǎn)到SysWOW64目錄,并加載成功存在其中的DLL。這個(gè)SNES模擬器是一個(gè)32位應(yīng)用程序,因此它檢查SysWOW64是否需要dll是有意義的。
注意,System32和SysWOW64有點(diǎn)像冰島/格陵蘭的情況。在標(biāo)準(zhǔn)的x64設(shè)備上,64位系統(tǒng)目錄是System32, 32位系統(tǒng)目錄是SysWOW64。
總之,這是一個(gè)漏洞。程序試圖從一個(gè)可以寫入的目錄中裝入DLL。這是因?yàn)樵摮绦蛞驯粡?fù)制到桌面,而不是安裝在標(biāo)準(zhǔn)程序目錄中,例如“程序文件”。
這就很容易發(fā)生這個(gè)軟件被共享即從硬盤驅(qū)動(dòng)器共享它,然后將文件復(fù)制到他們自己的筆記本電腦上。
另外,在這里需要注意的是,SNES模擬器有DEP但沒(méi)有ASLR,將來(lái)可能會(huì)回到那個(gè)狀態(tài)。
現(xiàn)在在一些攻擊場(chǎng)景中,DLL劫持是通過(guò)以下方式實(shí)現(xiàn)的:只需要做一個(gè)MSFVenom DLL有效載荷,并將其替換為程序試圖加載4head的載荷即可。直到程序崩潰或無(wú)法加載:
DOS不在這個(gè)范圍內(nèi),要做得更好,可以進(jìn)入DLL代理。
DLL 代理
DLL 代理實(shí)現(xiàn) DLL 劫持的成功率更高。
你可能能夠從 DLL 劫持中獲得 shellcode 執(zhí)行,但程序仍然需要解析它想要從原始 DLL 進(jìn)行的函數(shù)調(diào)用。而且你的 MSFVenom 生成的 DLL 不知道如何處理這些請(qǐng)求的函數(shù),所以calc.exe運(yùn)行,程序崩潰。
在 DLL 代理中,你可以創(chuàng)建一個(gè)新的 DLL,其中包含指向原始 DLL 的導(dǎo)入函數(shù)的指針。有效載荷隱藏到這個(gè)DLL的一個(gè)部分中,并使用剩余的空間將可執(zhí)行文件指向它想要加載的原始DLL。
該程序以其原始預(yù)期功能執(zhí)行,得到有效載荷執(zhí)行。
使用 Procmon,過(guò)濾以下內(nèi)容:
打開(kāi)一個(gè)可執(zhí)行文件或 10 并確定一個(gè)嘗試加載到 DLL 中但沒(méi)有成功的程序。在本文的示例中,我專注于 OpenGL32.dll:
重新配置過(guò)濾器,以查看該DLL是否在程序執(zhí)行的任何時(shí)刻被成功加載。因此,只需過(guò)濾即可擴(kuò)大你的搜索條件:
如果你已經(jīng)找到一個(gè)好的DLL代理候選,你可能會(huì)看到如下內(nèi)容:
可以看到,將成功加載的 DLL 從主機(jī)復(fù)制到攻擊者的設(shè)備上,并將其命名為 [dllName]_original.dll。幸運(yùn)的是,這個(gè)DLL可能在所有Windows系統(tǒng)上都是本地的,因此你甚至可能不需要從目標(biāo)主機(jī)復(fù)制它,只需從你自己的 Windows 主機(jī)復(fù)制它即可。
在攻擊者的設(shè)備上,使用這個(gè)簡(jiǎn)單的Python腳本從原始DLL中取出導(dǎo)出的函數(shù)并將它們寫入一個(gè)模塊定義文件 (.def):
請(qǐng)注意引用原始DLL的兩個(gè)位置,并確保相應(yīng)地更改這些值。
在編譯程序時(shí),模塊定義文件向編譯器提供有關(guān)鏈接導(dǎo)出的信息。在本文示例中,我們將告訴編譯器創(chuàng)建代理DLL,并將它鏈接到原始DLL,以指向它導(dǎo)出的所有函數(shù)。
所以,每當(dāng)可執(zhí)行文件問(wèn)“嘿,DLL,函數(shù)在哪里?”代理DLL便回復(fù)到:“哦,是的,去檢查[dllName]_original.dll,它應(yīng)該在那里。”
現(xiàn)在,我們制作我們的代理 DLL。這是一個(gè)用 C 編寫的非常簡(jiǎn)單的程序,它導(dǎo)出一個(gè) DLLMain 函數(shù)作為 DLL 的入口點(diǎn)。在 DLLMain 方法中,我們偷偷調(diào)用了 Payload 函數(shù)。該函數(shù)執(zhí)行,然后所有其他請(qǐng)求的函數(shù)調(diào)用都傳遞給 [dllName]_original.dll:
創(chuàng)建此 C 文件并將其命名為 [dllName].c:
到目前為止,一切順利。
最后,我們需要?jiǎng)?chuàng)建這個(gè)代理 DLL 并將它與我們的模塊定義文件鏈接起來(lái)。這可以通過(guò) mingw-w64 來(lái)完成,它擁有編譯DLL所需的必要工具鏈。注意這里的架構(gòu):我代理了32位SNES模擬器,所以我需要為32位架構(gòu)編譯它。
如果你的攻擊設(shè)備上沒(méi)有 mingw-w64,只需輸入:
然后,編譯我們的 DLL:
當(dāng) DLL 編譯時(shí),你現(xiàn)在應(yīng)該有四個(gè)文件可以使用:
- [dllName]_original.dll:我們從主機(jī)上拉下來(lái)的原始DLL,重命名為“_original”
- [dllName].c:包含我們的有效載荷的代理 DLL 的 C 代碼。
- [dllName].def:使用Python腳本從原始dll中提取導(dǎo)出的函數(shù)調(diào)用創(chuàng)建的模塊定義文件。
- [dllName].dll:新編譯的代理DLL。
最后一步是將原始DLL和代理DLL在目標(biāo)主機(jī)的程序目錄下登陸,這兩個(gè) DLL 必須與彼此和原始程序位于同一目錄中。
找到兩個(gè) DLL 后,啟動(dòng)程序!
此時(shí),已經(jīng)有了一個(gè)加載到代理DLL中的功能完整的程序。沒(méi)有崩潰,沒(méi)有段錯(cuò)誤,只有calc。
本文翻譯自:https://huskyhacks.dev/2021/08/29/dll-hijacking-dll-proxying-an-snes-emulator/