MOSEC2022 MediAttack - break the boot chain of MediaTek SoC

目录

背景

议题学习

MTK Based Boot flow

Preloader部分

BROM部分

基本分析

议题中的漏洞

vuln1

[TBD]vuln2

攻击思路

基本概念

Attacking DA

DA stage1

DA stage2

policy_part_map?

BROM EXPLOIT

common exp

MTE mode

more exploit

后记

参考


背景

前段时间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了

MT6737/linux at master · SoCXin/MT6737 · GitHub

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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,不过不影响理解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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(&region, &(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进去也可以(参考这里 bypass_utility/main.py at master · MTK-bypass/bypass_utility · GitHub )。

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

1
2
3
4
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相关
  • 根据GitHub - chaosmaster/bypass_payloads 中,我目前这个方案的一些寄存器、函数地址来确定的,比如可以确定
1
2
3
4
5
6
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

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

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,然后根据结果去对镜像做对应的操作,比如 是否应该做校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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是 0xd8的 CMD_GET_TARGET_CONFIG为入口就可以找到需要的东西了

common exp

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

  • sla_passed
  • skip_auth_1
  • skip_auth_2

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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还没找到,后面等不忙了时间多了再尝试看看好了 :)

参考

MT6737/linux at master · SoCXin/MT6737 · GitHub

GitHub - chaosmaster/bypass_payloads

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

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

[MT6765]Preloader_流程分析--基于android 10_preloader lk-CSDN博客

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

Resources - Oxygen Forensics

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值