Thunderstrike: EFI bootkits for Apple MacBooks 重點筆記

吃飽飯閒閒沒事就看了鼎鼎大名 Thunderstrike 漏洞的演講錄影:https://www.youtube.com/watch?v=5BrdX7VdOr0

有2個方法可以讓目標執行任意代碼:ROM “in-system programming" 和透過 Thunderbolt Option ROM

ROM “in-system programming"

就是直接拆開機殼,用針腳去改 ROM 裡面的資料,這是可行的,因為這幾年的 Apple 電腦並沒有 TPM 晶片的保護,檢查 ROM 是否被篡改的程序本身也是存在 ROM 裡面,而且只有檢查 CRC32,沒有使用數位簽章,所以改了 ROM 之後只要重新計算 CRC 然後寫回去就可以了,也可以直接改寫 CRC 函式讓它直接 return true 。反正重點是沒有硬體保護,完全使用軟體檢查,所以只要打開機殼改 ROM 就可以為所欲為。

上面 CRC32 的檢查是針對 ROM 的資料。

雖然硬碟上的 EFI firmware volume (EFI 開機韌體磁區)有數位簽章保護,不過因為 EFI firmware volume 的檢查程式也存在 ROM 裡面,所以依舊很容易 bypass 。

Thunderbolt Option ROM

那有沒有方法不拆開機殼就改寫 ROM 呢?有的,透過 Thunderbolt 的向下相容 (legacy) 功能 Option ROM。在一般的開機流程中,執行 option ROM 的時候(DXE 階段) flash (ROM) 已經被鎖定了(早在 PEI 階段),因此就算執行了 option ROM 也沒辦法寫入系統 ROM 。

Thunderstrike presentation at 31C3

但有一個弱點,在韌體更新模式開機的時候,flash (ROM) 不會被鎖定,而 option ROM 也一樣會被執行,但又遇到一個障礙,recovery mode 開機的時候,在執行 option ROM 之前的 PEI 階段,會對 firmware volume (SCAP 檔案)進行 RSA 數位簽章檢查。

解法是,在驗證 SCAP 數位簽章,實際「掛載」firmware volume 之前(應該是指 ProcessFirmwareVolume 這個函式),option ROM 會被執行,而 ProcessFirmwareVolume 函式是透過 function pointer 呼叫,也就是在 RAM 裡面,而不是在 ROM 裡面,因此可以修改 function pointer 到我們想要的位置 (hooking) 。

Thunderstrike presentation at 31C3

在我們自定的 our_ProcessFirmwareVolume 函式當中,把 SCAP 中 Apple 的 public key 換成我們的,重新計算簽章和 CRC 校驗值,再呼叫原本的 ProcessFirmwareVolume 把修改過的 SCAP 寫入。

接下來,雖然 firmware volume 裡面的韌體還是原廠的,但是 public key 已經改成我們的了,接下來再刷我們改過的 SCAP 檔案(使用我們的 RSA 簽章)就會成功了。(但是後面的提問有提到,改掉 public key 之後,若再刷原廠的 SCAP ,就會變磚,因為 public key 已經是我們的了嘛,不過我們可以把判斷函式再寫複雜一些讓它會接受原廠的 SCAP)

所以這就是 Thunderstrike 漏洞,藉由 option ROM 觸發 recovery mode 開機,然後覆蓋 boot ROM ,就可以只認特定 firmware volume 的簽章,避免未來任何其他軟體手段覆蓋 firmware volume 。另外,作者的 proof of concept 也會停用 option ROM ,因此未來就無法再用同樣的方法觸發 Thunderstrike 了。

修補

在新的 Apple 修補韌體當中,recovery mode 將不會執行 option ROM 。但是這個修補並沒有辦法防止 Snare 的攻擊方法,把韌體降版至有漏洞的版本再進行攻擊。

Thunderstrike v2

另有 Thunderstrike 升級版,在作者演講的前一天,Rafal and Corey 發表了 Dark Jedi coma attack,從休眠 (ACPI state S3) 開機時,boot ROM 的鎖定會被清除,直接允許 option ROM 程式覆寫 boot ROM ,輕鬆達成攻擊,不需要前面提到的繞過數位簽章等麻煩事。

Q&A

在後面的提問當中,有人問到,在開機之後才插入 thunderbolt 裝置是否也會有風險?答案是不一定,若 thunderbolt 裝置是主動式,那隨時插入都會執行 option ROM ,但若是被動式,則只有在開機的時候才有風險。(不過照這樣看起來在休眠喚醒的時候 thunderbolt 若是插入的狀態也一樣有風險)

不過我沒有找到什麼叫被動式和主動式 thunderbolt 裝置的資訊。

所以若是電腦不在身邊回來發現被重開那就要小心了。

感想

