【译】BINDER - ANALYSIS AND EXPLOITATION OF CVE-2020-0041
2019年12月,在Linux內核中推送了新的Binder提交。?此補丁修復了用于處理Binder事務中特定類型的對象的索引的計算。
本文研究了已更正問題的含義,為什么是安全漏洞以及如何利用它。
在閱讀本文之前,強烈建議先閱讀有關粘合劑內部的文章。
相關補丁和版本
CVE-2020-0041發布于2020年3月的Android安全公告中。
附加到此CVE的修補程序是提交16981742?,其說明如下:
活頁夾:修復num_valid的錯誤計算對于BINDER_TYPE_PTR和BINDER_TYPE_FDA交易,num_valid local計算錯誤,導致 在binder_validate_ptr()中進行范圍檢查以錯過越界 抵消。 修復:bde4a19(“活頁夾:使用用戶空間指針作為緩沖區空間的基礎”)描述中提到了binder_valid_ptr()函數out-of-bounds?。?這似乎是安全修復程序!
該錯誤是通過代碼重構(commit?bde4a19?)在2019年2月引入的。?實際上,幾乎沒有設備受到影響,因為大多數供應商都使用舊的內核,并且此漏洞僅影響最新的內核。?據我所知,只有Android 10上的Pixel 4和Pixel 3 / 3a XL受到影響:
- 像素4-內核msm-coral-4.14-android10
- Pixel 3 / 3a XL-內核msm-bonito-4.9-android10
補丁概述
差異--git a / drivers / android / binder.cb / drivers / android / binder.c 索引e9bc9fc..b2dad43 100644--- a /驅動程序/android/binder.c+++ b / drivers / android / binder.c@@ -3310,7 +3310,7 @@活頁夾大小struct binder_fd_array_object * fda =to_binder_fd_array_object(hdr);-size_t num_valid =(buffer_offset-off_start_offset)*+ size_t num_valid =(buffer_offset-off_start_offset)/sizeof(binder_size_t);struct binder_buffer_object *父=binding_validate_ptr(target_proc,t-> buffer,@@ -3384,7 +3384,7 @@t->緩沖區-> user_data + sg_buf_offset;sg_buf_offset + = ALIGN(bp-> length,sizeof(u64));-num_valid =(buffer_offset-off_start_offset)*+ num_valid =(buffer_offset-off_start_offset)/sizeof(binder_size_t);ret = binding_fixup_parent(t,線程,bp,off_start_offset,該修補程序修復了num_valid索引的計算,該索引在binder_fixup_parent()的調用中用作參數。?乘號*被除法/代替。
當活頁夾事務包含活頁夾對象時,偏移量列表將給出不同活頁夾對象在事務緩沖區中的位置。
?
活頁夾交易的沖銷緩沖區
讓我們舉個例子,如果對象的偏移量為0x10?(上圖中的對象BINDER_TYPE_PTR C),則索引的正確值應為0x2?:
//正確的版本size_t num_valid = (buffer_offset - off_start_offset) / sizeof (binder_size_t);/ *如果(buffer_offset-off_start_offset)= 0x10num_valid = 0x10 / 0x8num_valid = 0x2* /在易受攻擊的版本中,計算得出的值為0x80?。
//版本不正確size_t num_valid = (buffer_offset - off_start_offset) * sizeof (binder_size_t);/ *如果(buffer_offset-off_start_offset)= 0x10num_valid = 0x10 * 0x8num_valid = 0x80* /那是完全不同的!?越野車版本允許通過偏移緩沖區(藍色)發送帶有父索引的活頁夾對象。
?
偏移緩沖區越界
函數binder_validate_ptr()使用num_valid并檢查兩件事:
- 如果給定索引(此處為off_start_offset?)小于num_valid?,則該函數僅信任已處理的對象。
- 在索引(?off_start_offset?)中找到的偏移量處是否存在有效的binder_buffer_object?(使用幻數進行檢查)。
即使父索引存在出界(讀訪問),也只能訪問內存的有限部分,因為內核會驗證內存在接收方事務緩沖區中。?此外,在對象的開始處需要一個幻數,因此偏移量必須指向該幻數。?此外,如果魔術不正確,內核不會泄漏該值。
目前,該錯誤的影響很難看到。?要了解可能的利用,我們需要對Binder中的對象父系統有更好的了解。
有活頁夾的父母
活頁夾對象BINDER_TYPE_PTR和BINDER_TYPE_FDA具有一個字段parent和parent_offset?,該字段允許在父緩沖區內修補指針。?此功能由HIDL語言(硬件服務)使用,并且在上一則有關資料夾內部的文章中進行了解釋。
HIDL_STING示例
hidl_string結構是使用BINDER_TYPE_PTR父母的一個很好的例子。
//從system / libhidl / base / include / hidl / HidlSupport.h中提取struct hidl_memory {// ...私人的:hidl_handle mHandle __attribute__ ((aligned( 8 )));uint64_t mSize __attribute__ ((aligned( 8 )));hidl_string mName __attribute__ ((aligned( 8 )));};當進程A?hidl_string進程B發送hidl_string?,該結構包含一個屬于進程A的指針。為了使該結構在接收方進程內存中有效,Binder驅動程序必須通過屬于該內存空間的指針對其進行更改。這是由BINDER_TYPE_PTR?parent和parent_offset字段完成的。
// BINDER_TYPE_PTR的結構structinder_object_header {__u32 類型;};structinder_buffer_object {結構 binder_object_header hdr;__u32 標志;binding_uintptr_t 緩沖區;binding_size_t 長度;binding_size_t 父對象; //父對象的索引(在偏移緩沖區中)活頁夾大小 //偏移以修補父緩沖區};為了發送hidl_string?,第一個緩沖區(A)用于發送hidl_memory?C結構,第二個緩沖區(B)用于存儲實際字符串(在這種情況下為“我的演示字符串”)。?緩沖區A是B的父級,而A的偏移量0是使用目標過程存儲器中字符串的地址修補的。
?
帶有hidl_string的Binder父示例
家長修正規則
一些規則限制了在綁定對象中使用父對象。?當然,在調用此檢查之前,綁定程序內核已經檢查了指向緩沖區及其大小的指針是否有效(指向調用者內存的指針)。
通過調用binder_fixup_parent()檢查有關父代活頁夾對象層次結構的這些規則。
內核應用的規則如下:
通過活頁夾檢查規則()
- [1]父索引必須小于或等于num_valid?。?內核已驗證num_valid之前的所有對象。
通過活頁夾檢查規則()
- [2]僅允許對已驗證的最后一個緩沖區對象或其父對象進行修復
- [3]我們僅允許緩沖區內的修正在偏移增加時發生
對于本文的下一部分,這些以前的規則已由[1]到[3]的數字標識。
規則檢查示例(有效)
為了驗證事務的所有綁定程序對象,內核會按照它們在偏移緩沖區中注冊的順序檢查它們。?請記住,此緩沖區包含一個偏移量列表,其中活頁夾對象存儲在事務數據緩沖區中。
?
有效的活頁夾父母
此示例有效,并遵守所有規則。
規則檢查示例(無效1)
?
無效的父級偏移
該示例無效,因為它違反了規則[3]:
我們只允許緩沖區內的修正以增加的偏移量發生
規則檢查示例(無效2)
在下圖中,內核檢查與對象D對應的偏移量時,驗證失敗。
?
無效的父母
該示例無效,因為它違反了規則[2]:
僅允許對最后一個經過驗證的緩沖區對象或其父對象進行修復
在我們的例子中,最后一個驗證的對象是C?,而對象D的父對象是B。?但是B不是C或A?(C的父母)。?層次結構無效。
如何利用漏洞?
利用此錯誤的一種有趣方法是使父緩沖區的字段buffer和length具有任意值。
該錯誤允許輕松繞過規則[1],但是很難繞過規則[3]。
開發理念
訣竅是在驗證過程中更改父級層次結構。?這可以使用extra緩沖區來完成!?確實,內核使用緩沖區的這一部分來存儲與BINDER_TYPE_PTR對象有關的數據。?如果活頁夾對象的父索引指向該多余部分,則當內核在此位置復制數據時,其父對象將被更改。
內核驗證-初始配置
要執行此漏洞利用,需要具有以下層次結構的三個緩沖區對象(添加一個未在偏移列表中注冊的緩沖區):
?
對象D包含任意數據。
我們在計算num_valid使用此漏洞為對象B和C設置相同的父對象,并設置其父索引引用額外的數據部分(紫色區域)。?在驗證之前,多余的部分未初始化,并且包含以前的活頁夾交易記錄的數據。?這些數據可以通過發送事務并使用所需的offset A值向緩沖區噴灑來控制。
?
內核驗證-選中C時暫停
內核從offset A開始檢查偏移量列表中包含的對象。?直到C的所有對象都是有效的。?每次處理對象時,都會如下圖所示在多余部分中復制其相關緩沖區。
?
該圖顯示了算法的狀態。?內核已經檢查了緩沖區A和B?,但未檢查對象C。?此時,?parent_index的偏移量值尚未修改(設置為與offset A對應的值),并且最后一個驗證的對象為B。
?
內核驗證-對象C
活頁夾驅動程序處理對象C時,它首先在多余的部分復制其緩沖區。?但是,此副本將覆蓋parent_index的先前值。?準備緩沖器C的數據以用與對象D相對應的新值來改變該值。
?
此時,父母的等級會發生變化!
?
遵守所有規則!?實際上,?C的父對象(此處為D?)必須是最后一個經過驗證的對象或其父對象之一。
使用此配置,我們已繞過了binder_validate_ptr()和binder_validate_fixup()
內核驗證-父修補
一旦對象C通過所有檢查,內核便通過調用函數binder_alloc_copy_to_buffer()修補父緩沖區。
//提取binder.c靜態 詮釋 粘結劑_固定up_父母 (...){//在這里檢查規則[...]buffer_offset = bp- > parent_offset +( uintptr_t ) parent- > 緩沖區 - ( uintptr_t )b- > user_data;binding_alloc_copy_to_buffer( & target_proc- > alloc, b, buffer_offset,& bp- > 緩沖區, sizeof (bp- > 緩沖區));請記住,執行此代碼時,不會映射目標進程。
所有/ dev / binder設備都可以在內核內存中訪問。?用戶界面進程映射了活頁夾文件描述符時。?內核分配頁面(在此處使用kzalloc),并將這些頁面映射到進程內存中。
在綁定程序事務期間,內核可以通過在接收方進程的內存地址上應用偏移量來檢索此分配的內核地址。?在正常調用中,?parent->buffer的值屬于目標進程的/dev/binder內存,因為該值先前是在處理父對象時由內核修補的。?驅動程序可以通過以下計算獲得相應的內核地址:
kernel_proc_buffer = 父 -> 緩沖區 -b- > user_data使用我們的漏洞,我們可以部分控制kernel_proc_buffer因為b->user_data是未知的。
寫入父緩沖區的值(加上偏移量)是當前對象的地址(在我們的示例中,是目標進程內存中對象C的地址:另外)
不幸的是,對于我們的利用而言,函數binder_alloc_copy_to_buffer()對要修補的地址執行了附加檢查。
//在drivers / android / binder_alloc.c中int binding_alloc_copy_to_buffer ( struct binder_alloc * alloc,struct binder_buffer * 緩沖區,binding_size_t buffer_offset,無效 * src,size_t 字節){return binding_alloc_do_buffer_copy(alloc, true, buffer, buffer_offset,src, 字節);}靜態 整數 bind_alloc_do_buffer_copy ( struct binder_alloc * alloc,bool to_buffer,struct binder_buffer * 緩沖區,binding_size_t buffer_offset,無效 * ptr,size_t 字節){/ *所有副本必須對齊32位且大小為32位* /BUG_ON( ! check_buffer(分配, 緩沖區, buffer_offset, 字節));// [...]函數binder_alloc_do_buffer_copy()檢查要修補的緩沖區是否在當前綁定程序事務的當前接收緩沖區內。
此漏洞利用不允許針對內核內存?。
可以注意到,如果地址無效,內核將調用BUG_ON?,這將停止內核執行。
通過繞過所有檢查,我們可以將任意值設置為parent->buffer但是我們只能嘗試一次,否則內核將停止!?我們需要內存泄漏才能知道目標進程中/dev/binder的地址。
為了驗證該理論,讓我們來看一下所描述的利用工作。?由于尚未發生泄漏,因此我們在Android仿真器中運行了經過修改的內核。
靜態 void binder_alloc_do_buffer_copy ( struct binder_alloc * alloc,bool to_buffer,struct binder_buffer * 緩沖區,binding_size_t buffer_offset,無效 * ptr,size_t 字節){如果 ( ! check_buffer(alloc, buffer, buffer_offset, bytes)){size_t buffer_size = binder_alloc_buffer_size(alloc, buffer);pr_info( “ [JB] check_buffer buffer_size:0x%lx字節= 0x%lx偏移量= 0x%lx \ n ” , buffer_size, 字節, buffer_offset);}/ *所有副本必須對齊32位且大小為32位* /BUG_ON( ! check_buffer(分配, 緩沖區, buffer_offset, 字節));添加了調試打印(調用pr_info()?,以檢查buffer_offset值是否無效)
POC
自定義內核(基于msm-bonito-4.9-android10)與Pixel 3a XL固件一起啟動。?PoC使用先前圖中描述的父層次結構將綁定程序事務發送到servicemanager器。
。 / 模擬器 -AVD Pixel_3a_XL_API_29_64b- 內核 custom_bzImage- 顯示 - 內核 - 否 - 窗口 - 詳細 - 隨機 - 否 - 快照 [148.291702]活頁夾:3410:3410 ioctl c0306201 7fff98cb5f20返回-22[148.295022] binding_alloc:[JB] check_buffer buffer_size:0x10e0字節= 0x8偏移量= 0x71829fdc8b8[148.299460] ------------ [在此處剪切] ------------[148.301159]內核BUG位于drivers / android / binder_alloc.c:1133![148.303042]無效的操作碼:0000 [#1] PREEMPT SMP NOPTI[148.304537]模塊鏈接在:[148.305422] CPU:0 PID:3410 Comm:poc未受污染4.14.150HELLO +#28[148.307397]硬件名稱:QEMU Standard PC(i440FX + PIIX,1996),BIOS rel-1.11.1-0-g0551a4be2c-prebuilt.qemu-project.org 04/01/2014[148.311690]任務:0000000086b3eedc task.stack:0000000000a1c204[148.313730] RIP:0010:binder_alloc_do_buffer_copy + 0x8d / 0x15e[148.315692] RSP:0018:ffffa11501effa48 EFLAGS:00010246[148.317540] RAX:0000000000000000 RBX:ffff9e98a62079c0 RCX:0000000000000008[148.320403] RDX:ffff9e98aa0e5dd8 RSI:0000000000000000 RDI:ffff9e98aa0e5da0[148.323268] RBP:ffffa11501effaa0 R08:0000000000000ff4 R09:0000000000000000[148.325435] R10:0000000000000000 R11:0000000000000000 R12:0000000000000008[148.328290] R13:0000071829fdc8b8 R14:ffff9e98aa0e5da0 R15:ffff9e98a62079c0[148.330194] FS:000000000048d648(0000)GS:ffff9e98bfc00000(0000)knlGS:0000000000000000[148.331780] CS:0010 DS:0000 ES:0000 CR0:0000000080050033[148.332740] CR2:00007435311239a0 CR3:0000000010ee2000 CR4:00000000000006b0[148.333848]通話跟蹤:[148.334207] binding_alloc_copy_to_buffer + 0x1a / 0x1c[148.334895] binding_fixup_parent + 0x186 / 0x1ac調試字符串證明PoC起作用,因為偏移量值無效(0x71829fdc8b8的偏移量很大!)
活頁夾分配器: [JB] check_buffer buffer_size : 0x10e0 字節 = 0x8 偏移量 = 0x71829fdc8b8/ dev / binder的內存映射泄漏
在不知道目標進程的內存映射的情況下,此PoC幾乎是沒有用的:(。但是,沒有丟失任何信息!
Android Java應用程序具有特定性,它們都是Zygote或Zygote64?(取決于32/64位)。
Zygote是帶有預初始化的Java虛擬機的進程。?當系統需要啟動新的Java應用程序時,?Zygote被派生并開始執行該應用程序。?這種設計可以減少初始化步驟。?Java應用程序可以盡快啟動。?但是,在執行對fork()的調用時,虛擬內存將被克隆,因此其內存映射也將被克隆。?因此,Zygote的所有孩子都共享相同的映射。
讓我們檢查一下模擬器:
根1612 1 4758476 190144 poll_schedule_timeout 0 S zygote64...u0_a103 3891 1612 4927284 124964 SyS_epoll_wait 0 S com.foo.mypoc cat / proc / $(pidof com.foo.mypoc)/ maps | grep“ / dev / binder”7a6242192000-7a6242290000 r--p 00000000 00:12 7315 / dev假設我們可以將任意代碼作為com.foo.mypoc包執行,通過檢查進程內存映射,可以找到/dev/binder的映射位置。?在我們的情況下,它映射為0x7a6242192000?。
進程com.foo.mypoc是從zygote64分叉的。?其他與同一個母親一起的過程如下:
generic_x86_64:/#ps -e | grep $(pidof zygote64) 根1612 1 ... zygote64 系統1845 1612 ... system_serveru0_a89 1996 1612 ... com.android.systemuinetwork_stack 2118 1612 ... com.android.networkstackradio 2199 1612 ... com.android.phone 系統2210 1612 ... com.android.settingsu0_a55 2261 1612 ... android.ext.servicesu0_a84 2296 1612 ... com.android.launcher3u0_a102 2321 1612 ... com.android.inputmethod.latinu0_a87 2436 1612 ... com.android.dialeru0_a37 2465 1612 ... android.process.acoresecure_element 2553 1612 ... com.android.se 無線電2586 1612 ... com.android.ims.rcsservice 系統2626 1612 ... com.android.emulator.multidisplayu0_a77 2686 1612 ... com.android.smspushu0_a67 2705 1612 ... com.android.printspooleru0_a40 2787 1612 ... android.process.mediau0_a97 2884 1612 ... com.android.emailu0_a78 2947 1612 ... com.android.messagingu0_a81 2971 1612 ... com.android.onetimeinitializeru0_a52 3005 1612 ... com.android.packageinstalleru0_a54 3027 1612 ... com.android.permissioncontrolleru0_a39 3050 1612 ... com.android.providers.calendaru0_a62 3075 1612 ... com.android.traceuru0_a41 3097 1612 ... com.android.externalstorage 系統3134 1612 ... com.android.localtransport 系統3230 1612 ... com.android.keychainu0_a103 3891 1612 ... com.foo.mypoc軟件包com.android.settings看起來很有趣,因為它作為system運行。
generic_x86_64:/#cat / proc / $(pidof com.android.settings)/ maps | grep“ / dev / binder”7a6242192000-7a6242290000 r--p 00000000 00:12 7315實際上,綁定器設備與我們的應用程序com.foo.mypoc映射在同一位置!
開發思路
使用先前的PoC和目標進程的內存映射,可以覆蓋與已驗證的活頁夾對象有關的數據。
Userland綁定程序庫(?libbinder.so和libhwbinder.so?)信任綁定程序驅動程序處理,并認為所有綁定程序對象均已正確打補丁。?如果修補程序未正確完成,則在宗地反序列化步驟中,應用程序可能很容易受到攻擊。
文件描述符
覆蓋BINDER_TYPE_FDA修補的對象,使其指向BINDER_TYPE_FDA且未經檢查的文件描述符列表。?我們可以想象在目標進程中關閉任意文件描述符,以將其替換為受控文件描述符。
活頁夾緩沖區
覆蓋BINDER_TYPE_PTR對象的大小。?如果使用該漏洞更改了嵌入式緩沖區結構(如hidl_string?)的大小字段,則新值將是一個指針,并且對于正確的緩沖區無效。
詳細信息 :: hidl_pointer < const char > mBuffer;uint32_t mSize; //嘗試覆蓋大小bool mOwnsBuffer;活頁夾/句柄對象
覆蓋BINDER_TYPE_HANDLE?/?BINDER_TYPE_WEAK_HANDLE對象的指針。?當綁定程序內核模塊處理這些對象時,它將在遠程進程中用其原始指針值替換處理程序。?內核在其內存中保留處理程序/指針之間的映射,并使用此表修復BINDER_TYPE_HANDLE或BINDER_TYPE_BINDER?。?有時,遠程服務需要實例化對象以執行命令。?要使用此對象,客戶端將發送一個對象句柄(?BINDER_TYPE_HANDLE?)。?內核將其替換為BINDER_TYPE_BINDER?,該對象在目標接收緩沖區中包含實際指針。
如果攻擊者使用該漏洞替換了BINDER_TYPE_BINDER對象指針,則他可以控制該對象類型的所有字段以獲取代碼執行BINDER_TYPE_BINDER?。
?
正常交易
對象指向受控數據
結論
對這個錯誤及其利用方法的分析非常有趣且新穎。?它允許與活頁夾父母一起玩。
即使此漏洞不允許定向內核內存,也可能導致利用特權更高的應用程序。
本文完成的分析只是利用此漏洞所需的第一步。?將這些原語轉換為實際的權限提升漏洞需要大量的工作。
我認為,在將代碼添加到Linux內核源代碼之前,請仔細檢查一下該錯誤。?我的陳述與我先前對secctx patch進行的補丁分析相同,最近在內核源代碼中插入了幾個“簡單”的錯誤。
幸運的是,此最新漏洞僅影響了少數設備(Android 10上的Pixel 4和3a)。
總結
以上是生活随笔為你收集整理的【译】BINDER - ANALYSIS AND EXPLOITATION OF CVE-2020-0041的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《深入理解java虚拟机》第2章 Jav
- 下一篇: 链表队列初始化