在研究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;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!