【NFC】CVE-2018-9585_nfc_ncif_proc_get_routing未检测长度越界读写

漏洞原理总结:在函数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 版本

CVE-2018-9585

A-117554809

EoP

7.0、7.1.1、7.1.2、8.0、8.1、9

补丁

  • https://android.googlesource.com/platform/system/nfc/+/71764b791f262491e3f628c14ce3949863dd6058

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()

#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_codeNCI_MSG_RF_GET_ROUTING时,调用nfc_ncif_proc_get_routing(),传入的缓冲区指针指向p[3]len字段为p[2],这俩参数未做判断

#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;

        ...
    }
}

获取的tlp[8],然后将tl加2,tl的类型是uint8_t,最大是255,加上2之后就是257evt_data.param_tlvs定义的长度为125,所以拷贝数组的时候直接溢出

#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的判断,这个字段表示当前已经拷贝的长度

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;

Last updated