【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长度就进行读写数据的漏洞

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->eventBT_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_codeNCI_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.statusnfc_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

#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_IDS125,我们知道,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