原文:hexdetective

作者:Nitay Artenstein (@nitayart) and Gilad Goldman (@gnull00)

譯:Holic (知道創宇404安全實驗室)

三星的 Android 安全引導程序(S-Boot)是其信任鏈概念的核心。 攻擊者利用 S-Boot 可能會加載不受信任的內核和系統映像這點,以此繞過大多數手機的安全機制。

這是眾所周知的攻擊思路。它經常被 Android rooting 和 mod 社區使用,而我猜它大概更受執法和政府機構喜愛。

更有意思的是,S-boot 啟動時存在幾個內存破壞漏洞,其中一個可用于在引導加載程序中實現完整的代碼執行。

我們目前在 Exynos 芯片上確認存在該漏洞。貌似在三星S5,S6,S7 上有 90% 使用了 Exynos 的 ROM。最新的 S7 的 ROM 似乎修復了這個漏洞,而我們會在這幾天內確認這一點。

漏洞覆蓋面很大,那么我們將把它分成兩篇文章。在本文中,我們將重點介紹一些 S-Boot 內部機制,然后探索加載程序的攻擊面并獲得基本的調試功能。我們以發現一個有趣的攻擊面作為結束。在下一篇文章中,我們將公開真正的漏洞并利用它得以在 S-boot 中代碼執行。

我們不會深入太多逆向 S-Boot 的基礎知識,比如 IDA 加載或查找基址。Fernand Lone Sang (@_kamino_) 會放出一篇關于本文的精彩文章,當它發表之時,我會提供鏈接。如果你需要其他幫助,私信我即可,我很樂意提供幫助。

理解 S-Boot

三星的 Android 啟動過程開始于 Boot ROM 中運行的代碼,使用了 OEM 公鑰(在三星設備中被稱為三星安全啟動密鑰(SSBK))驗證下一階段引導加載程序的完整性。然后它將兩個獨立進程加載到內存中:一個是 S-Boot 本身,另一個是在 TrustZone TEE (可信執行環境)中運行的 “安全世界”。

這兩個進程協同工作。TEE 操作系統,在Exynos 處理器的情況下對應 Trustonic(之前的 MoboCore),由 S-Boot 調用,以驗證鏡像在加載之前的簽名。因此 S-Boot 或 TEE 中的任何一個都可能成為整個系統的潛在風險。

S-Boot本身分為兩部分:第一階段 Bootloader BL1 由 Boot ROM 調用并初始化低級系統原語。BL1 在驗證其簽名后跳入 BL2,這是自身已經是最小的操作系統,完成了對 USB,顯示器和 I/O 的驅動支持。

既然我們有興趣找到 bug,那么讓我們破壞啟動的引導過程吧,我們決定找到它盡可能接近實際的內核啟動方式。這是因為我們知道已經有了一個初始化的系統,,進一步的操作,比如磁盤 I/O - 我們需要做的是刷入自定義的鏡像 - 做一點微小的工作。因此,我們決定在此階段跳入 BL2 并忽略 BL1 (雖然我們確定此后會破壞這一階段)。

此階段下,沒有任何調試功能,只是 sboot.bin 與三星 Exynos 標準鏡像合并在了一起。那么我們在 IDA 中打開它,對 BL2 進行研究。

A typical function in BL2. Notice the quantity of strings

其實挺簡單的:知道了 BL1 主要負責底層初始化,而 BL2 幾乎是一個全功能的操作系統,我們可以由此得出結論,屬于 BL2 的功能肯定更多,而且具有更多的調試字符串和其他函數引用。一旦我們確定 BL2 的位置,我們使用經典的逆向技巧即可確定內存中映像的基址。

在更高的層次上,BL2 有幾個有趣的任務,包括且不僅限于:

  1. 啟動內核
  2. 閃存刷入新的固件鏡像
  3. 在固件更新期間顯示基本的用戶界面
  4. 調試(運氣好的話)

BootLoader 階段,加載新固件映像的機制通常是最好的攻擊面,因為它涉及到與攻擊者的輸入直接交互以及各種復雜的邏輯。那么下面就是我們首先看到的。

Into Odin

對三星 Android 手機有過研究的人都知道 Odin,這是個可敬但有些笨重的軟件,它的功能是將固件 ROM 刷入設備存儲器中。

在設備端,刷入新的固件要先把手機切換到下載模式,這個在 S-Boot 中得以實現,然后通過 USB 連接到運行 Odin 客戶端的主機。然后 Odin 客戶端將選中的固件鏡像發送到設備上運行的 Odin 服務器。當然,你不能刷入任何鏡像,在有鎖的三星設備上,BootLoader 將拒絕未簽名的三星固件。

Download mode. Locked bootloaders reject unsigned images

在 BootLoader 一端,Odin 引導 BootLoader使用了相當全面的協議,通過 USB 接收傳輸數據。這就是我們率先發力之處。