整場演講我聽得懂九成感覺還蠻開心的(聽不懂的部分是技術真的聽不懂不是英文聽不懂),不過不要問我什麼是 PEI, DXE 哈哈。

另外,Club-Mate!

螢幕快照 2015-01-01 下午10.09.07

那瓶就是 Club-Mate 啊,果然是傳奇的駭客飲料,我去柏林黑客松的時候大家都在喝這個XDD

最後面的提問我覺得各個都很厲害,都提出非常簡短、明確,一針見血的問題,沒有人在嘴砲,不像在臺灣的研討會有些人問問題之前要把所有聽眾講師貴賓都問候一次然後提出落落長講完之後不知所云的模糊問題;這講者也超級厲害每個都清楚明確的詳加回答。

有一個提問者大概是 Coreboot 社群的人,問完之後直接說 We should talk ,真是太帥了。

演講的文字轉錄:https://trmm.net/Thunderstrike_31c3

HTC Android kernel write protection

HTC 的 ROM kernel 裏面都會有特製的函數使得某些分割區在 kernel (也就是 driver )層的寫入被阻擋。

就算檔案系統掛載模式是 rw 也沒用,因爲 kernel 裏面寫入 mmc 卡的 driver 根本就不支援寫入。

這種寫入保護應該就是大家常稱的 s-on / s-off ,不過我不確定。還有這樣的保護應該是在所有機型的 kernel 當中都有,不過我也只檢查過 One SV 的 kernel。

這樣的寫入保護使得取得 root 的實際用途大大地受限,因爲就算取得 root 之後還是很多地方沒辦法改。而且不像取得 root ,HTC 官方並不提供任何解開寫入保護的方法。

防寫功能由 CONFIG_MMC_MUST_PREVENT_WP_VIOLATION 這個編譯選項啓動。

防寫相關的函式在 drivers/mmc/card/block.c 。

get_tamper_sf() 會去讀 bootloader 給的參數 td.sf ,應該是辨別裝置是否被修改過的變數,至於 bootloader 是如何判別這件事情的就不清楚了。

wp_prevention_partno 變數存的是要保護的 partition ,但只有一個 partition 就有點奇怪了。這個變數由 arch/arm/mach-msm/emmc_partitions.c 的 parse_tag_msm_partition() 設定。

真正阻擋寫入的地方是在 block/blk-core.c 的 generic_make_request_checks() 當中

繞過的方法應該有2種:一是把 mmc0_write_prot_type 設成 0 ,二是把 wp_prevention_partno 設成某個不存在的 partition 。這兩種方法目前都無法驗證是否有效,因用爲這兩種方法把防寫解開之後真的寫入都會有這樣的訊息:


<6>[ 1303.057286] c0 mmc0: Applied soft reset to Controller
<6>[ 1303.057317] c0 mmc0: cid 1501004d38473246420a660a5f1acf64 (M8G2FB)
<6>[ 1303.057347] c0 mmc0: csd d02701320f5903fff6dbffef8a404066
<6>[ 1303.057378] c0 mmc0: fw_ver
<3>[ 1303.058446] c0 mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x900
<3>[ 1303.058476] c0 mmcblk0: not retrying timeout
<3>[ 1303.058537] c0 end_request: I/O error, dev mmcblk0, sector 791569
<3>[ 1303.058598] c0 end_request: I/O error, dev mmcblk0, sector 791577
<3>[ 1303.058629] c0 end_request: I/O error, dev mmcblk0, sector 791585
<3>[ 1303.058660] c0 end_request: I/O error, dev mmcblk0, sector 791593
<3>[ 1303.058690] c0 end_request: I/O error, dev mmcblk0, sector 791601
<3>[ 1303.058751] c0 end_request: I/O error, dev mmcblk0, sector 791609
<3>[ 1303.058782] c0 end_request: I/O error, dev mmcblk0, sector 791617
<3>[ 1303.058934] c0 Aborting journal on device mmcblk0p33-8.

似乎是 driver 的問題,目前還沒找到解法,需要更熟悉 Linux kernel 的相關知識……

回到上面兩種解法,想法都是基於 flar2 的 wp_mod.ko ,取得了 root 權限以後再插入特製的 kernel module 來變更 mmc0_write_prot_type 或 wp_prevention_partno 的值。

兩種方法的原始碼在 gist (feng.c 是我隨便命名的,就是風啦),順便附上 Makefile ,至於如何 compile 給 Android 的 kernel module ,我打算寫在另外一篇。

初探 Android kernel 漏洞

爲了想要取得 HTC One SV 的 root 權限,進行了這次研究。

