漏洞原理总结:在函数nfc_ncif_proc_get_routing()
里,直接获取数据作为长度进行数组拷贝,读取的类型是uint8_t
,最大是255
,拷贝的数组定义长度125
,直接溢出
官方描述:In nfc_ncif_proc_get_routing 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_proc_get_routing()
里,因为缺少边界检查,存在一个越界写,这可以造成提权并且无需额外的执行权限,利用这个漏洞也不需要用户交互
CVE
参考编号
类型
严重程度
已更新的 AOSP 版本
7.0、7.1.1、7.1.2、8.0、8.1、9
补丁
https://android.googlesource.com/platform/system/nfc/+/71764b791f262491e3f628c14ce3949863dd6058
Copy Prevent OOB error in nfc_ncif_proc_get_routing()
Test: Tag reading; Card Emulation
Bug: 117554809
Change-Id: Ib49af2eadf870f030a6cddeec390dc498bd5078c
(cherry picked from commit ded496ea745656018dda505c23726b4304180c38)
diff --git a/src/nfc/nfc/nfc_ncif.cc b/src/nfc/nfc/nfc_ncif.cc
index 93666e0..6d6607d 100644
--- a/src/nfc/nfc/nfc_ncif.cc
+++ b/src/nfc/nfc/nfc_ncif.cc
@@ -25,6 +25,7 @@
******************************************************************************/
#include <android-base/stringprintf.h>
#include <base/logging.h>
+#include <log/log.h>
#include <metricslogger/metrics_logger.h>
#include "nfc_target.h"
@@ -1235,8 +1236,13 @@
for (yy = 0; yy < evt_data.num_tlvs; yy++) {
tl = *(p + 1);
tl += NFC_TL_SIZE;
- STREAM_TO_ARRAY(pn, p, tl);
evt_data.tlv_size += tl;
+ if (evt_data.tlv_size > NFC_MAX_EE_TLV_SIZE) {
+ android_errorWriteLog(0x534e4554, "117554809");
+ LOG(ERROR) << __func__ << "Invalid data format";
+ return;
+ }
+ STREAM_TO_ARRAY(pn, p, tl);
pn += tl;
}
tNFC_RESPONSE nfc_response;
入口简单的取值判断分支,最后调用到nci_proc_rf_management_ntf()
Copy #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
#define NCI_OID_MASK 0x3F
/* 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;
pp = p;
NCI_MSG_PRS_HDR0(pp, mt, pbf, gid);
oid = ((*pp) & NCI_OID_MASK); // oid = p[1] & 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_NTF:
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("NFC received ntf gid:%d", gid);
switch (gid) {
...
case NCI_GID_RF_MANAGE: /* 0001b NCI Discovery group */
nci_proc_rf_management_ntf(p_msg);
break;
...
}
break;
}
return (free);
}
获取op_code
,当op_code
为NCI_MSG_RF_GET_ROUTING
时,调用nfc_ncif_proc_get_routing()
,传入的缓冲区指针指向p[3]
,len
字段为p[2]
,这俩参数未做判断
Copy #define NCI_OID_MASK 0x3F
/* parse byte1 of NCI Cmd/Ntf */
#define NCI_MSG_PRS_HDR1(p, oid) \
(oid) = (*(p)&NCI_OID_MASK); \
(p)++;
/*******************************************************************************
**
** Function nci_proc_rf_management_ntf
**
** Description Process NCI notifications in the RF Management group
**
** Returns void
**
*******************************************************************************/
void nci_proc_rf_management_ntf(NFC_HDR* p_msg) {
uint8_t* p;
uint8_t *pp, len, op_code;
/* find the start of the NCI message and parse the NCI header */
p = (uint8_t*)(p_msg + 1) + p_msg->offset; // 取用户传入的Buffer
pp = p + 1; // pp = p[1]
NCI_MSG_PRS_HDR1(pp, op_code); // op_code = p[1] & NCI_OID_MASK
len = *pp++; // len = p[2],获取完后pp指向p[3]
switch (op_code) {
...
#if (NFC_NFCEE_INCLUDED == TRUE)
#if (NFC_RW_ONLY == FALSE)
case NCI_MSG_RF_GET_ROUTING:
nfc_ncif_proc_get_routing(pp, len); // (p[3], p[2])
break;
...
}
}
获取的tl
为p[8]
,然后将tl
加2,tl
的类型是uint8_t
,最大是255
,加上2之后就是257
,evt_data.param_tlvs
定义的长度为125
,所以拷贝数组的时候直接溢出
Copy #define NFC_MAX_EE_TLV_SIZE 150
typedef struct {
...
uint8_t param_tlvs[NFC_MAX_EE_TLV_SIZE]; /* the TLVs */
} tNFC_GET_ROUTING_REVT;
/*******************************************************************************
**
** Function nfc_ncif_proc_get_routing
**
** Description This function is called to process get routing notification
**
** Returns void
**
*******************************************************************************/
void nfc_ncif_proc_get_routing(uint8_t* p,
__attribute__((unused)) uint8_t len) {
tNFC_GET_ROUTING_REVT evt_data;
uint8_t more, num_entries, xx, yy, *pn, tl;
tNFC_STATUS status = NFC_STATUS_CONTINUE;
if (nfc_cb.p_resp_cback) {
more = *p++; // more = p[3]
num_entries = *p++; // num_entries = p[4]
for (xx = 0; xx < num_entries; xx++) { // 这里可能存在多个entry
if ((more == false) && (xx == (num_entries - 1))) status = NFC_STATUS_OK;
evt_data.status = (tNFC_STATUS)status;
evt_data.nfcee_id = *p++; // evt_data.nfcee_id = p[5]
evt_data.num_tlvs = *p++; // evt_data.num_tlvs = p[6]
evt_data.tlv_size = 0;
pn = evt_data.param_tlvs; // 定义的数组长度为150
for (yy = 0; yy < evt_data.num_tlvs; yy++) {
tl = *(p + 1); // tl = p[8]
tl += NFC_TL_SIZE; // #define NFC_TL_SIZE 2
STREAM_TO_ARRAY(pn, p, tl); // tl的值最大为(255 + 2),但是数组长度定义为150,存在越界写
evt_data.tlv_size += tl;
pn += tl;
}
tNFC_RESPONSE nfc_response;
nfc_response.get_routing = evt_data;
(*nfc_cb.p_resp_cback)(NFC_GET_ROUTING_REVT, &nfc_response);
}
}
}
补丁所做的就是在拷贝前做好evt_data.tlv_size
的判断,这个字段表示当前已经拷贝的长度
Copy for (yy = 0; yy < evt_data.num_tlvs; yy++) {
tl = *(p + 1);
tl += NFC_TL_SIZE;
- STREAM_TO_ARRAY(pn, p, tl);
evt_data.tlv_size += tl;
+ if (evt_data.tlv_size > NFC_MAX_EE_TLV_SIZE) {
+ android_errorWriteLog(0x534e4554, "117554809");
+ LOG(ERROR) << __func__ << "Invalid data format";
+ return;
+ }
+ STREAM_TO_ARRAY(pn, p, tl);
pn += tl;
}
tNFC_RESPONSE nfc_response;