如果想繼續跟進分析,我們使用的 ROM 版本是 G930FXXU1APF2。這是三星 Galaxy S7 的 ROM,可以直接從 Sam Mobile 下載。

Odin 處理程序代碼中有個關鍵函數是 process_packet(位于地址 0x8F00A0A4 處),它幾乎處理了所有的 Odin 協議。剛讀到函數代碼我們就遇到一個問題:

The beginning of process_packet

如你所見,Odin 協議查找數據包 ID,并選擇代碼分支。數據包 ID 0x65 告訴 Odin 要進行一個 PIT 文件相關的操作(PIT 包含分區信息,可參考此 XDA 串)。

當代碼運行至 ID 0x65,它會將當前 PIT 文件讀取到緩沖區,或者寫入一個新的 PIT 文件到保存 PIT 數據的特殊分區。如果第二個字節是 1,Odin 繼續將當前 PIT 復制到緩沖區,然后傳遞至 Odin 客戶端。客戶端需要它來確認新的固件是否適合當前分區方案。

但是 PIT 被復制的目標緩沖區(xfer_data.pit_buf)在哪里被初始化呢?顯然,它在這種情況下被分配:

The allocated of pit_buf

這意味著你必須在緩沖區分配之前發送初始化數據包(ID 0x64)。如果沒有的話,緩沖區是指向 0 地址的。如果你試圖在緩沖區分配之前復制 PIT ,代碼會繼續向 0 地址空間復制:經典的空指針解引用漏洞。

這個類似于我們在 Odin 中發現的其他漏洞,它能使 BootLoader 崩潰,但不一定能利用。這種情況下,鑒于 ARM64 架構,0 地址空間沒有映射,任何嘗試復制到這兒的操作都會使它馬上崩潰,而在 ARM32 體系上就不是那么糟糕了,因為0地址可能包含被覆蓋的異常向量表(EVT)。那么問題來了,我們寫入的數據依然是不可控的,因為我們無法控制 PIT 數據。

但是這個漏洞確實給了我們很多額外的東西。當我們觸發漏洞并崩潰 BootLoader 時,屏幕上會顯示什么?

Inside Upload Mode

Dumping Memory

代碼表明 BootLoader 處理的異常輸出到了屏幕上面,然后輸入參考引用為“上傳模式”的東西。這個方面的開發就有意思了:上傳模式是一個半密碼引導的 BootLoader 模式,且已經困惑了mod社區多年。有些用戶報告內核恐慌后得以觸發,也有人說,它是由于 PMIC 問題。現在我們知道了可以在 BootLoader 異常的時候進入該模式。

觀察對應代碼,我們看到上傳模式位于 usbd3_rdx_process(對應地址 0x8F028C1C),屬于內聯函數。我對代碼進行了一些修改簡化,以便觀察。

