Tuesday, February 23, 2021

A Practical Approach to Attacking IoT Embedded Designs (II)

by Ruben Santamarta

In this second and final blog post on this topic, we cover some OTA vulnerabilities we identified in wireless communication protocols, primarily Zigbee and BLE.




As in the previous post, the findings described herein are intended to illustrate the type of vulnerabilities a malicious actor could leverage to attack a specified target to achieve DoS, information leakage, or arbitrary code execution.

These vulnerabilities affect numerous devices within the IoT ecosystem. IOActive worked with the semiconductor vendors to coordinate the disclosure of these security flaws, but it is worth mentioning that due the specific nature of the IoT market and despite the fact that patches are available, a significant number of vulnerable devices will likely never be patched.

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.

OTA Vulnerabilities

Affected vendors

  • Nordic Semiconductor
  • Texas Instruments
  • Espressif Systems
  • Qualcomm

Nordic Semiconductor  - www.nordicsemi.com

Vulnerability

Integer overflow in ‘ble_advdata_search

Affected Products

nRF5 SDK prior to version 16

Background 

“The nRF5 SDK is your first stop for building fully featured, reliable and secure applications with the nRF52 and nRF51 Series. It offers developers a wealth of varied modules and examples right across the spectrum including numerous Bluetooth Low Energy profiles, Device Firmware Upgrade (DFU), GATT serializer and driver support for all peripherals on all nRF5 Series devices. The nRF5 SDK will almost certainly have something for your needs in developing exciting yet robust wireless products” https://www.nordicsemi.com/Software-and-tools/Software/nRF5-SDK

Impact

A malicious actor able to send specially crafted BLE advertisements could leverage this vulnerability to execute arbitrary code in the context of a device running a nRF5-SDK-based application. This may lead to the total compromise of the affected device.

Technical Details

At line 644, an attacker-controlled buffer pointed to by ‘p_encoded_data[i]’ may be 0x00, which will overflow ‘len’, whose value will be 0xFFFF after the operation.

This effectively bypasses the sanity check at line 645.

File: nRF5SDK160098a08e2/components/ble/common/ble_advdata.c
619: uint16_t ble_advdata_search(uint8_t const * p_encoded_data,
620:                             uint16_t        data_len,
621:                             uint16_t      * p_offset,
622:                             uint8_t         ad_type)
623: {
624:     if ((p_encoded_data == NULL) || (p_offset == NULL))
625:     {
626:         return 0;
627:     }
628: 
629:     uint16_t i = 0;
630: 
631:     while (((i < *p_offset) || (p_encoded_data[i + 1] != ad_type)) && (i < data_len))
632:     {
633:         // Jump to next data.
634:         i += (p_encoded_data[i] + 1);
635:     }
636: 
637:     if (i >= data_len)
638:     {
639:         return 0;
640:     }
641:     else
642:     {
643:         uint16_t offset = i + 2;
644:         uint16_t len    = p_encoded_data[i] - 1;      // FLAW
645:         if ((offset + len) > data_len) 		   // bypass
646:         {
647:             // Malformed. Extends beyond provided data.
648:             return 0;
649:         }
650:         *p_offset = offset;
651:         return len;
652:     }
653: }

Exploitation

Different scenarios are possible depending on how ‘len’ is handled by the caller. In the following example, this vulnerability leads to a classic stack overflow at line 185.

