首页
社区
课程
招聘
Qcom QMI fuzzing分享
2021-10-25 15:43 22876

Qcom QMI fuzzing分享

2021-10-25 15:43
22876

在研究Qcom服务,发现一个潜在的fuzzing攻击面,分享出来供大家交流.

1. 什么是QMI?

Qualcomm MSM Interface(QMI) is a messaging format used to communicate between software components in the modem and other peripheral subsystems.

 

QMI communication is based on a client-server model, where clients and servers exchange messages in QMI wire format.

 

0 - QMI Request

 

2 - QMI Response

 

4 - QMI Indication

 

更多信息可以参考以下文档

 

通过下面command来查看所有使用QMI接口的service

 

adb shell qmi-lookup

 

图片描述

 

这些services分别分布在3个环境中

  1. User-space closed-source services
  2. Kernel
  3. Modem

2. QMI接口的攻击方法

通过以下demo我们可以发送任意数据到这些对应的services.

1
2
3
4
5
6
7
8
9
socketFd = socket(AF_QIPCRTR, 524290, 0);
 
// Get socket address
uint8_t remote_addr[16] = {service_id,  instance_id,  node_id,  port_id};
uint8_t *dest_addr = remote_addr;
addr->sa_family = AF_QIPCRTR;
memcpy((uint8_t *)&(addr->sa_data[2]), dest_addr+8, 8);
 
ssize_t ret = sendto(socketFd, buf, len, 64, addr, sizeof(struct sockaddr));

注意此socket任然被selinux限制, normal app无法访问

3. QMI Fuzzing的方法

  • closed-sourced services
    我们先可以看下有哪些闭源的services, 这些模块可以通过AFL+frida的方式去尝试

radio 965 1 0 03:15:47 ? 00:00:34 imsqmidaemon
system 1020 1 0 03:15:47 ? 00:00:00 cnss-daemon -n -l
gps 1103 1040 0 03:15:47 ? 00:00:06 xtra-daemon
radio 1132 1 1 03:15:47 ? 00:42:36 imsdatadaemon
radio 1486 1 0 03:15:52 ? 00:21:12 ims_rtp_daemon
system 3182 1 0 03:42:01 ? 00:00:00 time_daemon

  • Kernel services
    kernel中可以看到qmi协议的具体实现在以下源码中
    drivers/soc/qcom/qmi_encdec.c
    drivers/soc/qcom/qmi_interface.c
    Decode函数的实现
1
2
3
4
5
6
7
8
9
10
11
12
int qmi_decode_message(const void *buf, size_t len,
               struct qmi_elem_info *ei, void *c_struct)
{
    if (!ei)
        return -EINVAL;
 
    if (!c_struct || !buf || !len)
        return -EINVAL;
 
    return qmi_decode(ei, c_struct, buf + sizeof(struct qmi_header),
              len - sizeof(struct qmi_header), 1);
}

以下是kernel中用到的QMI接口的服务
drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
sound/usb/usb_audio_qmi_svc.c drivers/char/diag/diagfwd_socket.c

 

我将以发现的oob来说明, 此issue位于Diag service

 