通常要 root HTC 的手機必須先到 HTCDev 網站解鎖 bootloader ,安裝第三方 bootloader ,然後注入 su 執行檔。如此一來 HTC 就知道那一臺裝置解鎖了,未來維修的時候或許會拒絕。(雖然網路上爬文的結果大部分人都說只要有將 bootloader 重新鎖定,維修的工程師就不會去特別檢查解鎖記錄。非軟體問題在保固期間內就算有解鎖記錄,只要有重新鎖定,維修的價格還是有保固的優惠。這是臺灣的情形)這種官方解鎖也會將手機上的資料完全清除,因爲一旦解鎖就有可能取得 root 權限,可以取得 /data 分割區內的各種個人資料、帳號密碼。

我就在想,有沒有辦法不跟 HTC 註冊就取得 root 權限?

如果不找官方解鎖,取得 root 權限的方法還有一種,就是利用 kernel 本身的提權 (privilege escalation) 漏洞。開啓 USB 偵錯模式,使用 adb shell ,我們就有了本地一般使用者的權限,再上傳利用這種漏洞的執行檔,執行之後如果利用成功,就可以取得 root 權限。

這種漏洞很多,android_run_root_shell 工具整合了多種漏洞來取得 root 權限。

這種漏洞可以藉由分析 kernel 原始碼來取得,很多都是 overflow 和 pointer 的問題(但我不會分析啦)。就算不親自去分析,也有很多已經公開,甚至已經有 exploit 程式的漏洞,大部分手機的 kernel 都沒有修補。Android kernel 很多漏洞都是從 Linux kernel 移植過來的。出自 Linux kernel 的漏洞影響的裝置幾乎是全部(所有的裝置都可以利用這個漏洞);出自 driver 的漏洞就只影響有使用此 driver 的裝置。

研究 Android kernel 漏洞的人好像蠻多的,而且很多已經有 Proof of Concept 或是實際的攻擊程式,大概是因爲 Android 裝置很多,而且這類核心漏洞修補困難吧。(製造商客製化的 Android kernel 要套用 patch 比較麻煩吧,而且每一款裝置的 kernel 都略有不同)

CVE-2013-2094

又稱 perf_swevent_init exploit 。

這個漏洞的破解程式 android_run_root_shell 裏面有,有漏洞的裝置清單在這

這個漏洞有點麻煩,要能夠利用它得取得幾個 kernel symbol 的位址,可以藉由反組譯 kernel 來取得,爲了要反組譯 kernel 得先取得 kernel image (zImage) ,使用工具可以把 zImage 從 boot.img 分離出來。

boot.img 是從 /dev/block/mmcblk0p16 之類的裝置(每一款手機裝置節點不同)當中 dd 出來的映像檔,裏面包含 zImage 和 init ramdisk ,boot.img 格式的細節

再利用 arm-linux-androideabi-objdump (ndk 裏面有)反組譯 kernel ,取得 kernel symbol 位址

$ arm-linux-androideabi-objdump --disassemble-all -b binary -m arm --adjust-vma=0xc0008000 kernel.zImage > kernel.dasm

這步驟我不是很懂。後來找到一個 script 把分離 boot.img 和反組譯、找 symbol 全包了。總之就是要找到這幾個位址(別照抄這幾個位址,這不是臺灣版 One SV 的):

prepare_kernel_cred=0xc00b3cc4
commit_creds=0xc00b3814
remap_pfn_range=0xc0132584
ptmx_fops=0xc0ede13c
perf_swevent_enabled=0xc0ec7784

利用找到的 kernel symbol 位址去 android_run_root_shell 加上 One SV 的支援,改改 C 的 code ,加上裝置和位址的定義這樣,這還是在資工系學 C 以來第一次有實際用途…… 不過這個工具裏面的另一個 fb_mem exploit 會造成 segmentation fault ,執行不到這個漏洞就結束,所以要註解掉 fb_mem 的 code 。如何編譯 README 有寫了。

但……我最大的麻煩是,連 boot.img 都沒有辦法取得。不同版本不同地區的 kernel 略有不同,kernel symbol 的位址也不一樣,我在網路上找不到臺灣版 One SV 原廠 Android 4.1.2 的 boot.img

另一個取得 symbol 位址的方法是讀取 /proc/kallsyms ,但是從 2011 年初 Android 就引進了 patch ,預設在 /proc/kallsyms 的地址只顯示全零,必須要將 /proc/sys/kernel/kptr_restrict 設爲1,才會顯示真的地址。但 /proc/sys/kernel/kptr_restrict 限定只有 root 才能更改……

這就要靠下一個漏洞來解決了……

CVE-2013-2596

又稱 fb_mem exploit ,我在 One SV 實測確實可以取得 root 權限的漏洞。kernelchopper 是比較低階利用此漏洞的程式,可以任讀寫記憶體,我實測是可以讀,但我不敢亂寫記憶體所以沒試,所以這個漏洞在 One SV 是存在的,它寫的手動改記憶體取得 root 權限的方式我看不太懂,不會用。motochopper 是高階工具 ,原本是專爲 Galaxy S4 做的,我稍微修改一下執行用 script 讓它破解完不要重開機就可以給 One SV 用了。HTC 的手機都有額外的防寫機制,執行檔放進 /system 之後重開機會消失,這應該就是所謂的 S-ON / S-OFF (我還沒深入研究),所以破解程式送進去執行之後不能重開機。