File: nRF5SDK160098a08e2/examples/ble_central/experimental/ble_app_hrs_nfc_c/ble_m.c
153: static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report)
154: {
155:     ret_code_t  err_code;
156:     uint8_t   * p_adv_data;
157:     uint16_t    data_len;
158:     uint16_t    field_len;
159:     uint16_t    dev_name_offset = 0;
160:     char        dev_name[DEV_NAME_LEN];
161: 
162:     // Initialize advertisement report for parsing.
163:     p_adv_data = (uint8_t *)p_adv_report->data.p_data;
164:     data_len   = p_adv_report->data.len;
165: 
166:     // Search for advertising names.
167:     field_len = ble_advdata_search(p_adv_data, 
168:                                    data_len, 
169:                                    &dev_name_offset, 
170:                                    BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME);
171:     if (field_len == 0)
172:     {
173:         // Look for the short local name if it was not found as complete.
174:         field_len = ble_advdata_search(p_adv_data, 
175:                                        data_len,
176:                                        &dev_name_offset,
177:                                        BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME);
178:         if (field_len == 0)
179:         {
180:             // Exit if the data cannot be parsed.
181:             return;
182:         }
183:     }
184: 
185:     memcpy(dev_name, &p_adv_data[dev_name_offset], field_len);

Vulnerability

Incorrect DFU packet length resulting in remote code execution

Affected Products

nRF5 SDK for Mesh prior to version 4.1.0

Background 

“The nRF5 SDK for Mesh combined with the nRF52 Series is the complete solution for your Bluetooth mesh development.” https://www.nordicsemi.com/Software-and-tools/Software/nRF5-SDK-for-Mesh

Impact

A malicious actor able to initiate a DFU connection to the affected device could potentially leverage this vulnerability to execute arbitrary code in the context of the bootloader. This may lead to the total compromise of the affected device.

Technical Details

When the bootloader handles DFU messages, the length of the mesh advertising data packets is not properly checked. The vulnerable code path is as follows:

1. In ‘bootloader_init’ at line 466, the rx callback is initialized to ‘rx_cb’ by ‘transport_init’.

File: nRF5-SDK-for-Mesh-master/mesh/bootloader/src/bootloader.c
439: void bootloader_init(void)
440: {
441:     rtc_init();
442: 
443:     memset(&m_flash_fifo, 0, sizeof(fifo_t));
.
SNIP

461: 
462: #ifdef RBC_MESH_SERIAL
463:     mesh_aci_init();
464: #endif
465: 
466:     transport_init(rx_cb, RBC_MESH_ACCESS_ADDRESS_BLE_ADV);
467: 
468:     bool dfu_bank_flash_start;
469:     dfu_bank_scan(&dfu_bank_flash_start);
 

2. At line 211, the advertising packet queue is checked for DFU packets by calling ‘mesh_packet_adv_data_get’, which does not perform proper validation of the ‘adv_data_length’ field (e.g. by checking for a minimum value [ > 3 ]). As a result at line 217, 'p_adv_data->adv_data_length' (8-bit) may wrap to a large 32-bit value, which is stored at ‘rx_cmd.params.rx.length’.

File: nRF5-SDK-for-Mesh-master/mesh/bootloader/src/bootloader.c
209: static void rx_cb(mesh_packet_t* p_packet)
210: {
211:     mesh_adv_data_t* p_adv_data = mesh_packet_adv_data_get(p_packet);
212:     if (p_adv_data && p_adv_data->handle > RBC_MESH_APP_MAX_HANDLE)
213:     {
214:         bl_cmd_t rx_cmd;
215:         rx_cmd.type = BL_CMD_TYPE_RX;
216:         rx_cmd.params.rx.p_dfu_packet = (dfu_packet_t*) &p_adv_data->handle;
217:         rx_cmd.params.rx.length = p_adv_data->adv_data_length - 3;
218:         bl_cmd_handler(&rx_cmd);
219:     }
220: }


3. A ‘signature’ packet is then routed, without checking the length (truncated to 16-bit at ‘bl_cmd_handler’), through ‘bl_cmd_handler’-> ‘dfu_mesh_rx’ -> ‘handle_data_packet’ and finally ‘target_rx_data’, where the memory corruption may occur at line 861.

File:  nRF5-SDK-for-Mesh-master/mesh/bootloader/src/dfu_mesh.c
827: static uint32_t target_rx_data(dfu_packet_t* p_packet, uint16_t length, bool* p_do_relay)
828: {
829:     uint32_t* p_addr = NULL;
830:     uint32_t error_code = NRF_ERROR_NULL;
831: 
832:     if (m_data_req_segment == p_packet->payload.data.segment)
833:     {
834:         /* Got missing packet, stop requesting. */
835:         m_data_req_segment = DATA_REQ_SEGMENT_NONE;
836:         bl_evt_t tx_abort_evt;
837:         tx_abort_evt.type = BL_EVT_TYPE_TX_ABORT;
838:         tx_abort_evt.params.tx.abort.tx_slot = TX_SLOT_BEACON;
839:         bootloader_evt_send(&tx_abort_evt);
840:     }
841: 
842:     if (p_packet->payload.data.segment <=
843:         m_transaction.segment_count - m_transaction.signature_length / SEGMENT_LENGTH)
844:     {
845:         p_addr = addr_from_seg(p_packet->payload.data.segment, m_transaction.p_start_addr);
846:         error_code = dfu_transfer_data((uint32_t) p_addr,
847:                 p_packet->payload.data.data,
848:                 length - (DFU_PACKET_LEN_DATA - SEGMENT_LENGTH));
849:     }
850:     else /* treat signature packets at the end */
851:     {
852:         uint32_t index = p_packet->payload.data.segment -
853:             (m_transaction.segment_count - m_transaction.signature_length / SEGMENT_LENGTH) - 1;
854:         if (index >= m_transaction.signature_length / SEGMENT_LENGTH ||
855:                 m_transaction.signature_bitmap & (1 << index))
856:         {
857:             error_code = NRF_ERROR_INVALID_STATE;
858:         }
859:         else
860:         {
861:             memcpy(&m_transaction.signature[index * SEGMENT_LENGTH],
862:                     p_packet->payload.data.data,
863:                     length - (DFU_PACKET_LEN_DATA - SEGMENT_LENGTH));
864: 
865:             __LOG("Signature packet #%u\n", index);
866:             m_transaction.signature_bitmap |= (1 << index);
867:             error_code = NRF_SUCCESS;
868:         }

Vulnerability

Multiple buffer overflows when handling Advertising Bearer data packets

Affected Products

nRF5 SDK for Mesh prior to version 4.1.0

Background 

“The nRF5 SDK is your first stop for building fully featured, reliable and secure applications with the nRF52 and nRF51 Series. It offers developers a wealth of varied modules and examples right across the spectrum including numerous Bluetooth Low Energy profiles, Device Firmware Upgrade (DFU), GATT serializer and driver support for all peripherals on all nRF5 Series devices. The nRF5 SDK will almost certainly have something for your needs in developing exciting yet robust wireless products” https://www.nordicsemi.com/Software-and-tools/Software/nRF5-SDK

Impact

A malicious actor able to send malicious Advertising Bearer packets to the affected device could potentially leverage this vulnerability to execute arbitrary code. This may lead to the total compromise of the affected device.

Technical Details

The length of the Advertising Bearer data packets is not properly checked. The vulnerable code path is as follows:

1. When an AD listener is dispatched (it has been previously registered at line 1062 in 'prov_bearer_adv.c'), there is just one action performed to sanitize the length, at line 115 ( > 0 ).

File: nRF5-SDK-for-Mesh-master/mesh/prov/src/prov_bearer_adv.c
1059: AD_LISTENER(m_pb_adv_ad_listener) = {
1060:     .ad_type = AD_TYPE_PB_ADV,
1061:     .adv_packet_type = BLE_PACKET_TYPE_ADV_NONCONN_IND,
1062:     .handler = packet_in,
1063: };


File: nRF5-SDK-for-Mesh-master/mesh/bearer/src/ad_listener.c
108: void ad_listener_process(ble_packet_type_t adv_type, const uint8_t * p_payload, uint32_t payload_length, const nrf_mesh_rx_metadata_t * p_metadata)
109: {
110: #ifdef AD_LISTENER_DEBUG_MODE
111:     uint8_t frame_hash = hash_count(p_payload, payload_length);
112: #endif
113: 
114:     for (ble_ad_data_t * p_ad_data = (ble_ad_data_t *)p_payload;
115:          (uint8_t *)p_ad_data < &p_payload[payload_length] && p_ad_data->length > 0;
116:          p_ad_data = packet_ad_type_get_next((ble_ad_data_t *)p_ad_data))
117:     {
118:         NRF_MESH_SECTION_FOR_EACH(ad_listeners, const ad_listener_t, p_listener)
119:         {
120:             if ((adv_type != p_listener->adv_packet_type && (uint8_t) p_listener->adv_packet_type != ADL_WILDCARD_ADV_TYPE) ||
121:                 (p_listener->ad_type != p_ad_data->type && p_listener->ad_type != ADL_WILDCARD_AD_TYPE))
122:             {
123:                 continue;
124:             }
125: 
126:             p_listener->handler(p_ad_data->data, p_ad_data->length - BLE_AD_DATA_OVERHEAD, p_metadata);
127: 
128: #ifdef AD_LISTENER_DEBUG_MODE
129:             NRF_MESH_ASSERT(hash_count(p_payload, payload_length) == frame_hash);
130: #endif
131:          }
132:      }
133: }


2. The handler for Advertising Bearer packets does not perform any additional validation on the received ‘length’, which is then propagated to specific packet handling functions at lines 1035, 1047, and 1051.

File: nRF5-SDK-for-Mesh-master/mesh/prov/src/prov_bearer_adv.c
1020: 
1021: static void packet_in(const uint8_t * p_data, uint32_t data_len, const nrf_mesh_rx_metadata_t * p_metadata)
1022: {
1023:     NRF_MESH_ASSERT(p_data != NULL);
1024:     NRF_MESH_ASSERT(p_metadata != NULL);
1025: 
1026:     pb_adv_pdu_t * p_packet = (pb_adv_pdu_t *) p_data;
1027: 
1028:     nrf_mesh_prov_bearer_adv_t * p_pb_adv = get_bearer_from_link_id(BE2LE32(p_packet->link_id));
1029: 
1030:     switch (p_packet->pdu.control)
1031:     {
1032:         case PB_ADV_PACKET_C_TRANSACTION_START:
1033:             if (p_pb_adv != NULL && p_pb_adv->state == PROV_BEARER_ADV_STATE_LINK_OPEN)
1034:             {
1035:                 handle_transaction_start_packet(p_pb_adv, p_packet, data_len);
1036:             }
1037:             break;
1038:         case PB_ADV_PACKET_C_TRANSACTION_ACK:
1039:             if (p_pb_adv != NULL && p_pb_adv->state == PROV_BEARER_ADV_STATE_LINK_OPEN)
1040:             {
1041:                 handle_transaction_ack_packet(p_pb_adv, p_packet);
1042:             }
1043:             break;
1044:         case PB_ADV_PACKET_C_TRANSACTION_CONTINUE:
1045:             if (p_pb_adv != NULL && p_pb_adv->state == PROV_BEARER_ADV_STATE_LINK_OPEN)
1046:             {
1047:                 handle_transaction_continuation_packet(p_pb_adv, p_packet, data_len);
1048:             }
1049:             break;
1050:         case PB_ADV_PACKET_C_CONTROL:
1051:             handle_control_packet(p_pb_adv, p_packet, data_len);
1052:             break;
1053:         default:
1054:             /* Ignore */
1055:             break;
1056:     }
1057: }

3. ‘handle_transaction_start_packet’ does not perform any validation on ‘length’ before reaching lines 706 (underflow) and 707 (buffer overflow).

File: nRF5-SDK-for-Mesh-master/mesh/prov/src/prov_bearer_adv.c
684: /**** Packet handling ****/
685: 
686: static void handle_transaction_start_packet(nrf_mesh_prov_bearer_adv_t * p_pb_adv, pb_adv_pdu_t * p_packet, uint32_t length)
687: {
688:     if (p_pb_adv->buffer.state == PROV_BEARER_ADV_BUF_STATE_UNUSED)
689:     {
690:         if (p_packet->transaction_number == p_pb_adv->transaction_in)
691:         {
692:             /* finished_segments, which is used in handle_transaction_continuation_packet
693:                 is 8-bits hence we are limited to receiving a maximum of 7 segments. */
694:             p_pb_adv->buffer.length = BE2LE16(p_packet->pdu.payload.transaction.start.total_length);
695:             uint8_t SegN = transaction_total_segment_count_get(p_pb_adv->buffer.length);
696:             if (SegN > (sizeof(p_pb_adv->buffer.finished_segments)*8) -1)
697:             {
698:                 prov_bearer_adv_link_close(&p_pb_adv->prov_bearer, NRF_MESH_PROV_LINK_CLOSE_REASON_ERROR);
699:             }
700:             else
701:             {
702:                 /* New message */
703:                 p_pb_adv->buffer.fcs = p_packet->pdu.payload.transaction.start.fcs;
704:                 p_pb_adv->buffer.state = PROV_BEARER_ADV_BUF_STATE_RX;
705:                 p_pb_adv->buffer.finished_segments = 1;
706:                 uint32_t payload_length = length - PB_ADV_PACKET_OVERHEAD - PROV_BEARER_PACKET_TRANSACTION_START_OVERHEAD;
707:                 memcpy(p_pb_adv->buffer.payload, p_packet->pdu.payload.transaction.start.payload, payload_length);


4. ‘handle_transaction_continuation_packet’ does not perform any validation on ‘length’ before reaching lines 759 (underflow) and 760 (buffer overflow).

File: nRF5-SDK-for-Mesh-master/mesh/prov/src/prov_bearer_adv.c
748: static void handle_transaction_continuation_packet(nrf_mesh_prov_bearer_adv_t * p_pb_adv, pb_adv_pdu_t * p_packet, uint32_t length)
749: {
750:     if (p_pb_adv->buffer.state == PROV_BEARER_ADV_BUF_STATE_RX)
751:     {
752:         if (p_packet->transaction_number == p_pb_adv->transaction_in)
753:         {
754:             /* Check segment bitfield, to figure out if we've received this packet before: */
755:             if (!((1 << p_packet->pdu.id) & p_pb_adv->buffer.finished_segments))
756:             {
757:                 /* First time we receive this packet. */
758:                 uint32_t data_index = (PROV_BEARER_ADV_PACKET_START_PAYLOAD_MAXLEN + (p_packet->pdu.id - 1) * PROV_BEARER_ADV_PACKET_CONTINUATION_PAYLOAD_MAXLEN);
759:                 uint32_t payload_length = length - PB_ADV_PACKET_OVERHEAD - PROV_BEARER_PACKET_TRANSACTION_CONTINUATION_OVERHEAD;
760:                 memcpy(&p_pb_adv->buffer.payload[data_index], p_packet->pdu.payload.transaction.continuation.payload, payload_length); 


Vulnerability

Buffer overflow in BLE Queued Writes

Affected Products

nRF5 SDK prior to version 16

Background 

“The nRF5 SDK is your first stop for building fully featured, reliable and secure applications with the nRF52 and nRF51 Series. It offers developers a wealth of varied modules and examples right across the spectrum including numerous Bluetooth Low Energy profiles, Device Firmware Upgrade (DFU), GATT serializer and driver support for all peripherals on all nRF5 Series devices. The nRF5 SDK will almost certainly have something for your needs in developing exciting yet robust wireless products” https://www.nordicsemi.com/Software-and-tools/Software/nRF5-SDK

Impact

A malicious actor able to send a initiate a Queued Write request to the affected device could potentially leverage this vulnerability to execute arbitrary code. This may lead to the total compromise of the affected device.

Technical Details

val_offset’ and ‘val_len’ are not properly sanitized. As a result, a malicious request containing a specific combination of both values (containing a large ‘val_len’ value) may lead to an integer overflow  at line 135, resulting in a value that can bypass the check at line 136. Finally, at line 138, the overflow occurs as ‘val_len’ is used in the memcpy operation.

File: nRF5_SDK_16.0.0_98a08e2/components/ble/nrf_ble_qwr/nrf_ble_qwr.c
101: 
102: ret_code_t nrf_ble_qwr_value_get(nrf_ble_qwr_t * p_qwr,
103:                                  uint16_t        attr_handle,
104:                                  uint8_t       * p_mem,
105:                                  uint16_t      * p_len)
106: {
107:     VERIFY_PARAM_NOT_NULL(p_qwr);
108:     VERIFY_PARAM_NOT_NULL(p_mem);
109:     VERIFY_PARAM_NOT_NULL(p_len);
110:     VERIFY_MODULE_INITIALIZED();
111: 
112:     uint16_t i          = 0;
113:     uint16_t handle     = BLE_GATT_HANDLE_INVALID;
114:     uint16_t val_len    = 0;
115:     uint16_t val_offset = 0;
116:     uint16_t cur_len    = 0;
117: 
118:     do
119:     {
120:         handle = uint16_decode(&(p_qwr->mem_buffer.p_mem[i]));
121: 
122:         if (handle == BLE_GATT_HANDLE_INVALID)
123:         {
124:             break;
125:         }
126: 
127:         i         += sizeof(uint16_t);
128:         val_offset = uint16_decode(&(p_qwr->mem_buffer.p_mem[i]));
129:         i         += sizeof(uint16_t);
130:         val_len    = uint16_decode(&(p_qwr->mem_buffer.p_mem[i]));
131:         i         += sizeof(uint16_t);
132: 
133:         if (handle == attr_handle)
134:         {
135:             cur_len = val_offset + val_len;
136:             if (cur_len <= *p_len)
137:             {
138:                 memcpy((p_mem + val_offset), &(p_qwr->mem_buffer.p_mem[i]), val_len);
139:             }
140:             else
141:             {
142:                 return NRF_ERROR_NO_MEM;
143:             }
144:         }
145: 
146:         i += val_len;
147:     }
148:     while (i < p_qwr->mem_buffer.len);
149: 
150:     *p_len = cur_len;
151:     return NRF_SUCCESS;
152: }
153: #endif

Texas Instruments  - www.ti.com

Vulnerability

Z-Stack - Multiple heap overflows in ZCL parsing functions

Affected Products

SIMPLELINK-CC13X2-26X2-SDK prior to version 4.40.00.44
Other Zigbee stacks based on the Z-Stack code are also affected (i.e. Telink)

Background 

“Z-Stack is a component of the SimpleLink™ CC13x2 / CC26x2 Software Development Kit. This component enables development of Zigbee® 3.0 specification based products. Z-Stack is TI’s complete solution for developing certified Zigbee 3.0 solution on CC13x2 and CC26x2 platforms. Z-Stack contained in this release is based on Zigbee 3.0 specification with the added benefit of running on top of TI-RTOS." https://www.ti.com/tool/Z-STACK


Impact

A malicious actor in possession of the NWK key (authenticated to the Zigbee Network) may send OTA malicious Zigbee ZCL packets to the victim’s node, which may result in the execution of arbitrary code in the context of the affected device.

Technical Details

Z-Stack parses the ZCL payloads by performing a two-steps flawed logic:

1. It calculates the total length of the attributes by iterating over the incoming ZCL frame payload without checking for integer overflows.
2. Dynamic memory is allocated according to this total length; however, attributes are individually copied to the parsing structure without sanitizing its length.

In the following example, the first step can be mapped to the ‘while’ loop at lines 3699-3718.

File: simplelink_cc13x2_26x2_sdk_4_20_00_35/source/ti/zstack/stack/zcl/zcl.c
3674: #ifdef ZCL_WRITE
3675: /*********************************************************************
3676:  * @fn      zclParseInWriteCmd
3677:  *
3678:  * @brief   Parse the "Profile" Write, Write Undivided and Write No
3679:  *          Response Commands
3680:  *
3681:  *      NOTE: THIS FUNCTION ALLOCATES THE RETURN BUFFER, SO THE CALLING
3682:  *            FUNCTION IS RESPONSIBLE TO FREE THE MEMORY.
3683:  *
3684:  * @param   pCmd - pointer to incoming data to parse
3685:  *
3686:  * @return  pointer to the parsed command structure
3687:  */
3688: void *zclParseInWriteCmd( zclParseCmd_t *pCmd )
3689: {
3690:   zclWriteCmd_t *writeCmd;
3691:   uint8_t *pBuf = pCmd->pData;
3692:   uint16_t attrDataLen;
3693:   uint8_t *dataPtr;
3694:   uint8_t numAttr = 0;
3695:   uint8_t hdrLen;
3696:   uint16_t dataLen = 0;
3697: 
3698:   // find out the number of attributes and the length of attribute data
3699:   while ( pBuf < ( pCmd->pData + pCmd->dataLen ) )
3700:   {
3701:     uint8_t dataType;
3702: 
3703:     numAttr++;
3704:     pBuf += 2; // move pass attribute id
3705: 
3706:     dataType = *pBuf++;
3707: 
3708:     attrDataLen = zclGetAttrDataLength( dataType, pBuf );
3709:     pBuf += attrDataLen; // move pass attribute data
3710: 
3711:     // add padding if needed
3712:     if ( PADDING_NEEDED( attrDataLen ) )
3713:     {
3714:       attrDataLen++;
3715:     }
3716: 
3717:     dataLen += attrDataLen;
3718:   }
3719: 

dataLen’ is intended to hold the total length of the attributes in the message. Each length is individually calculated in ‘zclGetAttrDataLength’. 

File:  simplelink_cc13x2_26x2_sdk_4_20_00_35/source/ti/zstack/stack/zcl/zcl.c
3258: uint16_t zclGetAttrDataLength( uint8_t dataType, uint8_t *pData )
3259: {
3260:   uint16_t dataLen = 0;
3261: 
3262:   if ( dataType == ZCL_DATATYPE_LONG_CHAR_STR || dataType == ZCL_DATATYPE_LONG_OCTET_STR )
3263:   {
3264:     dataLen = BUILD_UINT16( pData[0], pData[1] ) + 2; // long string length + 2 for length field
3265:   }
3266:   else if ( dataType == ZCL_DATATYPE_CHAR_STR || dataType == ZCL_DATATYPE_OCTET_STR )
3267:   {
3268:     dataLen = *pData + 1; // string length + 1 for length field
3269:   }
3270:   else
3271:   {
3272:     dataLen = zclGetDataTypeLength( dataType );
3273:   }
3274: 
3275:   return ( dataLen );


There is neither an overflow check for ‘dataLen’ nor a bounds check in the last iteration of the loop against ‘pBuf’ before adding the value to ‘dataLen’. According to this logic, an attacker can create a ZCL payload containing a specific combination of attributes that may force the ‘dataLen’ integer to be wrapped, holding a value lower than the actual total length.

Example:

#define MALICIOUS_PAYLOAD "\x00\x01\x43\x06\x00\x41\x41\x41\x41\x41\x41\x00\x02\x43\x10\x00\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00\x03\x43\xF0\xFF"    

Attribute 1: 
type: long octet string: length : 0x6 (+2)
Attribute 2: 
type: long octet string: length:  0x10 (+2)
Attribute 3:
type: long octet string: length: 0xFFF0 (+2)
Total length (truncated to 16-bit as in ‘dataLen’) = 0xC

Back in ‘zclParseInWriteCmd’, ‘dataLen’ is used to allocate the buffer where the attributes’ data will be copied. As there is no sanity check on the consistency of this memory allocation (line 3723 is allocating less memory than expected due to the ‘dataLen’ overflow), this operation may result in a memory corruption at line 3740 (memcpy) as ‘attrDataLen’ may be higher than the buffer allocated at ‘dataPtr’. 

3720:   // calculate the length of the response header
3721:   hdrLen = sizeof( zclWriteCmd_t ) + ( numAttr * sizeof( zclWriteRec_t ) );
3722: 
3723:   writeCmd = (zclWriteCmd_t *)zcl_mem_alloc( hdrLen + dataLen );
3724:   if ( writeCmd != NULL )
3725:   {
3726:     uint8_t i;
3727:     pBuf = pCmd->pData;
3728:     dataPtr = (uint8_t *)( (uint8_t *)writeCmd + hdrLen );
3729: 
3730:     writeCmd->numAttr = numAttr;
3731:     for ( i = 0; i < numAttr; i++ )
3732:     {
3733:       zclWriteRec_t *statusRec = &(writeCmd->attrList[i]);
3734: 
3735:       statusRec->attrID = BUILD_UINT16( pBuf[0], pBuf[1] );
3736:       pBuf += 2;
3737:       statusRec->dataType = *pBuf++;
3738: 
3739:       attrDataLen = zclGetAttrDataLength( statusRec->dataType, pBuf );
3740:       zcl_memcpy( dataPtr, pBuf, attrDataLen);
3741:       statusRec->attrData = dataPtr;
3742: 
3743:       pBuf += attrDataLen; // move pass attribute data
3744: 
3745:       // advance attribute data pointer
3746:       if ( PADDING_NEEDED( attrDataLen ) )
3747:       {
3748:         attrDataLen++;
3749:       }
3750: 
3751:       dataPtr += attrDataLen;
3752:     }
3753:   }
3754: 
3755:   return ( (void *)writeCmd );
3756: }

Most of the parsing routines in ‘zclCmdTable’ are affected.

File:  simplelink_cc13x2_26x2_sdk_4_20_00_35/source/ti/zstack/stack/zcl/zcl.c
270: static CONST zclCmdItems_t zclCmdTable[] 

Additionally, there is an integer overflow in the way ‘zclLL_ProcessInCmd_GetGrpIDsRsp’ (also ‘zclLL_ProcessInCmd_GetEPListRsp’ and ‘zclLL_ProcessInCmd_DeviceInfoRsp’) parses the incoming message, as ‘cnt’ is not properly sanitized before allocating the buffer (line 1214). As a result, ‘rspLen’ wraps around, holding a value which is actually lower than ‘cnt’. Later on ‘cnt’ is used as the test expression in the ‘for’ loop (lines 1227) so it will end up triggering memory corruption at line 1231.

File:  simplelink_cc13x2_26x2_sdk_4_20_00_35/source/ti/zstack/stack/zcl/zcl_ll.c
1205: static ZStatus_t zclLL_ProcessInCmd_GetGrpIDsRsp( zclIncoming_t *pInMsg,
1206:                                                   zclLL_AppCallbacks_t *pCBs )
1207: {
1208:   ZStatus_t status = ZFailure;
1209: 
1210:   if ( pCBs->pfnGetGrpIDsRsp )
1211:   {
1212:     zclLLGetGrpIDsRsp_t *pRsp;
1213:     uint8_t cnt = pInMsg->pData[ZLL_CMDLEN_GET_GRP_IDS_RSP-1];
1214:     uint8_t rspLen = sizeof( zclLLGetGrpIDsRsp_t ) + ( cnt * sizeof( grpInfoRec_t ) );
1215: 
1216:     pRsp = (zclLLGetGrpIDsRsp_t *)zcl_mem_alloc( rspLen );
1217:     if ( pRsp )
1218:     {
1219:       uint8_t *pBuf = pInMsg->pData;
1220:       uint8_t i;
1221: 
1222:       pRsp->total = *pBuf++;
1223:       pRsp->startIndex = *pBuf++;
1224:       pRsp->cnt = *pBuf++;
1225:       pRsp->grpInfoRec = (grpInfoRec_t *)(pRsp+1);
1226: 
1227:       for ( i = 0; i < cnt; i++ )
1228:       {
1229:         grpInfoRec_t *pRec = &(pRsp->grpInfoRec[i]);
1230: 
1231:         pRec->grpID = BUILD_UINT16( pBuf[0], pBuf[1] );
1232:         pBuf += 2;
1233: 
1234:         pRec->grpType = *pBuf++;
1235:       }
1236: 
1237:       status = pCBs->pfnGetGrpIDsRsp( &(pInMsg->msg->srcAddr), pRsp );
1238: 
1239:       zcl_mem_free( pRsp );
1240:     }
1241:   }
1242: 
1243:   return ( status );
1244: }

Vulnerability

EasyLink – memory corruption in ‘rxDoneCallback

Affected Products

SIMPLELINK-CC13X2-26X2-SDK prior to version 4.40.00.44

Background 

“The EasyLink API should be used in application code. The EasyLink API is intended to abstract the RF Driver in order to give a simple API for customers to use as is or extend to suit their application use cases." http://software-dl.ti.com/simplelink/esd/simplelink_cc13x0_sdk/4.10.01.01/exports/docs/proprietary-rf/proprietary-rf-users-guide/easylink/easylink-api-reference.html


Impact

A remote attacker may send a specially crafted OTA EasyLink packet to the victim’s device, which may result in either a DoS condition or the execution of arbitrary code.

Technical Details

EasyLink does not properly validate the length of the received packet. At line 533, the attacker-controlled buffer ('pDataEntry->data') is used to extract 1 byte that is then used to calculate the number of bytes that will be copied (at line 545) to the static buffer pointed to by 'rxBuffer.payload' (fixed at 128 bytes).

File: simplelink_cc13x2_26x2_sdk_4_20_00_35/source/ti/easylink/EasyLink.c
503: //Callback for Async Rx complete
504: static void rxDoneCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
505: {
506:     EasyLink_Status status = EasyLink_Status_Rx_Error;
507:     //create rxPacket as a static so that the large payload buffer it is not
508:     //allocated from the stack
509:     static EasyLink_RxPacket rxPacket;
510:     rfc_dataEntryGeneral_t *pDataEntry;
511:     pDataEntry = (rfc_dataEntryGeneral_t*) rxBuffer;
512: 
513:     if (e & RF_EventLastCmdDone)
514:     {
515:         //Release now so user callback can call EasyLink API's
516:         Semaphore_post(busyMutex);
517:         asyncCmdHndl = EASYLINK_RF_CMD_HANDLE_INVALID;
518: 
519:         //Check command status
520:         if (EasyLink_cmdPropRxAdv.status == PROP_DONE_OK)
521:         {
522:             //Check that data entry status indicates it is finished with
523:             if (pDataEntry->status != DATA_ENTRY_FINISHED)
524:             {
525:                 status = EasyLink_Status_Rx_Error;
526:             }
527:             else if ( (rxStatistics.nRxOk == 1) ||
528:                     //or filer disabled and ignore due to addr mistmatch
529:                     ((EasyLink_cmdPropRxAdv.pktConf.filterOp == 1) &&
530:                      (rxStatistics.nRxIgnored == 1)) )
531:             {
532:                 //copy length from pDataEntry
533:                 rxPacket.len = *(uint8_t*)(&pDataEntry->data) - addrSize;
534:                 if(useIeeeHeader)
535:                 {
536:                     hdrSize = EASYLINK_HDR_SIZE_NBYTES(EASYLINK_IEEE_HDR_NBITS);
537:                 }
538:                 else
539:                 {
540:                     hdrSize = EASYLINK_HDR_SIZE_NBYTES(EASYLINK_PROP_HDR_NBITS);
541:                 }
542:                 //copy address from packet payload (as it is not in hdr)
543:                 memcpy(&rxPacket.dstAddr, (&pDataEntry->data + hdrSize), addrSize);
544:                 //copy payload
545:                 memcpy(&rxPacket.payload, (&pDataEntry->data + hdrSize + addrSize), rxPacket.len);
546:                 rxPacket.rssi = rxStatistics.lastRssi;
547:                 rxPacket.absTime = rxStatistics.timeStamp;


Espressif Systems  - www.espressif.com

Vulnerability

Protocomm ‘transport_simple_ble_read’ information leak

Affected Products

ESP-IDF prior to v4.0.2 https://github.com/espressif/esp-idf

Background 

“Espressif provides basic hardware and software resources to help application developers realize their ideas using the ESP32 series hardware. The software development framework by Espressif is intended for development of Internet-of-Things (IoT) applications with Wi-Fi, Bluetooth, power management and several other system features.”  https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/

This bug was awarded a $2,229 bounty as part of the ESP32 bug bounty program (https://www.espressif.com/en/news/bug-bounty), which was donated by Espressif, on my behalf, to a Spanish animal rescue organization.

Impact

A remote attacker may send a specially crafted BLE packet to the victim’s device, which may result in either a DoS condition or an information leak.

Technical Details

When handling a BLE READ request from the client, ‘offset’ is not properly sanitized before copying data to the response (line 128). As a result, a malicious client may leak sensitive information from the device by setting an overly large ‘offset’ parameter in the READ request.

File: esp-idf-v4.0.1/components/protocomm/src/transports/protocomm_ble.c
107: static void transport_simple_ble_read(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
108: {
109:     static const uint8_t *read_buf = NULL;
110:     static uint16_t read_len = 0;
111:     esp_gatt_status_t status = ESP_OK;
112: 
113:     ESP_LOGD(TAG, "Inside read w/ session - %d on param %d %d",
114:              param->read.conn_id, param->read.handle, read_len);
115:     if (!read_len && !param->read.offset) {
116:         ESP_LOGD(TAG, "Reading attr value first time");
117:         status = esp_ble_gatts_get_attr_value(param->read.handle, &read_len,  &read_buf);
118:     } else {
119:         ESP_LOGD(TAG, "Subsequent read request for attr value");
120:     }
121: 
122:     esp_gatt_rsp_t gatt_rsp = {0};
123:     gatt_rsp.attr_value.len = MIN(read_len, (protoble_internal->gatt_mtu - 1));
124:     gatt_rsp.attr_value.handle = param->read.handle;
125:     gatt_rsp.attr_value.offset = param->read.offset;
126:     gatt_rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
127:     if (gatt_rsp.attr_value.len && read_buf) {
128:         memcpy(gatt_rsp.attr_value.value,
129:                 read_buf + param->read.offset,
130:                 gatt_rsp.attr_value.len);
131:     }
132:     read_len -= gatt_rsp.attr_value.len;
133:     esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->read.conn_id,
134:                                                 param->read.trans_id, status, &gatt_rsp);
135:     if (err != ESP_OK) {
136:         ESP_LOGE(TAG, "Send response error in read");
137:     }
138: }

Qualcomm - www.qualcomm.com

Vulnerability

Api_ParseInfoElem’ improper handling of IEEE80211_ELEMID_RSN length may lead to a remote DoS

Affected Products

Qualcomm WIFI_QCA Middleware 

Background 

“The QCA4004 is an intelligent platform for the Internet of Things that contains a low-power Wi-Fi connectivity solution on a single chip. It includes a number of TCP/IP-based connectivity protocols along with SSL, allowing a low-cost, low-complexity system to obtain full-featured internet connectivity and reliable information exchange.”  https://www.qualcomm.com/products/qca4004 

Impact

A malicious actor able to send malicious 802.11 management frames to the affected device may potentially leverage this vulnerability to perform a DoS, as unmapped or invalid memory may be hit when parsing RSN IEs after a SCAN operation has been invoked.  

Technical Details

The vulnerable code path is as follows: 

1. When parsing the RSN IE, its length (‘ie_len’) is not properly sanitized against ‘len’ before calling ‘security_ie_parse’.

File: middleware/wifi_qca/common_src/api_interface/api_ioctl.c
695: A_STATUS
696: Api_ParseInfoElem(void *pCxt, WMI_BSS_INFO_HDR *bih, int32_t len, A_SCAN_SUMMARY *pSummary)
697: {
698:     uint8_t *buf;
699:     uint8_t *pie, *pieEnd, *pieTemp;
700:     uint8_t ie_result[2];
701:     uint16_t ie_len;

 SNIP

740:             case IEEE80211_ELEMID_RSN:
741:                 /*******************/
742:                 /* parse RSN IE    */
743:                 /*******************/
744:                 ie_len = pie[1];   /* init ie_len - sizeof wpa_oui */ 
745:                 pieTemp = &pie[2]; /* init pieTemp beyond wpa_oui */
746: 
747:                 if (A_LE_READ_2(pieTemp) != RSN_VERSION)
748:                 {
749:                     break;
750:                 }
751:                 ie_len -= 2;
752:                 pieTemp += 2;
753:                 ie_result[0] = ie_result[1] = 0;
754: 
755:                 security_ie_parse(pieTemp, ie_len, &ie_result[0], IEEE80211_ELEMID_RSN);

security_ie_parse’ does not perform any additional validation on the received ‘ie_len’. Then ‘ie_len’ is decremented at 637, 644, and 658 without performing any check for alignment or underflow, so a specific value in ‘ie_len’ makes the second condition in the ‘for’ loop (line 647) always true. 
As a result, this ‘for’ loop only depends on the first condition (where ‘cnt’ is also controlled by the potential attacker). This situation may force the ‘for’ loop to run beyond the original buffer’s bound, potentially hitting unmapped or invalid memory.

File: middleware/wifi_qca/common_src/api_interface/api_ioctl.c
629: static void security_ie_parse(uint8_t *pie, uint8_t ie_len, uint8_t *pResult, uint8_t ie_type)
630: {
631:     uint16_t cnt;
632:     uint16_t i;
633:     uint8_t wepKeyLen;
634:     /* skip mcast cipher */
635:     if (ie_len >= 4)
636:     {
637:         ie_len -= 4;
638:         pie += 4;
639:     }
640:     /* examine ucast cipher(s) */
641:     if (ie_len > 2)
642:     {
643:         cnt = A_LE_READ_2(pie);
644:         ie_len -= 2;
645:         pie += 2;
646: 
647:         for (i = 0; ((i < cnt) && (ie_len > 0)); i++)
648:         {
649:             if (ie_type == IEEE80211_ELEMID_RSN)
650:             {
651:                 pResult[0] |= rsn_cipher_parse(pie, &wepKeyLen);
652:             }
653:             else
654:             {
655:                 pResult[0] |= wpa_cipher_parse(pie, &wepKeyLen);
656:             }
657: 
658:             ie_len -= 4;
659:             pie += 4;
660:         }
661:     }

 

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.