Root cause分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
                 int len)
{
    uint8_t *ptr = buf;
    struct diag_ctrl_pkt_header_t *ctrl_pkt = NULL;
 
    if (!buf || len <= 0 || !p_info)
        return;
 
    while (read_len + header_len < len) {
        ctrl_pkt = (struct diag_ctrl_pkt_header_t *)ptr;
        switch (ctrl_pkt->pkt_id) {
        case DIAG_CTRL_MSG_SSID_RANGE_REPORT:
            process_ssid_range_report(ptr, ctrl_pkt->len,
....
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void process_ssid_range_report(uint8_t *buf, uint32_t len,
                      uint8_t peripheral)
{
    header = (struct diag_ctrl_ssid_range_report *)ptr;
    ptr += header_len;
    /* Don't account for pkt_id and length */
    read_len += header_len - (2 * sizeof(uint32_t));
 
    driver->max_ssid_count[peripheral] = header->count;
    for (i = 0; i < header->count && read_len < len; i++) {
        ssid_range = (struct diag_ssid_range_t *)ptr;
        ptr += sizeof(struct diag_ssid_range_t);
        read_len += sizeof(struct diag_ssid_range_t);
        mask_ptr = (struct diag_msg_mask_t *)msg_mask.ptr;
        found = 0;
        ....
    }
}

函数diag_cntl_process_read_data()的buffer来自于用户可控的输入,这个buffer头部是diag_ctrl_pkt_header_t类型, 如果我们分配ctrl_pkt->pkt_id = 0x18 and ctrl_pkt->len = UINT_MAX;
当进入到函数process_ssid_range_report():

1
2
3
4
5
6
7
8
header = (struct diag_ctrl_ssid_range_report *)ptr;
ptr += header_len;
for (i = 0; i < header->count && read_len < len; i++) {
    ssid_range = (struct diag_ssid_range_t *)ptr;
    ptr += sizeof(struct diag_ssid_range_t);
    read_len += sizeof(struct diag_ssid_range_t);
    
}

ptr指向struct diag_ctrl_ssid_range_report, header->count被赋值为UINT_MAX. ptr申请的大小只有16384 bytes. 当读取ptr content 循环UINT_MAX次, 就会导致OOB read issue.

kasan report

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
<3>[  480.576071] c1    373 ==================================================================
<3>[  480.576132] c1    373 BUG: KASAN: slab-out-of-bounds in diag_cntl_process_read_data+0x934/0x1cf8
<3>[  480.576164] c1    373 Read of size 2 at addr ffffffc1410bc1a0 by task kworker/u16:12/373
<3>[  480.576186] c1    373
<4>[  480.576225] c1    373 CPU: 1 PID: 373 Comm: kworker/u16:12 Tainted: G S              4.14.150-g05fe60fb845b-ab6126650 #1
<4>[  480.576252] c1    373 Hardware name: Qualcomm Technologies, Inc. SM8150 V2 PM8150 Google Inc. MSM sm8150 Flame (DT)
<4>[  480.576293] c1    373 Workqueue: MODEM_CNTL socket_read_work_fn
<4>[  480.576321] c1    373 Call trace:
<4>[  480.576360] c1    373  dump_backtrace+0x0/0x36c
<4>[  480.576391] c1    373  show_stack+0x20/0x2c
<4>[  480.576428] c1    373  dump_stack+0xe0/0x124
<4>[  480.576463] c1    373  print_address_description+0x80/0x2d8
<4>[  480.576492] c1    373  kasan_report_error+0x198/0x1fc
<4>[  480.576522] c1    373  kasan_report_error+0x0/0x1fc
<4>[  480.576562] c1    373  check_memory_region+0x1b4/0x1bc
<4>[  480.576590] c1    373  __asan_loadN+0x14/0x1c
<4>[  480.576623] c1    373  diag_cntl_process_read_data+0x934/0x1cf8
<4>[  480.576654] c1    373  diagfwd_cntl_read_done+0x5c/0x588
<4>[  480.576685] c1    373  diagfwd_channel_read_done+0x60/0x384
<4>[  480.576716] c1    373  diag_socket_read+0xe28/0x1054
<4>[  480.576747] c1    373  diagfwd_channel_read+0x390/0x46c
<4>[  480.576778] c1    373  socket_read_work_fn+0x3ec/0x490
<4>[  480.576814] c1    373  process_one_work+0x528/0x8f8
<4>[  480.576845] c1    373  worker_thread+0x660/0x8e4
<4>[  480.576875] c1    373  kthread+0x1a4/0x1bc
<4>[  480.576905] c1    373  ret_from_fork+0x10/0x18
<3>[  480.576926] c1    373
<3>[  480.576950] c1    373 Allocated by task 1:
<4>[  480.576985] c1    373  kasan_kmalloc+0xe0/0x1ac
<4>[  480.577018] c1    373  kmem_cache_alloc_trace+0x260/0x2c0
<4>[  480.577055] c1    373  __class_register+0x50/0x224
<4>[  480.577086] c1    373  __class_create+0x8c/0xc8
<4>[  480.577126] c1    373  diagchar_init+0xda8/0xefc
<4>[  480.577157] c1    373  do_one_initcall+0x274/0x3b8
<4>[  480.577197] c1    373  kernel_init_freeable+0x27c/0x338
<4>[  480.577230] c1    373  kernel_init+0x14/0x294
<4>[  480.577260] c1    373  ret_from_fork+0x10/0x18

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞3
打赏
分享
最新回复 (7)
雪    币: 6571
活跃值: (3823)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
LowRebSwrd 4 2021-10-26 09:10
2
0
雪    币: 107
活跃值: (1412)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
frozenrain 2021-10-26 17:19
3
0
受selinux的限制,楼主发现的crash,官方给cve了么?
雪    币: 18
活跃值: (561)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
ele7enxxh 1 2021-10-27 15:37
4
0
qmi-lookup是哪里来的?
雪    币: 183
活跃值: (273)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DKFATE 2021-10-30 09:20
5
0
ele7enxxh qmi-lookup是哪里来的?
Pixel 自带
雪    币: 18
活跃值: (561)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
ele7enxxh 1 2021-10-30 15:01
6
0
DKFATE Pixel 自带
我的pixel3xl里没有
雪    币: 18
活跃值: (561)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
ele7enxxh 1 2021-10-30 15:04
7
0
你是哪个pixel型号自带的?
雪    币: 183
活跃值: (273)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DKFATE 2021-11-4 09:47
8
0
ele7enxxh 你是哪个pixel型号自带的?
Pixel 4xl有的
游客
登录 | 注册 方可回帖
返回