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

Qcom QMI fuzzing分享

2021-10-25 15:43
23961

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

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个环境中

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

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

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中用到的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分析

函数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():

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

 
 
 
 
 
 
 
 
 
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));
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));
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);
}
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);
}
 
 
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,
....
}
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,
....
}
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;
        ....
    }
}
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;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

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