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