Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86386127

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

在Windows上,第三方產品有多種方式將其代碼注入其他正在運行的進程。這樣做的原因有很多,最常見的是殺毒軟件、硬件驅動程序、屏幕閱讀器和銀行的需要,當然惡意軟件也會趁機而入。

將第三方產品的DLL注入Firefox進程是非常常見的,超過70%的Windows用戶至少有一個這樣的DLL!需要明確的是,這意味著沒有經過Mozilla或操作系統部分數字簽名的任何DLL。

大多數用戶不在Windows上,第三方產品有多種方式將其代碼注入其他正在運行的進程。這樣做的原因有很多,最常見的是殺毒軟件、硬件驅動程序、屏幕閱讀器和銀行的需要,當然惡意軟件也會趁機而入。

將第三方產品的DLL注入Firefox進程是非常常見的,超過70%的Windows用戶至少有一個這樣的DLL!需要明確的是,這意味著沒有經過Mozilla或操作系統部分數字簽名的任何DLL。

大多數用戶不知道DLL何時被注入Firefox,因為大多數時候除了檢查about:third-party page.之外,沒有明顯的跡象表明正在發生這種情況。

不過,將DLL注入Firefox可能會導致性能、安全性或穩定性問題。原因如下:

1.DLL通常會掛鉤到Firefox的內部函數中,這些函數會隨著版本的不同而變化。所以,第三方產品的發行商必須努力使用新版本的Firefox進行測試,以避免穩定性問題。

2.Firefox作為一種網絡瀏覽器,可以從不受信任和潛在的惡意網站加載並運行代碼。所以,安全研究人員需要付出很多努力來保護Firefox的安全,第三方產品可能對安全性沒有這麼關注。

3.研究人員在Firefox上運行了大量的測試,第三方產品可能不會測試到這種程度,因為它們可能不是專門為配合Firefox而設計的。

事實上,我們的數據顯示,在所有Windows上的Firefox崩潰報告中,只有2%以上是第三方代碼造成的。儘管Firefox已經阻止了許多已知會導致崩潰的特定第三方DLL,但情況依然如此。

這也低估了由第三方DLL間接引起的崩潰,因為研究人員的指標只在調用堆棧中直接查找第三方DLL。此外,第三方DLL在啟動時更容易導致崩潰,這對用戶來說要嚴重得多。

Firefox有第三方注入策略,只要有可能,我們建議第三方使用擴展來集成到Firefox中,因為這是官方支持的,而且更穩定。

為什麼不在默認情況下阻止所有DLL注入?為了獲得最大的穩定性和性能,Firefox可以嘗試阻止所有第三方DLL注入其進程。然而,這會破壞一些有用的產品,比如用戶希望能夠與Firefox一起使用的屏幕閱讀器。這在技術上也很有挑戰性,不可能阻止每個第三方DLL,尤其是使用比Firefox更高權限運行的第三方產品。

自2010年以來,Mozilla已經能夠為Firefox的所有Windows用戶屏蔽特定的第三方DLL。這樣做只是作為最後的手段,在嘗試與供應商溝通以解決潛在問題後,研究人員會盡可能嚴格地進行調整,以使Firefox用戶不再崩潰。目前研究人員只能阻止特定版本的DLL,並且只能在特定的Firefox進程中阻止它。這是一個有用的工具,但只有當特定的第三方DLL導致大量崩潰時,研究人員才會考慮使用它,這樣它就會出現在Firefox崩潰列表中。

即使我們知道第三方DLL會導致Firefox崩潰,但有時DLL提供的功能對用戶來說是必不可少的,用戶不希望安全人員代表他們阻止DLL。如果用戶的銀行或當地政府需要一些軟件來訪問他們的賬戶或報稅,我們屏蔽它不會給他們帶來任何好處,即使屏蔽它會使Firefox更加穩定。

賦予用戶阻止注入DLL的權限在Firefox 110中,用戶可以阻止第三方dll加載到Firefox中。這可以在about:third-party上完成,該頁面已經列出了所有加載的第三方模塊。 about:third-party還顯示了哪些第三方DLL與之前的Firefox崩潰有關,還有就是DLL發布者的信息也會顯示,希望這能讓用戶在知情的情況下決定是否阻止DLL。下面是一個最近導致Firefox崩潰的DLL示例,點擊帶有破折號的按鈕將阻止它:

1.png

以下是阻止DLL並重新啟動Firefox後的情況:

2.png

如果阻止DLL導致問題,在故障排除模式下啟動Firefox將禁用該運行的Firefox的所有第三方DLL阻止,並且可以像往常一樣在about:third-party上阻止或取消阻止DLL。

工作原理阻止DLL加載到進程中是一項棘手的業務,為了檢測加載到Firefox進程中的所有DLL,必須在啟動過程中儘早設置阻止列表。為此,使用啟動進程,它創建處於掛起狀態的主瀏覽器進程。然後,它設置任何沙盒策略,從磁盤加載阻止列表文件,並在啟動該進程之前將條目複製到瀏覽器進程中。

複製是以一種有趣的方式完成的,啟動程序進程使用CreateFileMapping()創建一個操作系統支持的文件映射對象,在用塊列表條目填充後,複製句柄並使用WriteProcessMemory()將句柄值寫入瀏覽器進程。具有諷刺意味的是,WriteProcessMemory()經常被用作第三方DLL將自己注入其他進程的一種方式,這裡我們使用它在已知位置設置一個變量,因為啟動器進程和瀏覽器進程是從同一個.exe文件運行的!