mode_switch = p_board_info->mode_switch;
if ( mode_switch & UPLOAD_MODE )
{
  if ( !transaction_data.response_buffer )
  {
    transaction_data.response_buffer = (char *)malloc(0x80000);
    if ( !transaction_data.response_buffer )
    {
      printf("%s: buffer allocation failed.\n", "usbd3_rdx_process");
      goto INFINITE_LOOP;
    }
  }
  if ( !strcmp(packet_buf, "PoWeRdOwN") )
  {
    goto POWERDOWN;
  }
  if ( !strcmp(packet_buf, "PrEaMbLe") )
  {
    memcpy(transaction_data.response_buffer, "AcKnOwLeDgMeNt", 15);
    goto SEND_RESPONSE;
  }
  if ( !strcmp(packet_buf, "PrObE") )
  {
    memcpy(transaction_data.response_buffer, log_location_buf, log_location_buf_size);
    goto SEND_RESPONSE;
  }
  ...
  dump_start_addr = strtol(packet_buf, NULL, 16);
  dump_end_addr = strtol(packet_buf + 9, NULL, 16);
  ...
  (some length checks)
  ...
  memcpy(transaction_data.response_buffer, dump_start_addr,   dump_end_addr - dump_start_addr);
  goto SEND_RESPONSE;

這是個相當基礎的協議,用于設備的內存轉儲。發送一系列初始化數據包后,只需再發送轉儲起始地址和結束地址,然后通過 USB 恢復轉儲。

這對調試和逆向分析很有用,因為我們可以在崩潰之后轉儲內存,查看寄存器和堆棧,然后弄清發生了什么。當然還可以 dump 全部內存以幫助我們逆向。我們將會看到這種能力在文章第二部分很有用。

由于我們沒有找到公開的工具用來通過上傳模式轉儲 RAM,然后我自己寫了個

Fuzzing Odin

這時,我們回到 Odin 協議,希望找到一個可利用的 bug。進入新的攻擊面之時,我們要寫出基本的 fuzz 工具,以便盡快達成目標。

實施證明 S-Boot 還是有點難度的,因為它使用 CDC ACM(一種串口形式)的專有協議,很難使其正常運行。一些小細節至關重要:比如,你必須在每個標準數據包后發送一個空包,一些數據包需要 1024 字節,即使它實際只包含 4 個字節的有效數據。時間有限,從零開始 fuzz 就太慢了。

Benjamin Dobell 的 Heimdall 此處就該登場了。Heimdall 是個開源的 Odin 客戶端協議交互工具,它輔助處理與 Odin BootLoader 交互的部分,所以我們將其作為 fuzzer 的基礎,然后進行一些擴展。

我們添加了一個名為 “fuzz” 的命令行選項,僅需一些原始數據包,可以使用 Python 預先生成,然后將它們依次發送至設備,同時處理底層細節。點此下載

我們使用這種方法在 Odin 中發生了一些有趣的崩潰,但貌似不能利用。此時我們決定花一些時間擴展 Odin 應用,那么就需要更深入的研究 Odin。下面是我們的一些有趣的發現。

The UART Console

搜索二進制,我們在 0x8F08BD78 處找到一些可疑的字符串指針。

The possible command list

看起來像是配對的命令名稱和描述,可能是某種終端用于診斷的命令 - 在其他嵌入式項目中挺常見的,但此處并不是我們想要的。

要是有某種串口,能使我們連接到這個終端就好了,我們發現 XDA 的成員已經做到了這一步

事實證明,三星已經留下了一個 UART 終端,可以通過 BootLoader 打開,通過它暴露了一些低級的命令用戶服務診斷。其中一些命令可以使用特殊參數啟動內核,從存儲器讀取或寫入,并觸發各種 USB 模式。

然而,自從 2012 年 XDA 發布以來,還沒有人對這些終端的輸入做公開報告,使很多研究人員以為這個借口已被切斷。我們想要對這一假設進行測試。

進一步閱讀后,尤其是 Michael Ossmann和Kyle Osborn 2013年的Black Hat 演示,我們意識到三星手機以及所有的 Google Nexus手機都有一個多路復用 IC(MUIC),它位于 USB 連接器和 USB 控制器之間。通過檢測 USB 連接器上的 ID 和接地直接的電阻,多路復用器在設備上切換了不同的連接路徑。

公開文檔有記錄這兩個路徑的是普通的 USB 和 USB OTG。另一種模式,在公共文檔中沒有提到的是 UART。

The Samsung Anyway

然后調整設置,以獲取該沒有公開記錄的 UART 的連接方式。調用的第一個端口便是Samsung Anyway Jig,這是三星比較隱秘的一個設備。這個東西通常只有三星工程師使用,很難搞到,盡管它每一段時間就會出現在 eBay 上。

顯然,Anyway 只是為ID 引腳設置了幾個預定義的電阻電平,并將 D+/D- 線分接到 DBUS 連接器,然后就可以通過串口 USB 適配器連接到 PC。

在 eBay 上收了個二手的,然后我測試了各種開關組合,試著讓 MUIC 切換到 UART 終端模式。這在舊三星機器上管用,而我們只是成功獲取到輸入交互 - 我們從 BootLoader 和內核獲取日志,但是并沒有得到終端。

在此階段,我們決定自己制作一條臨時的 UART 電纜,類似于 Joshua Drake 的 Nexus 4 UART 電纜。我們從 XDA 收集了一些關于 ID 引腳電阻以及相關制造商的各種數據,還從內核 DTS 文件中得到一些提示。下面假想電路圖:

Our makeshift jig

由于需要控制一系列電阻,我們使用了一個可變電阻,將其設為需要的值(萬用表測量),并將其連接到 S7。

jig 相當簡單:RS232-to-USB 的TX / RX線連接到micro USB連接器的D/D- USB線,ID 引腳通過可變電阻連接接地引腳。

結果正確的電阻值是 619K 歐姆。設置成此電阻的時候,能夠在啟動設備時得到一些輸出。但是并不完美,輸出幾行后就沒反應了 - 我們仍然無法得到一個終端。

The initial UART output. Logs went silent after ifconn_com_to_open

為了深入理解問題的根源,來看看標記為 get_initial_uart_str(0x8F006ECC 的函數。似乎 UART 控制臺只有在此函數返回非空時才會啟動:

get_initial_uart_str

這里特別是 LABEL_9 ,我們可以看到 BootLoader 要求在進入控制臺模式之前至少有四個連續的回車。

此時一目了然:通過連接 jig 啟動時按下 “回車” 鍵,同時按下音量和電源按鈕,我們試著清除了 ifconn_com_to_open 的檢測和終端校驗。

最后,我們的付出得到了回報:

如你所見,控制臺暴露了一些非常有趣的命令。而下一篇文章將會更加有趣。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/237/