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

```c++
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()`

```c++
#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]`，这俩参数未做判断

```c++
#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`，所以拷贝数组的时候直接溢出

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

```c++
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;
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wnagzihxa1n.gitbook.io/happy-android-security/system_security/cve20189585nfcncifprocgetrouting-wei-jian-ce-chang-du-yue-jie-du-xie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
