> For the complete documentation index, see [llms.txt](https://wnagzihxa1n.gitbook.io/happy-android-security/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wnagzihxa1n.gitbook.io/happy-android-security/system_security/cve20189584nfcncifsetconfigstatus-wei-jian-ce-chang-du-yue-jie-du-xie.md).

# 【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           | 参考编号        | 类型  | 严重程度 | 已更新的 AOSP 版本              |
| ------------- | ----------- | --- | ---- | ------------------------- |
| 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长度就进行读写数据的漏洞

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

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

```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

/* 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`的位置

```c++
/* 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()`，此时可控的变量一共有三个

1. evt\_data.set\_config.status
2. evt\_data.set\_config.num\_param\_id
3. evt\_data.set\_config.param\_ids

```c++
#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`数组

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

再来回顾补丁

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


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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/cve20189584nfcncifsetconfigstatus-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.
