Summary
A malicious actor can send a IEEE 802.15.4 packet with spoofed length byte and optionally spoofed FCS, which eventually results into an endless loop on a CC2538 as receiver.
Details
Before #20998, the receiver would check for the location of the CRC bit using the packet length byte by considering all 8 bits, instead of discarding bit 7, which is what the radio does. This then results into reading outside of the RX FIFO here:
|
if (rfcore_peek_rx_fifo(pkt_len) & CC2538_CRC_BIT_MASK) { |
Although it prints an error when attempting to read outside of the RX FIFO, it will continue doing this. This may lead to a discrepancy in the CRC check according to the firmware and the radio. If the CPU judges the CRC as correct and the radio is set to
AUTO_ACK
, when the packet requests and acknowledgment the CPU will go into the state
CC2538_STATE_TX_ACK
here:
|
if (IS_ACTIVE(CONFIG_IEEE802154_AUTO_ACK_DISABLE) || |
|
(!(rfcore_peek_rx_fifo(1) & IEEE802154_FCF_ACK_REQ))) { |
However, if the radio judged the CRC as incorrect, it will not send an acknowledgment, and thus the TXACKDONE
event will not fire. It will then never return to the state CC2538_STATE_READY
since the baseband processing is still disabled:
Then the CPU will be in an endless loop here:
|
while (ieee802154_radio_set_idle(&submac->dev, false) < 0) {} |
Since setting to idle is not forced, it won't do it if the radio's state is not
CC2538_STATE_READY
:
|
if (!force && cc2538_state != CC2538_STATE_READY) { |
PoC
The endless loop may occur even during normal operation when bit 7 of the length byte is flipped over the air. An attacker can induce it by spoofing the length byte to a value of more than 127. Since Commercial Off-The-Shelf IEEE 802.15.4 devices will likely not be capable of sending a length byte with a value higher than 127, it needs to be done with a Software-Defined Radio.
To emulate the behaviour with COTS devices, I modified the firmware of a receiving CC2538 to process a packet as if a length byte of more than 127 was received:
bool realCRC = rfcore_peek_rx_fifo(pkt_len) & CC2538_CRC_BIT_MASK;
pkt_len += 0x80; // Fake the spoofed length bit
bool fakeCRC = rfcore_peek_rx_fifo(pkt_len) & CC2538_CRC_BIT_MASK;
if (!realCRC && fakeCRC) {
DEBUG("realCRC: %i, fakeCRC: %i\n", realCRC, fakeCRC);
}
if (fakeCRC) {
By sending several IEEE 802.15.4 packets with the ACK request bit set (which is the default) and with low transmit power to induce a CRC error, eventually realCRC
would be 0 and fakeCRC
1, resulting in an endless loop at the receiver. The receiver would need to be set to AUTO_ACK
, which is the default for plain 802.15.4. Using an SDR, the FCS can be spoofed as well, such that the receiver will always run into the endless loop as soon as the bit in the memory location where the fake CRC check is performed is 1.
Impact
Via e-mail, Marian Buschsieweke initially pointed out that this bug might be a DoS attack vector. The device will not be able to recover from this, except for a reboot.
Summary
A malicious actor can send a IEEE 802.15.4 packet with spoofed length byte and optionally spoofed FCS, which eventually results into an endless loop on a CC2538 as receiver.
Details
Before #20998, the receiver would check for the location of the CRC bit using the packet length byte by considering all 8 bits, instead of discarding bit 7, which is what the radio does. This then results into reading outside of the RX FIFO here:
RIOT/cpu/cc2538/radio/cc2538_rf_radio_ops.c
Line 417 in 1a418cc
Although it prints an error when attempting to read outside of the RX FIFO, it will continue doing this. This may lead to a discrepancy in the CRC check according to the firmware and the radio. If the CPU judges the CRC as correct and the radio is set to
AUTO_ACK
, when the packet requests and acknowledgment the CPU will go into the stateCC2538_STATE_TX_ACK
here:RIOT/cpu/cc2538/radio/cc2538_rf_radio_ops.c
Lines 421 to 422 in 1a418cc
However, if the radio judged the CRC as incorrect, it will not send an acknowledgment, and thus the
TXACKDONE
event will not fire. It will then never return to the stateCC2538_STATE_READY
since the baseband processing is still disabled:RIOT/cpu/cc2538/radio/cc2538_rf_radio_ops.c
Line 419 in 1a418cc
Then the CPU will be in an endless loop here:
RIOT/sys/net/link_layer/ieee802154/submac.c
Line 149 in 1a418cc
Since setting to idle is not forced, it won't do it if the radio's state is not
CC2538_STATE_READY
:RIOT/cpu/cc2538/radio/cc2538_rf_radio_ops.c
Line 183 in 1a418cc
PoC
The endless loop may occur even during normal operation when bit 7 of the length byte is flipped over the air. An attacker can induce it by spoofing the length byte to a value of more than 127. Since Commercial Off-The-Shelf IEEE 802.15.4 devices will likely not be capable of sending a length byte with a value higher than 127, it needs to be done with a Software-Defined Radio.
To emulate the behaviour with COTS devices, I modified the firmware of a receiving CC2538 to process a packet as if a length byte of more than 127 was received:
By sending several IEEE 802.15.4 packets with the ACK request bit set (which is the default) and with low transmit power to induce a CRC error, eventually
realCRC
would be 0 andfakeCRC
1, resulting in an endless loop at the receiver. The receiver would need to be set toAUTO_ACK
, which is the default for plain 802.15.4. Using an SDR, the FCS can be spoofed as well, such that the receiver will always run into the endless loop as soon as the bit in the memory location where the fake CRC check is performed is 1.Impact
Via e-mail, Marian Buschsieweke initially pointed out that this bug might be a DoS attack vector. The device will not be able to recover from this, except for a reboot.