【NFC】CVE-2018-9584 nfc_ncif_set_config_status未检测长度越界读写
漏洞原理总结:NFC漏洞,在函数nfc_ncif_set_config_status()
里直接将缓冲区数据作为长度进行拷贝操作,造成uint8_t
类型溢出定义长度125
位的数组
官方描述:In nfc_ncif_set_config_status of nfc_ncif.cc, there is a possible out of bounds write due to a missing bounds check. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.
官方描述翻译:函数nfc_ncif.cc - nfc_ncif_set_config_status()
里,因为缺少边界检查,存在一个越界写,这可以造成提权并且无需额外的执行权限,利用这个漏洞也不需要用户交互
CVE-2018-9584
A-114047681
EoP
高
7.0、7.1.1、7.1.2、8.0、8.1、9
补丁
https://android.googlesource.com/platform/system/nfc/+/5f0f0cc6a10f710dea7e1ddd4ba19acb877a7081
看起来是很常规的未检测Buffer长度就进行读写数据的漏洞
Prevent Out of bounds read/write in nfc_ncif_set_config_status
Test: Nfc Enable/Disable; Android Beam; Tag reading
Bug: 114047681
Merged-In: Iaba48380879373a4807a9d50634f4f40be97ef81
Change-Id: Iaba48380879373a4807a9d50634f4f40be97ef81
(cherry picked from commit 74cf5266c1bb9ee064cbc7e2544909d5d001e429)
(cherry picked from commit f3b6b4e1502771d94418cd2bc02591eb4379db34)
diff --git a/src/nfc/nfc/nfc_ncif.cc b/src/nfc/nfc/nfc_ncif.cc
index 4fb767f..93666e0 100644
--- a/src/nfc/nfc/nfc_ncif.cc
+++ b/src/nfc/nfc/nfc_ncif.cc
@@ -479,18 +479,33 @@
** Returns void
**
*******************************************************************************/
-void nfc_ncif_set_config_status(uint8_t* p,
- __attribute__((unused)) uint8_t len) {
+void nfc_ncif_set_config_status(uint8_t* p, uint8_t len) {
tNFC_RESPONSE evt_data;
if (nfc_cb.p_resp_cback) {
- evt_data.set_config.status = (tNFC_STATUS)*p++;
- evt_data.set_config.num_param_id = NFC_STATUS_OK;
- if (evt_data.set_config.status != NFC_STATUS_OK) {
- evt_data.set_config.num_param_id = *p++;
- STREAM_TO_ARRAY(evt_data.set_config.param_ids, p,
- evt_data.set_config.num_param_id);
+ evt_data.set_config.num_param_id = 0;
+ if (len == 0) {
+ LOG(ERROR) << StringPrintf("Insufficient RSP length");
+ evt_data.set_config.status = NFC_STATUS_SYNTAX_ERROR;
+ (*nfc_cb.p_resp_cback)(NFC_SET_CONFIG_REVT, &evt_data);
+ return;
}
-
+ evt_data.set_config.status = (tNFC_STATUS)*p++;
+ if (evt_data.set_config.status != NFC_STATUS_OK && len > 1) {
+ evt_data.set_config.num_param_id = *p++;
+ if (evt_data.set_config.num_param_id > NFC_MAX_NUM_IDS) {
+ android_errorWriteLog(0x534e4554, "114047681");
+ LOG(ERROR) << StringPrintf("OOB write num_param_id %d",
+ evt_data.set_config.num_param_id);
+ evt_data.set_config.num_param_id = 0;
+ } else if (evt_data.set_config.num_param_id <= len - 2) {
+ STREAM_TO_ARRAY(evt_data.set_config.param_ids, p,
+ evt_data.set_config.num_param_id);
+ } else {
+ LOG(ERROR) << StringPrintf("Insufficient RSP length %d,num_param_id %d",
+ len, evt_data.set_config.num_param_id);
+ evt_data.set_config.num_param_id = 0;
+ }
+ }
(*nfc_cb.p_resp_cback)(NFC_SET_CONFIG_REVT, &evt_data);
}
}
从最开始走起,进入p_msg->event
为BT_EVT_TO_NFC_NCI
的分支,调用nfc_ncif_process_event()
/*******************************************************************************
**
** Function nfc_task
**
** Description NFC event processing task
**
** Returns nothing
**
*******************************************************************************/
uint32_t nfc_task(__attribute__((unused)) uint32_t arg) {
uint16_t event;
NFC_HDR* p_msg;
bool free_buf;
/* Initialize the nfc control block */
memset(&nfc_cb, 0, sizeof(tNFC_CB));
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("NFC_TASK started.");
/* main loop */
while (true) {
event = GKI_wait(0xFFFF, 0);
if (event == EVENT_MASK(GKI_SHUTDOWN_EVT)) {
break;
}
/* Handle NFC_TASK_EVT_TRANSPORT_READY from NFC HAL */
if (event & NFC_TASK_EVT_TRANSPORT_READY) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("NFC_TASK got NFC_TASK_EVT_TRANSPORT_READY.");
/* Reset the NFC controller. */
nfc_set_state(NFC_STATE_CORE_INIT);
nci_snd_core_reset(NCI_RESET_TYPE_RESET_CFG);
}
if (event & NFC_MBOX_EVT_MASK) {
/* Process all incoming NCI messages */
while ((p_msg = (NFC_HDR*)GKI_read_mbox(NFC_MBOX_ID)) != NULL) {
free_buf = true;
/* Determine the input message type. */
switch (p_msg->event & NFC_EVT_MASK) {
case BT_EVT_TO_NFC_NCI:
free_buf = nfc_ncif_process_event(p_msg); // <--
break;
...
}
if (free_buf) {
GKI_freebuf(p_msg);
}
}
}
...
}
通过对Buffer取第一字节并判断,进入NCI_GID_CORE
分支,调用nci_proc_core_rsp()
#define NCI_MT_MASK 0xE0
#define NCI_MT_SHIFT 5
#define NCI_PBF_MASK 0x10
#define NCI_PBF_SHIFT 4
#define NCI_GID_MASK 0x0F
/* parse byte0 of NCI packet */
#define NCI_MSG_PRS_HDR0(p, mt, pbf, gid) \
mt = (*(p)&NCI_MT_MASK) >> NCI_MT_SHIFT; \
(pbf) = (*(p)&NCI_PBF_MASK) >> NCI_PBF_SHIFT; \
(gid) = *(p)++ & NCI_GID_MASK;
/*******************************************************************************
**
** Function nfc_ncif_process_event
**
** Description This function is called to process the
** data/response/notification from NFCC
**
** Returns TRUE if need to free buffer
**
*******************************************************************************/
bool nfc_ncif_process_event(NFC_HDR* p_msg) {
uint8_t mt, pbf, gid, *p, *pp;
bool free = true;
uint8_t oid;
uint8_t *p_old, old_gid, old_oid, old_mt;
p = (uint8_t*)(p_msg + 1) + p_msg->offset; // 先获取Buffer指针
pp = p;
// 对Buffer取第一个字节并进行解析赋值
NCI_MSG_PRS_HDR0(pp, mt, pbf, gid);
oid = ((*pp) & NCI_OID_MASK);
if (nfc_cb.rawVsCbflag == true &&
nfc_ncif_proc_proprietary_rsp(mt, gid, oid) == true) {
nci_proc_prop_raw_vs_rsp(p_msg);
nfc_cb.rawVsCbflag = false;
return free;
}
nfcsnoop_capture(p_msg, true);
switch (mt) {
...
case NCI_MT_RSP:
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("NFC received rsp gid:%d", gid);
oid = ((*pp) & NCI_OID_MASK);
p_old = nfc_cb.last_hdr;
NCI_MSG_PRS_HDR0(p_old, old_mt, pbf, old_gid);
old_oid = ((*p_old) & NCI_OID_MASK);
/* make sure this is the RSP we are waiting for before updating the
* command window */
if ((old_gid != gid) || (old_oid != oid)) {
LOG(ERROR) << StringPrintf(
"nfc_ncif_process_event unexpected rsp: gid:0x%x, oid:0x%x", gid,
oid);
return true;
}
switch (gid) {
case NCI_GID_CORE: /* 0000b NCI Core group */
free = nci_proc_core_rsp(p_msg); // <--
break;
...
}
nfc_ncif_update_window();
break;
...
}
return (free);
}
使用NCI_MSG_PRS_HDR1
获取p[1]
位置的数据,赋值为op_code
,之后获取一个字节p[2]
作为len
,当op_code
为NCI_MSG_CORE_SET_CONFIG
时,调用nfc_ncif_set_config_status()
,并且传入Buffer指针,此时Buffer指针指向偏移为3
的位置
/* parse byte1 of NCI Cmd/Ntf */
#define NCI_MSG_PRS_HDR1(p, oid) \
(oid) = (*(p)&NCI_OID_MASK); \
(p)++;
/*******************************************************************************
**
** Function nci_proc_core_rsp
**
** Description Process NCI responses in the CORE group
**
** Returns TRUE-caller of this function to free the GKI buffer p_msg
**
*******************************************************************************/
bool nci_proc_core_rsp(NFC_HDR* p_msg) {
uint8_t* p;
uint8_t *pp, len, op_code;
bool free = true;
uint8_t* p_old = nfc_cb.last_cmd;
/* find the start of the NCI message and parse the NCI header */
p = (uint8_t*)(p_msg + 1) + p_msg->offset;
pp = p + 1;
NCI_MSG_PRS_HDR1(pp, op_code); // op_code = p[1]
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("nci_proc_core_rsp opcode:0x%x", op_code);
len = *pp++;
/* process the message based on the opcode and message type */
switch (op_code) {
...
case NCI_MSG_CORE_SET_CONFIG:
nfc_ncif_set_config_status(pp, len); // <--
break;
...
}
return free;
}
终于来到了补丁函数,补丁描述是越界读取
Prevent Out of bounds read/write in nfc_ncif_set_config_status
这里可以看到将*p++
赋值给evt_data.set_config.status
,nfc_cb.p_resp_cback
对应的是nfa_dm_nfc_response_cback()
,此时可控的变量一共有三个
evt_data.set_config.status
evt_data.set_config.num_param_id
evt_data.set_config.param_ids
#define STREAM_TO_ARRAY(a, p, len) \
{ \
int ijk; \
for (ijk = 0; ijk < (len); ijk++) ((uint8_t*)(a))[ijk] = *(p)++; \
}
/*******************************************************************************
**
** Function nfc_ncif_set_config_status
**
** Description This function is called to report NFC_SET_CONFIG_REVT
**
** Returns void
**
*******************************************************************************/
void nfc_ncif_set_config_status(uint8_t* p,
__attribute__((unused)) uint8_t len) {
tNFC_RESPONSE evt_data;
if (nfc_cb.p_resp_cback) {
evt_data.set_config.status = (tNFC_STATUS)*p++; // p[3]
// #define NFC_STATUS_OK NCI_STATUS_OK
// #define NCI_STATUS_OK 0x00
evt_data.set_config.num_param_id = NFC_STATUS_OK; // 初始化为0
if (evt_data.set_config.status != NFC_STATUS_OK) {
evt_data.set_config.num_param_id = *p++; // p[4]
STREAM_TO_ARRAY(evt_data.set_config.param_ids, p,
evt_data.set_config.num_param_id); // 长度字段为`p[4]`,这里长度可控,拷贝源可控
}
(*nfc_cb.p_resp_cback)(NFC_SET_CONFIG_REVT, &evt_data);
}
}
我们来看evt_data.set_config.param_ids
的定义,其中NFC_MAX_NUM_IDS
为125
,我们知道,uint8_t
为无符号类型,大小为255
,所以这里足够溢出param_ids
数组
#define NFC_MAX_NUM_IDS 125
/* Data for NFA_DM_SET_CONFIG_EVT */
typedef struct {
tNFA_STATUS status; /* NFA_STATUS_OK if successful */
uint8_t num_param_id; /* Number of rejected Param ID */
tNFA_PMID param_ids[NFC_MAX_NUM_IDS]; /* Rejected Param ID */
} tNFA_SET_CONFIG;
再来回顾补丁
void nfc_ncif_set_config_status(uint8_t* p, uint8_t len) {
tNFC_RESPONSE evt_data;
if (nfc_cb.p_resp_cback) {
evt_data.set_config.num_param_id = 0;
if (len == 0) { // 先判断len不能为0,如果为0表示后面没有数据,不能再取值
LOG(ERROR) << StringPrintf("Insufficient RSP length");
evt_data.set_config.status = NFC_STATUS_SYNTAX_ERROR;
(*nfc_cb.p_resp_cback)(NFC_SET_CONFIG_REVT, &evt_data);
return;
}
evt_data.set_config.status = (tNFC_STATUS)*p++;
if (evt_data.set_config.status != NFC_STATUS_OK && len > 1) {
evt_data.set_config.num_param_id = *p++;
// 保证num_param_id的大小不能大于NFC_MAX_NUM_IDS
if (evt_data.set_config.num_param_id > NFC_MAX_NUM_IDS) {
android_errorWriteLog(0x534e4554, "114047681");
LOG(ERROR) << StringPrintf("OOB write num_param_id %d",
evt_data.set_config.num_param_id);
evt_data.set_config.num_param_id = 0;
} else if (evt_data.set_config.num_param_id <= len - 2) {
// 多处判断通过之后再进行拷贝
STREAM_TO_ARRAY(evt_data.set_config.param_ids, p,
evt_data.set_config.num_param_id);
} else {
LOG(ERROR) << StringPrintf("Insufficient RSP length %d,num_param_id %d",
len, evt_data.set_config.num_param_id);
evt_data.set_config.num_param_id = 0;
}
}
(*nfc_cb.p_resp_cback)(NFC_SET_CONFIG_REVT, &evt_data);
}
}
Last updated