取得了 root 之後,就可以改 /proc/sys/kernel/kptr_restrict 然後讀 /proc/kallsyms 了。不過都已經有 root 了,要那些 kernel symbol 地址只是要驗證 perf_swevent_init 漏洞而已。

android_run_root_shell 工具包裏面還有其他漏洞,不過我要先去研究 s-off 了。

使用 Wireshark + stunnel 來側錄 SSL 通訊

概述

起因是我想要看 VMware vSphere Client 的運作,但它有 SSL 保護着。

要看到它實際的通訊內容,就要在中間架一個 proxy ,強迫 client 使用 proxy (還要讓 client 接受 proxy 使用的假憑證),然後 proxy 再去跟原本的 server 溝通。

本來是這樣:client <—> server , client 會看到正常 server 的憑證

我要讓他這樣: client <—> proxy <—> server , client 與 proxy 之間使用的是 proxy 的假憑證(憑證是我們自己產生的,私鑰也在我們手上,所以可以解密 client 和 proxy 的通訊),proxy 和 server 之間則是使用正常的 server 憑證(我們沒有私鑰所以無法解密)。

我們要用 stunnel 來建 支援 SSL 的 proxy ,用 Wireshark 竊聽 client 和 proxy 之間的通訊,並將私鑰匯入 Wireshark ,使用 Wireshark 來解密。

環境:

  • client 跑在 KVM guest (虛擬機器)裏面,IP: 192.168.122.26 , windows 7
  • proxy 跑在 本機,同時也是 KVM host ,對 client 做 NAT ,對內 IP 是 192.168.122.1 (guest 的 default gateway 就設成這個), Ubuntu 12.04

設定 KVM guest 的簡單 NAT 我之後再寫一篇。總之先假設 KVM guest 的網路通了。

產生憑證

我是去 Webscarab 的 git repository 找到這個 script 來產生憑證的。

$ sh doc_cert.sh <servername>

這樣就可以產生憑證,servername 最好填真正 server 的 domain name ,如果沒有 FQDN ,就自己隨便寫一個 domain name 。

合併憑證和私鑰部分到同一個檔案以供 stunnel 使用:

$ cat sslcerts/servername-cert.pem sslcerts/private/servername-key.pem > blabla.pem

強迫 windows 使用 proxy

去 windows 的 hosts 檔案加入這一行:

192.168.122.1     <servername>

如此一來,這個 domain name 就會強制對應到我們 proxy 的 IP 。

啓動 proxy

有兩個部分,首先是啓動對 client 的 tunnel :

# stunnel -p blabla.pem -d 443 -r 8080

443 是要對 client listen 的 port ,8080 是要將 traffic redirect 到 port 8080

第二部分是 client mode 的 stunnel ( stunnel 假扮 client):

# stunnel -c -d 8080 -r servername:443

stunnel 自己扮演對 servername:443 建立 SSL 連線的 client ,traffic 來源是 port 8080

將私鑰匯入 wireshark

執行 Wireshark , 按 Ctrl+Shift+P 進入設定,在 Protocols 底下找到 SSL ,在 RSA keys list: 旁邊按 Edit , 然後按 新增 (Add) ;

IP address 填 proxy 的 IP ,192.168.122.1 ;

Port 填 proxy listen 的 port , 443 ;

Protocol 填解密後的 traffic 是什麼協定,Wireshark 解密後會呼叫這個 protocol dissector ,若不確定就填 data , Wireshark 就不會呼叫任何 dissector ;

Key File 填私鑰的路徑,/path/to/sslcerts/private/servername-key.pem

詳細說明見 Wireshark Wiki

開始竊聽

如果你要竊聽的 client 也是 KVM guest ,竊聽的界面請選 virbr0

若 Wireshark 沒有聽到完整的 SSL handshake 過程,就會無法解密哦,所以最好是開始竊聽之後再讓 client 開始發封包。

操作 client

就做你想要分析的動作就好了,像是登入之類的,在開始 SSL 連線的時候,client 應該會有一些警告說憑證有問題(未信任的憑證簽發者等等),這是當然的,因爲我們假造了憑證嘛,這時候只要按忽略就好啦。(不然 SSL 連線失敗的話你根本不能進行操作)

解密

做完你想要分析的動作,去 Wireshark 按 Stop Capture , 如果一切 OK , wireshark 應該就會直接把解密後的內容顯示出來囉。

不過我解密過程中的確是失敗了好幾次,有各種原因,需要瞭解 SSL 協定才能排除問題…之後再寫一篇吧。

成果

參考