The booming IoT ecosystem has meant massive growth in the embedded systems market due to the high demand for connected devices. Nowadays, designing embedded devices is perhaps easier than ever thanks to the solutions, kits, chips, and code that semiconductor manufacturers provide to help developers cope with the vast number of heterogeneous requirements IoT devices should comply with.
This never-ending race to come up with new features within tight deadlines comes at a cost, which usually is paid in the security posture of the commercialized device.
Let's assume a product vendor has implemented security best practices and everything has been locked down properly. Our goal is to compromise the device, but we don't have access to any of the custom code developed for that specific device (not even in binary form). What about the code semiconductor vendors provide? How secure is the code in all those SDKs and middleware that IoT devices rely on?
I performed a manual code review of some of the most widely used IoT SDKs in order to try to answer this question and found multiple vulnerabilities in the code provided by leading semiconductor vendors, such as Texas Instruments, Nordic, and Qualcomm.
As usual, IOActive followed a responsible disclosure process, notifying the affected vendors and coordinating with them to determine the proper time to disclose issues. In general terms, most vendors properly handled the disclosure process.
At the time of publishing this blog post, the latest versions of the affected SDKs contain fixes for the vulnerabilities. Please note that IOActive has not verified these patches.
Introduction
Embedded IoT systems need to be designed for specific functions. As a result, we can't use a single reference design; however, it is possible to summarize the most common architectures.
SoC
These designs rely on an SoC that combines the MCU and communications transceiver into a single-chip solution. Thus, the wireless software stack and the application software run in the same component.
MCU + Transceiver
In these designs, the MCU is in charge of running the required software, including the applications and even part of the wireless stack, and the transceiver usually handles the physical and data link layers. In addition, there is a host interface (HIF), which is responsible for handling communication between the MCU (Host) and the transceiver, usually over a serial bus (e.g. SPI, UART, or I2C).
MCU + Network Co-Processor
These designs are a variant of the previous one, where the transceiver is swapped for a network co-processor (NCP), which runs the entire communications stack. In this architecture, the application is still running in the MCU (Host), while the network stack operations are entirely offloaded to the NCP. The HIF is still necessary to enable communication between the Host and NCP.
Attack Surface
As we have just outlined, the components within an embedded IoT design do not operate in isolation. They interact with the outside world via wireless and at the intra-board level with other chips.
1. Intra-board
Most current high-end MCUs and SoCs support some kind of secure boot or code integrity mechanism. Assuming a worst-case scenario (from an attacker’s perspective) where the vendor has closed the usual doors before deploying the product, we may face a pure black-box scenario, where we can’t dump or access the firmware.
In order to turn this situation to our favor, we can focus on the HIF code usually found in the SDKs from semiconductor vendors.
The advantage we have in this scenario is that, despite the fact that the firmware running in the Host MCU (Host from now on) may be unknown, analysis of HIF communications may reveal that the Host firmware has been compiled with potentially vulnerable SDK code.
A successful exploit targeting the HIF implementation could open the door to execute code in the Host if a local attacker has the ability to impersonate the transceiver/NCP over the HIF’s bus (SPI, UART, I2C, USB, etc.). At IOActive, we have been exploiting this attack vector for years in order to compromise among other devices, smart meters, which usually implement either an ‘MCU + NCP’ or ‘MCU + Transceiver’ design.
The vulnerabilities described in this post are intended to illustrate common patterns in the HIF layer implementation across different vendors, all of which lacked proper validation features. One of the advantages of intra-board attacks is that they can be used not only to discover memory corruption vulnerabilities, but much more interesting logic flaws, as Host firmware may not account for an attacker hijacking intra-board communication.
2. Wireless
From an attack surface perspective, the situation is similar to intra-board attacks. An attacker may lack access to the firmware, but it still possible to target the semiconductor vendor’s stack, usually provided in the corresponding component SDK.
This research focused on BLE and Zigbee stacks, which are some of the most common wireless communication interfaces used in the IoT ecosystem. The second part of this blog post series will cover these vulnerabilities.
Intra-board Vulnerabilities
Affected vendors
- Texas Instruments
- Qualcomm
- Silicon Labs
- Zephyr OS
- Microchip
- Infineon
Texas Instruments - www.ti.com
Vulnerability
Memory corruption via ‘NPITLSPI_CallBack’
Affected Products
CC26x0 BLE-STACK (v2.2.4 and prior versions)
Texas Instruments Advisory: https://www.ti.com/lit/an/swra684/swra684.pdf
Background
“TI’s Network Processor Interface (NPI) is used for establishing a serial data link between a TI SoC and external MCUs or PCs. It is an abstraction layer above the serial interface (UART or SPI) that handles sending / receiving data power management, and data parsing It is mainly used by TI’s network processor solutions.” Texas Instruments Website
Impact
A local attacker able to interfere with the physical SPI bus between the Host and NCP could send a malformed UNPI packet that corrupts dynamic memory in the Host, potentially achieving code execution.
Technical Details
When ‘NPITLSPI_CallBack’ parses the UNPI packet coming from the Slave, it does not properly verify whether the 16-bit length is within the bounds of ‘npiRxBuf’.
At lines 210-211, ‘readLen’ is directly calculated from the untrusted input coming from the slave.
Assuming that the local attacker will either guess or force a deterministic FCS by taking into account the malloc implementation, the memory corruption will take place at line 221.
File: ble_sdk_2_02_04_06/src/components/npi/src/unified/npi_tl_spi.c 190: // ----------------------------------------------------------------------------- 191: //! \brief This callback is invoked on transmission completion 192: //! 193: //! \param[in] handle - handle to the SPI port 194: //! \param[in] objTransaction - handle for SPI transmission 195: //! 196: //! \return void 197: // ----------------------------------------------------------------------------- 198: static void NPITLSPI_CallBack(SPI_Handle handle, SPI_Transaction *objTransaction) 199: { 200: uint16_t i; 201: uint16_t readLen = 0; 202: uint16_t storeWriteLen; 203: 204: // Check if a valid packet was found 205: // SOF: 206: if (npiRxBuf[NPI_SPI_MSG_SOF_IDX] == NPI_SPI_MSG_SOF && 207: objTransaction->count) 208: { 209: // Length: 210: readLen = npiRxBuf[NPI_SPI_MSG_LEN_LSB_IDX]; 211: readLen += ((uint16)npiRxBuf[NPI_SPI_MSG_LEN_MSB_IDX] << 8); 212: readLen += NPI_SPI_MSG_HDR_NOSOF_LEN; // Include the header w/o SOF 213: 214: // FCS: 215: if (npiRxBuf[readLen + 1] == 216: NPITLSPI_calcFCS(&npiRxBuf[NPI_SPI_MSG_LEN_LSB_IDX],readLen)) 217: { 218: // Message is valid. Shift bytes to remove SOF 219: for (i = 0 ; i < readLen; i++) 220: { 221: npiRxBuf[i] = npiRxBuf[i + 1]; 222: } 223: } 224: else 225: { 226: // Invalid FCS. Discard message 227: readLen = 0; 228: } 229: } 230: 231: //All bytes in TxBuf must be sent by this point 232: storeWriteLen = tlWriteLen; 233: tlWriteLen = 0; 234: RxActive = FALSE; 235: 236: if (npiTransmitCB) 237: { 238: npiTransmitCB(readLen,storeWriteLen); 239: } 240: }
‘npiRxBuf’ is initialized at line 193, using the default value of 530 bytes.
File: source/ti/npi/npi_task.c 198: /* Default NPI parameters structure */ 199: const NPI_Params NPI_defaultParams = { 200: .stackSize = 1024, 201: .bufSize = 530, … File: source/ti/npi/npi_task.c 511: // Initialize Transport Layer 512: transportParams.npiTLBufSize = params->bufSize; … File: ble_sdk_2_02_04_06/src/components/npi/src/unified/npi_tl.c 178: //! 179: //! \param[in] params - Transport Layer parameters 180: //! 181: //! \return void 182: // ----------------------------------------------------------------------------- 183: void NPITL_openTL(NPITL_Params *params) 184: { 185: _npiCSKey_t key; 186: key = NPIUtil_EnterCS(); 187: 188: // Set NPI Task Call backs 189: memcpy(&taskCBs, ¶ms->npiCallBacks, sizeof(params->npiCallBacks)); 190: 191: // Allocate memory for Transport Layer Tx/Rx buffers 192: npiBufSize = params->npiTLBufSize; 193: npiRxBuf = NPIUTIL_MALLOC(params->npiTLBufSize);
Qualcomm - www.qualcomm.com
Vulnerability
Multiple buffer overflows when parsing malformed WMI packets in the ‘Wifi_qca’ middleware
Affected Products
Products that use the Qualcomm Atheros WIFI_QCA middleware: https://www.qualcomm.com/products/qca4004
Background
"The Wireless Module Interface (WMI) is a communication protocol for QCA wireless components. It defines a set of commands that can be issued to the target firmware or that the target firmware can send back to the host for processing. This WMI communication is happening over the defined HIF layer."
Impact
A local attacker able to interfere with the physical SPI bus between the Host and target QCA SoC could send a malformed WMI packet that corrupts kernel memory in the Host, thus potentially achieving local code execution with kernel privileges.
Technical Details
There are multiple places in the QCA middleware where WMI messages coming from the device are not properly validated.
#1 ‘WMI_GET_CHANNEL_LIST_CMDID’
When processing ‘WMI_GET_CHANNEL_LIST_CMDID’ at ‘wmi_control_rx’ there is no sanity check for the attacker-controlled value ‘CHAN_EV->numChannels’.
File: middleware/wifi_qca/common_src/wmi/wmi.c 667: switch (id) 668: { 669: case (WMI_GET_CHANNEL_LIST_CMDID): 670: A_WMI_CHANNELLIST_RX(wmip->wmi_devt, devId, (int8_t)CHAN_EV->numChannels, (void*)CHAN_EV->channelList); 671: break; 672: case (WMI_GET_BITRATE_CMDID):
‘Api_ChannelListEvent’ then uses ‘numChan’ to calculate the number of bytes, without performing any bounds checking, that will be copied into the fixed buffer ‘pDCxt->ChannelList’.
File: middleware/wifi_qca/common_src/api_interface/api_wmi_rx.c 119: void Api_ChannelListEvent(void *pCxt, uint8_t devId, int8_t numChan, void *chanList) 120: { 121: uint16_t i; 122: A_DRIVER_CONTEXT *pDCxt = GET_DRIVER_COMMON(pCxt); 123: UNUSED_ARGUMENT(devId); 124: A_MEMCPY(pDCxt->channelList, chanList, numChan * sizeof(uint16_t)); 125: pDCxt->numChannels = numChan; 126: /* convert each channel to proper endianness */ 127: for (i = 0; i < pDCxt->numChannels; i++) 128: { 129: pDCxt->channelList[i] = A_LE2CPU16(pDCxt->channelList[i]); 130: } 131: 132: API_CHANNEL_LIST_EVENT(pCxt, pDCxt->numChannels, pDCxt->channelList); 133: } 134:
This buffer is defined within the Driver Context structure at line 256. In terms of exploitability, an attacker could easily overwrite a function pointer at line 272 with controlled values.
File: middleware/wifi_qca/common_src/include/driver_cxt.h 56: #define MAX_NUM_CHANLIST_CHANNELS (16) File: middleware/wifi_qca/common_src/include/driver_cxt.h 265: uint16_t channelList[MAX_NUM_CHANLIST_CHANNELS]; 266: 267: uint32_t regCode; 268: int8_t rssi; 269: int32_t bitRate; 270: boolean wmmEnabled; 271: boolean tx_complete_pend; /* tracks new tx completes for task sync */ 272: TEMP_FUNC_T asynchRequest;
#2 ‘WMI_STORERECALL_STORE_EVENTID’
File: middleware/wifi_qca/common_src/wmi/wmi.c 775: case (WMI_STORERECALL_STORE_EVENTID): 776: A_WMI_STORERECALL_EVENT_RX(wmip->wmi_devt, devId, datap, len, osbuf); 777: break;
When processing ‘WMI_STORERECALL_STORE_EVENTID’ at ‘wmi_control_rx’, there is no sanity check for the ‘len’ value, in order to verify it is not larger than ‘pDCxt->tempStorageLength’. As a result, an overly large WMI packet could corrupt the fixed-length buffer pointed to by ‘strrclData’. This buffer is initialized at ‘Custom_Driver_ContextInit’.
File: middleware/wifi_qca/common_src/api_interface/api_wmi_rx.c 882: void Api_StoreRecallEvent(void *pCxt, uint8_t devId, uint8_t *datap, int32_t len, void *pReq) 883: { 884: WMI_STORERECALL_STORE_EVENT *pEv; 885: A_DRIVER_CONTEXT *pDCxt = GET_DRIVER_COMMON(pCxt); 886: uint8_t *strrclData = pDCxt->tempStorage; 887: 888: UNUSED_ARGUMENT(devId); 889: 890: switch (pDCxt->strrclState) 891: { 892: case STRRCL_ST_START: 893: A_MEMCPY(strrclData, datap, len);
This buffer is initialized at ‘Custom_Driver_ContextInit’.
File: middleware/wifi_qca/custom_src/driver/cust_driver_main.c 150: Custom_Driver_ContextInit(void *pCxt) 151: { 152: uint32_t tempStorageLen = 0; 153: A_DRIVER_CONTEXT *pDCxt = GET_DRIVER_COMMON(pCxt); 154: 155: /*Allocate the temporary storage buffer. This may be shared by multiple modules. 156: If store recall is enabled, it may use this buffer for storing target data. 157: Will also be shared by scan module to store scan results*/ 158: tempStorageLen = max((STORE_RECALL_BUF_SIZE), (ATH_MAX_SCAN_BUFFERS * sizeof(QCA_SCAN_INFO))); 159: tempStorageLen = max(tempStorageLen, (ATH_MAX_SCAN_BUFFERS * sizeof(ATH_SCAN_EXT))); 160: 161: if (tempStorageLen) 162: { 163: if (NULL == (pDCxt->tempStorage = A_MALLOC(tempStorageLen, 0))) 164: { 165: return A_NO_MEMORY; 166: } 167: pDCxt->tempStorageLength = tempStorageLen;
#3 ‘WMI_HOST_DSET_STORE_EVENTID’
File: middleware/wifi_qca/common_src/wmi/wmi.c 783: case (WMI_HOST_DSET_STORE_EVENTID): 784: A_WMI_HOST_DSET_EVENT_STORE_RX(wmip->wmi_devt, devId, datap, len, osbuf);
At ‘Api_HostDsetStoreEvent’,’dset_length’ is an attacker-controlled value. At line 418, there is an integer overflow which could bypass the ‘if’ condition. As a result, ‘dset_length’ can still be larger than ‘pDCxt->tempStorageLength’, leading to memory corruption.
File: middleware/wifi_qca/common_src/api_interface/api_wmi_rx.c 403: int dset_length; 404: 405: void Api_HostDsetStoreEvent(void *pCxt, uint8_t devId, uint8_t *datap, int32_t len, void *pReq) 406: { 407: A_DRIVER_CONTEXT *pDCxt = GET_DRIVER_COMMON(pCxt); 408: uint8_t *strrclData = pDCxt->tempStorage; 409: uint16_t cur_len = pDCxt->strrclCxtLen; 410: 411: WMI_HOST_DSET_STORE_EVENT *pDsetEvent = (WMI_HOST_DSET_STORE_EVENT *)datap; 412: UNUSED_ARGUMENT(devId); 413: 414: dset_length = pDsetEvent->length; 415: 416: strrclData = pDCxt->tempStorage + cur_len; 417: 418: if (cur_len + dset_length <= pDCxt->tempStorageLength) 419: { 420: A_MEMCPY(strrclData, pDsetEvent->data, dset_length); 421: } 422: else 423: { 424: A_ASSERT(0); 425: }426: pDCxt->strrclCxtLen += dset_length; 427: }
Any other code relying on ‘dset_length’ may also be vulnerable (e.g. ‘Api_HostDsetReadEvent’).
#4 ‘WMI_P2P_NODE_LIST_EVENTID’
File: middleware/wifi_qca/common_src/wmi/wmi.c 834: case WMI_P2P_NODE_LIST_EVENTID: 835: status = wmi_p2p_node_list_event_rx(wmip, devId, datap, len); 836: break;
When processing ‘WMI_P2P_NODE_LIST_EVENTID’ messages coming from the device, the attacker-controlled value ‘num_p2p_dev’ is not sanitized. As a result, at line 1399 it is possible to corrupt the fixed-length buffer pointed to by ‘tmpBuf’, which is ‘pCxt->pScanOut’ (actually, it is ‘pDCxt->tempStorage’, which has been previously explained).
File: middleware/wifi_qca/common_src/api_interface/api_wmi_rx.c 1382: void Api_p2p_node_list_event(void *pCxt, uint8_t devId, uint8_t *datap, uint32_t len) 1383: { 1384: uint32_t evt_id = 0; 1385: uint8_t *tmpBuf; 1386: evt_id = WMI_P2P_NODE_LIST_EVENTID; 1387: WMI_P2P_NODE_LIST_EVENT *handleP2PDev = (WMI_P2P_NODE_LIST_EVENT *)datap; 1388: A_DRIVER_CONTEXT *pDCxt = GET_DRIVER_COMMON(pCxt); 1389: 1390: tmpBuf = GET_DRIVER_COMMON(pCxt)->pScanOut; 1391: DRIVER_SHARED_RESOURCE_ACCESS_ACQUIRE(pCxt); 1392: { 1393: A_MEMZERO(GET_DRIVER_COMMON(pCxt)->pScanOut, pDCxt->tempStorageLength); 1394: A_MEMCPY(GET_DRIVER_COMMON(pCxt)->pScanOut, &evt_id, sizeof(uint32_t)); 1395: tmpBuf += sizeof(uint32_t); 1396: *tmpBuf = handleP2PDev->num_p2p_dev; 1397: tmpBuf++; 1398: 1399: A_MEMCPY((uint8_t *)tmpBuf, ((uint8_t *)(handleP2PDev->data)), 1400: (sizeof(P2P_DEVICE_LITE) * (handleP2PDev->num_p2p_dev))); 1401: 1402: GET_DRIVER_COMMON(pCxt)->p2pEvtState = false; 1403: } 1404: DRIVER_SHARED_RESOURCE_ACCESS_RELEASE(pCxt); 1405: 1406: DRIVER_WAKE_USER(pCxt); 1407: }
Silicon Labs - www.silabs.com
Vulnerability
Buffer overflow in ‘sl_wfx_get_pmk’
Affected Products
Silicon Labs’ FMAC WFx driver: https://github.com/SiliconLabs/wfx-fullMAC-driver/
Background
“The WFx FMAC driver is a software resource meant to allow a host to communicate with the WFx Wi-Fi transceiver. The API exposed by the driver gives control over the WFx Wi-Fi capabilities. In addition, the API enables data transfer at the IP level. This means that the host requires an IP stack if it wants to send/receive Ethernet frames.”
https://docs.silabs.com/wifi/wf200/rtos/latest/wfx-fmac-driver
Impact
A local attacker able to interfere with the physical SPI/SDIO bus between the Host and the Silicon Labs NCP could forge a malformed WFx response frame that corrupts memory in the Host, thus potentially achieving local code execution.
Technical Details
‘sl_wfx_get_pmk’ does not sanitize ‘reply->body.password_length’, by either comparing it to ‘password_length’ value or checking against SL_WFX_PASSWORD_SIZE, before copying it (line 1119) into the provided buffer. As a result, there is no guarantee that the buffer pointed by ‘password’ can safely receive the length specified in the response.
File: wfx-fullMAC-driver-master/wfx_fmac_driver/sl_wfx.c 1097: /**************************************************************************//** 1098: * @brief Get the PMK used to connect to the current secure network 1099: * 1100: * @param password is the current Pairwise Master Key 1101: * @param password_length is its length in bytes 1102: * @param interface is the interface used to send the request. 1103: * @arg SL_WFX_STA_INTERFACE 1104: * @arg SL_WFX_SOFTAP_INTERFACE 1105: * @returns SL_STATUS_OK if the command has been sent correctly, 1106: * SL_STATUS_FAIL otherwise 1107: *****************************************************************************/ 1108: sl_status_t sl_wfx_get_pmk(uint8_t *password, 1109: uint32_t *password_length, 1110: sl_wfx_interface_t interface) 1111: { 1112: sl_status_t result; 1113: sl_wfx_get_pmk_cnf_t *reply = NULL; 1114: 1115: result = sl_wfx_send_command(SL_WFX_GET_PMK_REQ_ID, NULL, 0, interface, (sl_wfx_generic_confirmation_t **)&reply); 1116: 1117: if (result == SL_STATUS_OK) { 1118: result = sl_wfx_get_status_code(sl_wfx_htole32(reply->body.status), SL_WFX_GET_PMK_REQ_ID); 1119: memcpy(password, reply->body.password, sl_wfx_htole32(reply->body.password_length)); 1120: *password_length = sl_wfx_htole32(reply->body.password_length); 1121: } 1122: 1123: return result; 1124: }
Vulnerability
Kernel memory corruption when decoding ‘Secure Channel’ HIF frame
Affected Products
Silicon Labs’ WFx Linux driver: https://github.com/SiliconLabs/wfx-linux-driver
Background
Silicon Labs’ WF(M)200 chips have the ability to encrypt the SPI or SDIO serial link between the Host and the device.
Impact
A local attacker able to interfere with the physical SPI/SDIO bus between the Host and the Silicon Labs NCP could send a malformed HIF frame that corrupts kernel memory in the Host, thus potentially achieving local code execution with kernel privileges.
Technical Details
The driver handles attacker-controlled inputs (lines 78-80) when the HIF protocol is using the ‘secure channel functionality,’ even before the proper sanity check on the ‘hif->len’ field is performed (at line 94). As a result, the computed length for the HIF frame would be different from the actual ‘read_len’.
File: wfx-linux-driver-master/bh.c 051: static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) 052: { 053: struct sk_buff *skb; 054: struct hif_msg *hif; 055: size_t alloc_len; 056: size_t computed_len; 057: int release_count; 058: int piggyback = 0; 059: 060: WARN(read_len < 4, "corrupted read"); 061: WARN(read_len > round_down(0xFFF, 2) * sizeof(u16), 062: "%s: request exceed WFx capability", __func__); 063: 064: // Add 2 to take into account piggyback size 065: alloc_len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, read_len + 2); 066: skb = dev_alloc_skb(alloc_len); 067: if (!skb) 068: return -ENOMEM; 069: 070: if (wfx_data_read(wdev, skb->data, alloc_len)) 071: goto err; 072: 073: piggyback = le16_to_cpup((__le16 *)(skb->data + alloc_len - 2)); 074: _trace_piggyback(piggyback, false); 075: 076: hif = (struct hif_msg *)skb->data; 077: WARN(hif->encrypted & 0x1, "unsupported encryption type"); 078: if (hif->encrypted == 0x2) { 079: if (wfx_sl_decode(wdev, (void *)hif)) { 080: dev_kfree_skb(skb); 081: // If frame was a confirmation, expect trouble in next 082: // exchange. However, it is harmless to fail to decode 083: // an indication frame, so try to continue. Anyway, 084: // piggyback is probably correct. 085: return piggyback; 086: } 087: computed_len = 088: round_up(le16_to_cpu(hif->len) - sizeof(hif->len), 16) + 089: sizeof(struct hif_sl_msg) + 090: sizeof(struct hif_sl_tag); 091: } else { 092: computed_len = round_up(le16_to_cpu(hif->len), 2); 093: } 094: if (computed_len != read_len) { 095: dev_err(wdev->dev, "inconsistent message length: %zu != %zu\n", 096: computed_len, read_len); 097: print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET, 16, 1, 098: hif, read_len, true); 099: goto err; 100: }
‘clear_len’ is controlled by the attacker, so ‘payload_len’, ‘tag’ and ‘output’ are also indirectly controlled to some extent (lines 51-54). At line 69, ‘mbedtls_ccm_auth_decrypt’ is invoked to decrypt the HIF payload using ‘payload_len’ with the ‘skb->data’ buffer as output. As the length of ‘skb->data’ may be different than ‘payload_len’, it is possible to corrupt that memory chunk.
File: wfx-linux-driver-master/secure_link.c 48: int wfx_sl_decode(struct wfx_dev *wdev, struct hif_sl_msg *m) 49: { 50: int ret; 51: size_t clear_len = le16_to_cpu(m->len); 52: size_t payload_len = round_up(clear_len - sizeof(m->len), 16); 53: u8 *tag = m->payload + payload_len; 54: u8 *output = (u8 *)m; 55: u32 nonce[3] = { }; 56: 57: WARN(m->hdr.encrypted != 0x02, "packet is not encrypted"); 58: 59: // Other bytes of nonce are 0 60: nonce[1] = m->hdr.seqnum; 61: if (wdev->sl.rx_seqnum != m->hdr.seqnum) 62: dev_warn(wdev->dev, "wrong encrypted message sequence: %d != %d\n", 63: m->hdr.seqnum, wdev->sl.rx_seqnum); 64: wdev->sl.rx_seqnum = m->hdr.seqnum + 1; 65: if (wdev->sl.rx_seqnum == slk_renew_period) 66: schedule_work(&wdev->sl.key_renew_work); 67: 68: memcpy(output, &m->len, sizeof(m->len)); 69: ret = mbedtls_ccm_auth_decrypt(&wdev->sl.ccm_ctxt, payload_len, 70: (u8 *)nonce, sizeof(nonce), NULL, 0, 71: m->payload, output + sizeof(m->len), 72: tag, sizeof(struct hif_sl_tag)); 73: if (ret) { 74: dev_err(wdev->dev, "mbedtls error: %08x\n", ret); 75: return -EIO; 76: } 77: if (memzcmp(output + clear_len, payload_len + sizeof(m->len) - clear_len)) 78: dev_warn(wdev->dev, "padding is not 0\n"); 79: return 0; 80: }
The actual memory corruption happens at line 146 in CTR_CRYPT as ‘dst’ is pointing to HIF’s payload.
File: wfx-linux-driver-master/mbedtls/library/ccm.c 131: /* 132: * Encrypt or decrypt a partial block with CTR 133: * Warning: using b for temporary storage! src and dst must not be b! 134: * This avoids allocating one more 16 bytes buffer while allowing src == dst. 135: */ 136: #define CTR_CRYPT( dst, src, len ) \ 137: do \ 138: { \ 139: if( ( ret = mbedtls_cipher_update( &ctx->cipher_ctx, ctr, \ 140: 16, b, &olen ) ) != 0 ) \ 141: { \ 142: return( ret ); \ 143: } \ 144: \ 145: for( i = 0; i < (len); i++ ) \ 146: (dst)[i] = (src)[i] ^ b[i]; \ 147: } while( 0 )
Zephyr OS - www.zephyrproject.org
Vulnerability
Multiple buffer overflows in the ‘Zephyr’ eswifi driver
Affected Products
Zephyr RTOS 2.3.0:
- - https://github.com/zephyrproject-rtos/zephyr
- - https://www.zephyrproject.org/
Impact
A local attacker able to interfere with the physical SPI bus between the Host and target controller could send a malformed SPI response that corrupts kernel memory in the Host, thus potentially achieving local code execution with kernel privileges.
Technical Details
#1 ‘__parse_ipv4_address’ buffer overflow
This function does not properly verify that ‘byte’ is within IP’s bounds (4 bytes) when parsing the IPv4 address. As a result, a malformed IP string with an overly large number of ‘dots’ will corrupt the ‘ip’ buffer.
File: zephyr-master/drivers/wifi/eswifi/eswifi_core.c 179: static int __parse_ipv4_address(char *str, char *ssid, uint8_t ip[4]) 180: { 181: unsigned int byte = -1; 182: 183: /* fmt => [JOIN ] SSID,192.168.2.18,0,0 */ 184: while (*str) { 185: if (byte == -1) { 186: if (!strncmp(str, ssid, strlen(ssid))) { 187: byte = 0U; 188: str += strlen(ssid); 189: } 190: str++; 191: continue; 192: } 193: 194: ip[byte++] = atoi(str); 195: while (*str && (*str++ != '.')) { 196: } 197: } 198: 199: return 0; 200: }
This vulnerability results in a stack overflow at lines 243 and 286.
File:zephyr-master/drivers/wifi/eswifi/eswifi_core.c 240: static int eswifi_connect(struct eswifi_dev *eswifi) 241: { 242: char connect[] = "C0\r"; 243: struct in_addr addr; …SNIP… 284: /* Any IP assigned ? (dhcp offload or manually) */ 285: err = __parse_ipv4_address(rsp, eswifi->sta.ssid, 286: (uint8_t *)&addr.s4_addr); 287: if (err < 0) { 288: LOG_ERR("Unable to retrieve IP address");289: goto error; 290: }
#2 ‘__parse_ssid’ buffer overflow
A similar situation can be found in ‘__parse_ssid’, which extracts and then copies the quoted SSID coming from the SPI response without checking its length.
File: zephyr-master/drivers/wifi/eswifi/eswifi_core.c 54: static inline int __parse_ssid(char *str, char *ssid) 55: { 56: /* fnt => '"SSID"' */ 57: 58: if (!*str || (*str != '"')) { 59: return -EINVAL; 60: } 61: 62: str++; 63: while (*str && (*str != '"')) { 64: *ssid++ = *str++; 65: } 66: 67: *ssid = '\0'; 68: 69: if (*str != '"') { 70: return -EINVAL; 71: } 72: 73: return -EINVAL; 74: } 75:
This vulnerability also ends up in a stack overflow scenario according to the following vulnerable path:
1.
File: zephyr-master/drivers/wifi/eswifi/eswifi_core.c 202: static void eswifi_scan(struct eswifi_dev *eswifi) 203: { 204: char cmd[] = "F0\r"; 205: char *data; 206: int i, ret; 207: 208: LOG_DBG(""); 209: 210: eswifi_lock(eswifi); 211: 212: ret = eswifi_at_cmd_rsp(eswifi, cmd, &data); 213: if (ret < 0) { 214: eswifi->scan_cb(eswifi->iface, -EIO, NULL); 215: eswifi_unlock(eswifi); 216: return; 217: } 218: 219: for (i = 0; i < ret; i++) { 220: if (data[i] == '#') { 221: struct wifi_scan_result res = {0}; 222: 223: __parse_scan_res(&data[i], &res); 224: 225: eswifi->scan_cb(eswifi->iface, 0, &res); 226: k_yield(); 227: 228: while (data[i] && data[i] != '\n') { 229: i++; 230: } 231: } 232: }
2.
File: zephyr-master/include/net/wifi_mgmt.h 84: /* Each result is provided to the net_mgmt_event_callback 85: * via its info attribute (see net_mgmt.h) 86: */ 87: struct wifi_scan_result { 88: uint8_t ssid[WIFI_SSID_MAX_LEN]; 89: uint8_t ssid_length; 90: 91: uint8_t channel; 92: enum wifi_security_type security; 93: int8_t rssi; 94: };
3.
File: zephyr-master/drivers/wifi/eswifi/eswifi_core.c 76: static void __parse_scan_res(char *str, struct wifi_scan_result *res) 77: { 78: int field = 0; 79: 80: /* fmt => #001,"SSID",MACADDR,RSSI,BITRATE,MODE,SECURITY,BAND,CHANNEL */ 81: 82: while (*str) { 83: if (*str != ',') { 84: str++; 85: continue; 86: } 87: 88: if (!*++str) { 89: break; 90: } 91: 92: switch (++field) { 93: case 1: /* SSID */ 94: __parse_ssid(str, res->ssid);
Microchip - www.microchip.com
Vulnerability
Multiple vulnerabilities in ‘CryptoAuth Lib’ 3.2.2
Affected Products
Patched https://github.com/MicrochipTech/cryptoauthlib/releases/tag/v3.2.3
Background
"Designed to work with CryptoAuthentication devices such as the ATECC608B, ATSHA204A or ATSHA206A and to simplify your development, the CryptoAuthLib is a software support library written in C code. It is a component of any application or device driver that requires crypto services from the CryptoAuthentication devices. It works on a variety of platforms including Arm® Cortex®-M based or PIC® microcontrollers, PCs running the Windows® operating system or an embedded Linux® platform"
Microchip Website
Impact
A local attacker able to partially emulate a malicious of Crypto Authentication device through USB could send malformed KIT protocol packets that corrupt memory in the Host, thus potentially achieving code execution.
Technical Details
When ‘kit_phy_receive’ is receiving the KIT packet from the device, it does not properly verify whether the total amount of bytes received is within the bounds of ‘rxdata’.
The reading loop’s condition is merely ‘continue_read == true’ without taking into account ‘rxsize’, at line 324.
At line 308, we can see how the logic constantly tries to read a fixed amount of one byte from the USB device.
At line 316, ‘total_bytes_read’ is incremented by ‘bytes_read’. As the attacker controls the input, it is possible to evade the check for ‘\n’ at line 319. As a result, ‘total_bytes_read’ will be incremented beyond ‘rxsize’, thus overflowing ‘rxdata’ at line 308 during the call to ‘fread’.
File: secure_elements/lib/hal/hal_linux_kit_hid.c 287: ATCA_STATUS kit_phy_receive(ATCAIface iface, uint8_t* rxdata, int* rxsize) 288: { 289: ATCAIfaceCfg *cfg = atgetifacecfg(iface); 290: atcahid_t* pHid = (atcahid_t*)atgetifacehaldat(iface); 291: bool continue_read = true; 292: size_t bytes_read = 0; 293: size_t total_bytes_read = 0; 294: 295: if ((rxdata == NULL) || (rxsize == NULL) || (cfg == NULL) || (pHid == NULL)) 296: { 297: return ATCA_BAD_PARAM; 298: } 299: 300: if (pHid->kits[cfg->atcahid.idx].read_handle == NULL) 301: { 302: return ATCA_COMM_FAIL; 303: } 304: 305: // Receive the data from the kit USB device 306: do 307: { 308: bytes_read = fread(&rxdata[total_bytes_read], sizeof(uint8_t), 1, 309: pHid->kits[cfg->atcahid.idx].read_handle); 310: if (ferror(pHid->kits[cfg->atcahid.idx].read_handle) != 0) 311: { 312: clearerr(pHid->kits[cfg->atcahid.idx].read_handle); 313: return ATCA_RX_FAIL; 314: } 315: 316: total_bytes_read += bytes_read; 317: 318: // Check if the kit protocol message has been received 319: if (strstr((char*)rxdata, "\n") != NULL) 320: { 321: continue_read = false; 322: } 323: } 324: while (continue_read == true); 325: 326: // Save the total bytes read 327: *rxsize = total_bytes_read; 328: 329: return ATCA_SUCCESS;
A similar issue can also be found in ‘kit_phy_receive’, although in this case instead of just one byte, it is reading CDC_BUFFER_MAX to a local stack buffer at line 263. ‘bytes_to_cpy’ is used to increment the offset (‘total_bytes’) (line 288) where the bytes will be copied (line 287).
File: secure_elements/lib/hal/hal_linux_kit_cdc.c 232: ATCA_STATUS kit_phy_receive(ATCAIface iface, char* rxdata, int* rxsize) 233: { 234: ATCA_STATUS status = ATCA_SUCCESS; 235: ATCAIfaceCfg *cfg = atgetifacecfg(iface); 236: int cdcid = cfg->atcauart.port; 237: atcacdc_t* pCdc = (atcacdc_t*)atgetifacehaldat(iface); 238: uint8_t buffer[CDC_BUFFER_MAX] = { 0 }; 239: bool continue_read = true; 240: int bytes_read = 0; 241: uint16_t total_bytes = 0; 242: char* location = NULL; 243: int bytes_remain = 0; 244: int bytes_to_cpy = 0; 245: 246: do 247: { 248: // Verify the input variables 249: if ((rxdata == NULL) || (rxsize == NULL) || (pCdc == NULL)) 250: { 251: status = ATCA_BAD_PARAM; 252: break; 253: } 254: // Verify the write handle 255: if (pCdc->kits[cdcid].read_handle == INVALID_HANDLE_VALUE) 256: { 257: status = ATCA_COMM_FAIL; 258: break; 259: } 260: // Read all of the bytes 261: while (continue_read == true) 262: { 263: bytes_read = read(pCdc->kits[cdcid].read_handle, buffer, CDC_BUFFER_MAX); 264: 265: // Find the location of the '\n' character in read buffer 266: // todo: generalize this read... it only applies if there is an ascii protocol with an <eom> of \n and if the <eom> exists 267: location = strchr((char*)&buffer[0], '\n'); 268: if (location == NULL) 269: { 270: // Copy all of the bytes 271: bytes_to_cpy = bytes_read; 272: } 273: else 274: { 275: // Copy only the bytes remaining in the read buffer to the <eom> 276: bytes_to_cpy = (uint8_t)(location - (char*)buffer) + 1; 277: // The response has been received, stop receiving more data 278: continue_read = false; 279: } 280: // Protect rxdata from overwriting, this will have the result of truncating the returned bytes 281: // Remaining space in rxdata 282: //bytes_remain = (*rxsize - total_bytes); 283: // Use the minimum between number of bytes read and remaining space 284: //bytes_to_cpy = min(bytes_remain, bytes_to_cpy); 285: 286: // Copy the received data 287: memcpy(&rxdata[total_bytes], &buffer[0], bytes_to_cpy); 288: total_bytes += bytes_to_cpy; 289: } 290: 291: } 292: while (0); 293: 294: *rxsize = total_bytes; 295: #ifdef KIT_DEBUG 296: printf("<-- %s", rxdata); 297: #endif 298: return status; 299: } 300:
A similar situation is found below, where the function is constantly trying to read ‘cfg->atcahid.packetsize + 1)’ and then copy to ‘rxdata’ at lines 365 without performing any bounds checking.
File: secure_elements/lib/hal/hal_win_kit_hid.c 333: while (continue_read == true) 334: { 335: result = ReadFile(pHid->kits[hidid].read_handle, buffer, (cfg->atcahid.packetsize + 1), &bytes_read, NULL); 336: if (result == false) 337: { 338: return ATCA_RX_FAIL; 339: } 340: // Find the location of the '\n' character in read buffer 341: // todo: generalize this read... it only applies if there is an ascii protocol with an <eom> of \n and if the <eom> exists 342: location = strchr((char*)&buffer[1], '\n'); 343: if (location == NULL) 344: { 345: // Copy all of the bytes 346: bytes_to_cpy = bytes_read; 347: // An extra byte is prepended to HID communication 348: size_adjust = 1; 349: } 350: else 351: { 352: // Copy only the bytes remaining in the read buffer to the <eom> 353: bytes_to_cpy = (uint8_t)(location - (char*)buffer); 354: size_adjust = 0; 355: // The response has been received, stop receiving more data 356: continue_read = false; 357: } 358: // Protect rxdata from overwriting, this will have the result of truncating the returned bytes 359: // Remaining space in rxdata 360: //bytes_remain = (*rxsize - total_bytes); 361: // Use the minimum between number of bytes read and remaining space 362: //bytes_to_cpy = min(bytes_remain, bytes_to_cpy); 363: 364: // Copy the received data 365: memcpy(&rxdata[total_bytes], &buffer[1], bytes_to_cpy); 366: total_bytes += bytes_to_cpy - size_adjust; 367: }
Infineon - www.infineon.com
Vulnerability
Memory corruption via ‘DtlsRL_Record_ProcessRecord’
Affected Products
Optiga Trust X DTLS: https://github.com/Infineon/optiga-trust-x/tree/master/optiga/dtls
Background
Impact
A local attacker able to interfere with the physical I2C bus between the Host and Optiga Trust X security chip could send a malformed DTLS record that corrupts heap memory in the Host, thus potentially achieving local code execution.
Technical Details
During the DTLS handshake, the fragment length field of a DTLS record is not properly sanitized.
File: optiga_trust_x/optiga/dtls/DtlsRecordLayer.c 159: wRecvFragLen = Utility_GetUint16(PpsBlobRecord->prgbStream + OFFSET_RL_FRAG_LENGTH);
‘wRecvFragLen’ is an attacker-controlled 16-bit value, which is used in the subsequent record processing logic without being sanitized. As a result, it is possible to trigger memory corruption at line 235
File: optiga_trust_x/optiga/dtls/DtlsRecordLayer.c 233: PpsRecData->bContentType = bContentType; 234: //No Decryption, just copy the data 235: Utility_Memmove(PpsRecData->psBlobInOutMsg->prgbStream, \ 236: PpsBlobRecord->prgbStream + OFFSET_RL_FRAGMENT, \ 237: wRecvFragLen); 238: PpsRecData->psBlobInOutMsg->wLen = wRecvFragLen; 239: i4Status = OCP_RL_OK;
‘PpsRecData->psBlobInOutMsg->prgbStream’ points to a dynamically allocated buffer whose size is fixed (TLBUFFER_SIZE 1500 bytes).
File: optiga_trust_x/optiga/dtls/DtlsHandshakeProtocol.c
1361: sMessageLayer.sTLMsg.prgbStream = (uint8_t*)OCP_MALLOC(TLBUFFER_SIZE);
The vulnerable path would be as follows:
- DtlsHS_Handshake
- DtlsHS_ReceiveFlightMessage
- DtlsRL_Recv
- DtlsCheckReplay
- DtlsRL_CallBack_ValidateRec
- DtlsRL_Record_ProcessRecord
Vulnerability
Memory corruption via ‘CmdLib_CalcHash’
Affected Products
Optiga Trust X: https://github.com/Infineon/optiga-trust-x/
Impact
A local attacker able to interfere with the physical I2C bus between the Host and Optiga Trust X security chip could send a malformed ‘CmdLib_CalcHash’ response that corrupts memory in the Host, thus potentially achieving local code execution.
Technical Details
In the ‘CmdLib_CalcHash’ function, a potentially untrusted length field is used without being sanitized.
The tag length is directly obtained from the response buffer at line 1757, which could contain a value larger than ‘PpsCalcHash->sOutHash.wBufferLength’.Then at line 1758, this length is used to perform a ‘memcpy’ operation that will trigger the memory corruption. The same issue applies to lines 1772 and 1773
File: optiga_trust_x/optiga/cmd/CommandLib.c 1748: if((TAG_HASH_OUTPUT == (*(sApduData.prgbRespBuffer + LEN_APDUHEADER))) && (sApduData.wResponseLength != 0)) 1749: { 1750: //Length check for sOutData 1751: if((psHashinfo->bHashLen) > PpsCalcHash->sOutHash.wBufferLength) 1752: { 1753: i4Status = (int32_t)CMD_LIB_INSUFFICIENT_MEMORY; 1754: break; 1755: } 1756: 1757: PpsCalcHash->sOutHash.wRespLength = Utility_GetUint16(sApduData.prgbRespBuffer + LEN_APDUHEADER + BYTES_SEQ); 1758: OCP_MEMCPY(PpsCalcHash->sOutHash.prgbBuffer, (sApduData.prgbRespBuffer + CALC_HASH_FIXED_OVERHEAD_SIZE), PpsCalcHash->sOutHash.wRespLength); 1759: } 1760: 1761: //Validate the Context buffer size if the 0x06 context data tag is there in response and 1762: //copy the context data to pbContextData buffer 1763: if((TAG_CONTEXT_OUTPUT == (*(sApduData.prgbRespBuffer + LEN_APDUHEADER))) && (sApduData.wResponseLength != 0)) 1764: { 1765: //Length check for Context Data 1766: if((psHashinfo->wHashCntx) > PpsCalcHash->sContextInfo.dwContextLen) 1767: { 1768: i4Status = (int32_t)CMD_LIB_INSUFFICIENT_MEMORY; 1769: break; 1770: } 1771: 1772: PpsCalcHash->sContextInfo.dwContextLen = Utility_GetUint16(sApduData.prgbRespBuffer + LEN_APDUHEADER + BYTES_SEQ); 1773: OCP_MEMCPY(PpsCalcHash->sContextInfo.pbContextData, (sApduData.prgbRespBuffer + CALC_HASH_FIXED_OVERHEAD_SIZE), PpsCalcHash->sContextInfo.dwContextLen);
Vulnerability
Multiple memory corruption issues in ‘Optiga_cmd.c’
Affected Products
Optiga Trust M: https://github.com/Infineon/optiga-trust-m/
Background
Impact
A local attacker able to interfere with the physical I2C bus between the Host and Optiga Trust M security chip could send a malformed response that corrupts memory in the Host, thus potentially achieving local code execution.
Technical Details
In the ‘optiga_cmd_gen_keypair_handler’ function, a potentially untrusted length field is used without being sanitized.
The private key tag length is directly obtained from the response buffer at line 2576, which could contain a value larger than the buffer pointed by ‘p_optiga_ecc_gen_keypair->private_key’. Then at line 2579, this length is used to perform a ‘memcpy’ operation that will trigger the memory corruption.
File: infineon/optiga_cmd.c 2573: else if (CMD_GEN_KEY_PAIR_PRIVATE_KEY_TAG == me->p_optiga->optiga_comms_buffer[OPTIGA_CMD_APDU_INDATA_OFFSET + 2574: header_offset]) 2575: { 2576: optiga_common_get_uint16(&me->p_optiga->optiga_comms_buffer[OPTIGA_CMD_APDU_INDATA_OFFSET + header_offset 2577: + OPTIGA_CMD_NO_OF_BYTES_IN_TAG], &private_key_length); 2578: 2579: pal_os_memcpy(p_optiga_ecc_gen_keypair->private_key, 2580: &me->p_optiga->optiga_comms_buffer[OPTIGA_CMD_APDU_INDATA_OFFSET + header_offset + 2581: OPTIGA_CMD_UINT16_SIZE_IN_BYTES+ OPTIGA_CMD_NO_OF_BYTES_IN_TAG], private_key_length); 2582: header_offset += OPTIGA_CMD_UINT16_SIZE_IN_BYTES+ OPTIGA_CMD_NO_OF_BYTES_IN_TAG + private_key_length; 2583: return_status = OPTIGA_LIB_SUCCESS;
The same issue applies to lines 3068 and 3072 while processing the ‘Calc_Hash’ response in function ‘optiga_cmd_calc_hash_handler’.
File: infineon/optiga_cmd.c 3052: case OPTIGA_CMD_EXEC_PROCESS_RESPONSE: 3053: { 3054: OPTIGA_CMD_LOG_MESSAGE("Processing response for calculate hash command..."); 3055: // check if the write was successful 3056: if (OPTIGA_CMD_APDU_FAILURE == me->p_optiga->optiga_comms_buffer[OPTIGA_COMMS_DATA_OFFSET]) 3057: { 3058: OPTIGA_CMD_LOG_MESSAGE("Error in processing calculate hash response..."); 3059: SET_DEV_ERROR_NOTIFICATION(OPTIGA_CMD_EXIT_HANDLER_CALL); 3060: break; 3061: } 3062: if (OPTIGA_CRYPT_HASH_FINAL == p_optiga_calc_hash->hash_sequence) 3063: { 3064: if (OPTIGA_CRYPT_HASH_DIGEST_OUT != me->p_optiga->optiga_comms_buffer[OPTIGA_CMD_APDU_INDATA_OFFSET]) 3065: { 3066: break; 3067: } 3068: optiga_common_get_uint16(&me->p_optiga->optiga_comms_buffer[OPTIGA_CMD_APDU_INDATA_OFFSET + 3069: OPTIGA_CMD_NO_OF_BYTES_IN_TAG], &out_data_size); 3070: 3071: pal_os_memcpy(p_optiga_calc_hash->p_out_digest, 3072: &me->p_optiga->optiga_comms_buffer[OPTIGA_CMD_APDU_INDATA_OFFSET + OPTIGA_CMD_UINT16_SIZE_IN_BYTES + 3073: OPTIGA_CMD_NO_OF_BYTES_IN_TAG], out_data_size); 3074: }
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.