因為所有的事情都發生在啟動的早期,在加載Firefox配置文件之前,被阻止的dll列表是按Windows用戶而不是按Firefox配置文件存儲的。具體來說,文件位於%AppData%\Mozilla\Firefox中,文件名格式為blocklist-{install hash},其中install hash是Firefox磁盤上位置的哈希值。這是一種簡單的方法,可以將不同Firefox安裝的阻止列表分開。

檢測並阻止加載DLL為了檢測DLL何時試圖加載,Firefox使用了一種稱為函數攔截或掛鉤的技術。這會修改內存中的現有函數,以便在現有函數開始執行之前可以調用另一個函數。之所以如此,原因有很多,它允許更改函數的行為,即使函數不是為了允許更改而設計的。 Microsoft Detours是一種常用於攔截函數的工具。

在Firefox中,研究人員感興趣的函數是NtMapViewOfSection(),每當加載DLL時都會調用它。我們的目標是在發生這種情況時得到通知,這樣我們就可以檢查阻止列表,並禁止加載DLL(如果它在阻止列表上)。

為此,Firefox使用一個自定義的函數攔截器來攔截對NtMapViewOfSection()的調用,並返回如果DLL在阻止列表上則映射失敗的消息。為此,攔截器嘗試了兩種不同的技術:

在32位x86平台上,從DLL導出的一些函數將以一條不執行任何操作的兩字節指令(mov edi, edi)開始,並且在此(nop或int 3)之前有五條未使用的一字節指令,例如:

3.png

如果攔截器檢測到這種情況,它可以將未使用指令的五個字節替換為要調用的函數地址的jmp。由於研究人員是在32位平台上,因此只需要一個字節來指示跳轉,四個字節來表示地址,因此:

4.png

當修復的函數想要調用未修復版本的DLLFunction()時,它只需跳過DLLFunction()地址2個字節即可啟動實際的函數代碼

否則,事情會變得更加複雜。以x64的情況為例。跳轉到已修復函數的指令需要13個字節:10個字節用於將地址加載到寄存器中,3個字節用於跳轉到該寄存器的位置。因此,攔截器需要將至少前13字節的指令移動到一個蹦床函數中,如果需要的話,還要加上完成最後一條指令所需的足夠的字節。之所以被稱為蹦床,因為通常代碼會跳轉到那裡,這會導致一些指令運行,然後跳轉到目標函數的其餘部分。讓我們看看一個真實的示例,下面是我們要截取的一個簡單函數,首先是C源代碼(Godbolt編譯器資源管理器鏈接):

5.png

以上是用-O3編譯的,所以它有點密集:

6.png

現在,從fn()開始計算13個字節將我們置於lea eax,[rdi+rdi*2]指令的中間,因此我們必須將所有內容複製到蹦床上。

最終結果如下所示:

7.png

如果Firefox 修復函數想要調用未修復的fn(),那麼修復程序已經存儲了蹦床的地址(在本例中為0x3000000)。在C++代碼中,我們將其封裝在FuncHook類中,修復後的函數可以使用與普通函數調用相同的語法來調用蹦床。

整個過程比第一種情況要復雜得多,你可以看到第一個示例的修復只有200行左右,而處理這個示例的修復有1700多行,不過有些注意事項要注意:

1.並非所有轉移到蹦床上的指令都必須保持完全相同,一個示例是跳轉到一個沒有移動到蹦床的相對地址,由於指令已經在內存中移動了,修復程序需要用絕對跳躍來代替它。修復程序並不能處理所有類型的x64指令,否則它必須更長!但研究人員已經進行了自動化測試,以確保能夠成功攔截所知道Firefox需要的Windows函數。

2.研究人員專門使用了r11來加載修復函數的地址,因為根據x64調用約定,r11是一個不需要被調用方保存的易失性寄存器。

3.由於我們使用jmp從fn()返回到修復函數,而不是ret,並且類似地從蹦床返回到fn()的主代碼,這使代碼堆棧保持中立。因此,調用其他函數和從fn()返回都可以正確地處理堆棧的位置。

4.如果從fn()的後面跳轉到前13個字節,這些字節現在將跳轉到修復函數的中間,肯定會發生問題。幸運的是,這是非常罕見的。大多數函數在開始時都在進行函數序言操作,所以對於Firefox攔截的函數來說,這不是問題。

5.類似地,在某些情況下,fn()在前13個字節中存儲了一些數據,這些數據將被後面的指令使用,將這些數據移動到蹦床將導致後面的指令獲得錯誤的數據。我們已經遇到了這個問題,如果我們可以在前2GB的地址空間內為蹦床分配空間,那麼可以通過使用較短的mov指令來解決這個問題。這將導致10字節的修復而不是13字節的修復,在許多情況下,這足以避免問題。

其他一些需要注意的複雜情況:

6.Firefox也有一種跨進程攔截的方法;

7.對於Control Flow Guard安全措施來說,蹦床很棘手:由於它們是合法的間接調用目標,在編譯時不存在,所以需要特別注意允許Firefox修復過的函數調用它們;

8.蹦床還包括一些額外的異常處理;

9.如果DLL在阻止列表中,我們的修復版本NtMapViewOfSection()將返回映射失敗,這將導致整個DLL加載失敗。這不會阻止所有類型的注入,但它確實阻止了大多數注入;

一些DLL將通過修改firefox.exe的導入地址表來自我注入,該表是firefox.exe調用的外部函數的列表。如果其中一個函數加載失敗,Windows將終止Firefox進程。因此,如果Firefox檢測到這種注入並想要阻止DLL,我們將把DLL的DllMain()重定向到一個什麼都不做的函數。

總結希望讀者在閱讀本文後,可以讓Firefox用戶更加安全地訪問互聯網。用戶大可不必在卸載有用的第三方產品和Firefox的穩定性問題之間做出選擇,現在用戶有了第三種選擇,即保留第三方的產品並阻止其註入Firefox!