Skip to content

Type Confusion in 802154 ACK Frames Handling

Moderate
ceolin published GHSA-27r3-rxch-2hm7 May 17, 2021

Package

zephyr (west)

Affected versions

v2.4.0

Patched versions

v2.5.0

Description

2. Type Confusion in 802154 ACK Frames Handling

  • Bug Description: Mismatch between validation and handling of 802154 ACK frames, where ACK frames are considered during validation, but not during actual processing, leading to a type confusion.
  • Bug Result: Type confusion occurs in the face of 802154 ACK frames, where an ACK frame is wrongly handled as a DATA frame. In the current implementation of DATA frame handling, this results in erroneous size calculations, which eventually result in a NULL pointer being treated as packet data.
  • Impact: DoS (crash) of firmware if a malicious frame is sent. Niche situations might exist where address 0 is mapped and possibly attacker controlled. Then, attacker might achieve more powerful primitive (such as RCE).

Source Code Snippets

static enum net_verdict ieee802154_recv(struct net_if *iface,
					struct net_pkt *pkt)
{
	struct ieee802154_mpdu mpdu;
	size_t hdr_len;

    // TS: Frame validation called here
	if (!ieee802154_validate_frame(net_pkt_data(pkt),
				       net_pkt_get_len(pkt), &mpdu)) {
		return NET_DROP;
	}

	if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) {
		return ieee802154_handle_beacon(iface, &mpdu,
						net_pkt_ieee802154_lqi(pkt));
	}

	if (ieee802154_is_scanning(iface)) {
		return NET_DROP;
	}

	if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) {
		return ieee802154_handle_mac_command(iface, &mpdu);
	}
    // BUG: No case handling mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_ACK
    // BUG: Also, no fallback "return NET_DROP;" here

    // BUG: At this point, IEEE802154_FRAME_TYPE_ACK frame is handled as data frame
	/* At this point the frame has to be a DATA one */

    ...

    // BUG Result: mpdu.payload == 0 used here
    // -> hdr_len = -net_pkt_data(pkt)
    hdr_len = (uint8_t *)mpdu.payload - net_pkt_data(pkt);
    // -> this sets pkt->buffer = pkt->buffer + (- net_pkt_data(pkt)) = 0
	net_buf_pull(pkt->buffer, hdr_len);
    // -> pkt->buffer is set to NULL now

    // BUG Result: Using pkt->buffer->data later during processing crashes
	return ieee802154_manage_recv_packet(iface, pkt, hdr_len);
}
bool ieee802154_validate_frame(uint8_t *buf, uint8_t length,
			       struct ieee802154_mpdu *mpdu)
{
    ...
    return validate_payload_and_mfr(mpdu, buf, p_buf, &length);
}
static inline bool
validate_payload_and_mfr(struct ieee802154_mpdu *mpdu,
			 uint8_t *buf, uint8_t *p_buf, uint8_t *length)
{
    ...
    // BUG Description: Validation recognizes ACK frame type, which processing does not
    } else if (type == IEEE802154_FRAME_TYPE_ACK) {
    /** An ACK frame has no payload */
    if (*length) {
        return false;
    }

    // BUG Result: payload pointer set to NULL here
    mpdu->payload = NULL;
    }
    ...
    return true;
}

Propsed Fix

Add explicit check for DATA frame type, return NET_DROP in the default case, and initialize address struct explicitly for the IEEE802154_ADDR_MODE_NONE case.

/* At this point the frame has to be a DATA one */

diff --git a/subsys/net/l2/ieee802154/ieee802154.c b/subsys/net/l2/ieee802154/ieee802154.c
index da03e31b32..9d4908ed37 100644
--- a/subsys/net/l2/ieee802154/ieee802154.c
+++ b/subsys/net/l2/ieee802154/ieee802154.c
@@ -98,6 +98,8 @@ static inline void set_pkt_ll_addr(struct net_linkaddr *addr, bool comp,
 				   struct ieee802154_address_field *ll)
 {
 	if (mode == IEEE802154_ADDR_MODE_NONE) {
+		addr->len = 0;
+		addr->addr = NULL;
 		return;
 	}
 
@@ -202,27 +204,30 @@ static enum net_verdict ieee802154_recv(struct net_if *iface,
 		return ieee802154_handle_mac_command(iface, &mpdu);
 	}
 
-	/* At this point the frame has to be a DATA one */
-
-	ieee802154_acknowledge(iface, &mpdu);
+	// Proposed fix: Make the correct data type explicit
+	if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_DATA) {
+		ieee802154_acknowledge(iface, &mpdu);
 
-	set_pkt_ll_addr(net_pkt_lladdr_src(pkt), mpdu.mhr.fs->fc.pan_id_comp,
-			mpdu.mhr.fs->fc.src_addr_mode, mpdu.mhr.src_addr);
+		set_pkt_ll_addr(net_pkt_lladdr_src(pkt), mpdu.mhr.fs->fc.pan_id_comp,
+				mpdu.mhr.fs->fc.src_addr_mode, mpdu.mhr.src_addr);
 
-	set_pkt_ll_addr(net_pkt_lladdr_dst(pkt), false,
-			mpdu.mhr.fs->fc.dst_addr_mode, mpdu.mhr.dst_addr);
+		set_pkt_ll_addr(net_pkt_lladdr_dst(pkt), false,
+				mpdu.mhr.fs->fc.dst_addr_mode, mpdu.mhr.dst_addr);
 
-	if (!ieee802154_decipher_data_frame(iface, pkt, &mpdu)) {
-		return NET_DROP;
-	}
+		if (!ieee802154_decipher_data_frame(iface, pkt, &mpdu)) {
+			return NET_DROP;
+		}
 
-	pkt_hexdump(RX_PKT_TITLE " (with ll)", pkt, true);
+		pkt_hexdump(RX_PKT_TITLE " (with ll)", pkt, true);
 
-	hdr_len = (uint8_t *)mpdu.payload - net_pkt_data(pkt);
-	net_buf_pull(pkt->buffer, hdr_len);
+		hdr_len = (uint8_t *)mpdu.payload - net_pkt_data(pkt);
+		net_buf_pull(pkt->buffer, hdr_len);
 
-	return ieee802154_manage_recv_packet(iface, pkt, hdr_len);
+		return ieee802154_manage_recv_packet(iface, pkt, hdr_len);
+	}
 
+	// Unsupported frame type
+	return NET_DROP;
 }
 
 static int ieee802154_send(struct net_if *iface, struct net_pkt *pkt)

Patches

This has been fixed in:

  • master #31908
  • v2.5: TBD
  • v2.4: TBD
  • v1.14: TBD

For more information

If you have any questions or comments about this advisory:

embargo: 2021-04-14
zepsec: ZEPSEC-113

Severity

Moderate
5.9
/ 10

CVSS base metrics

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

CVE ID

CVE-2021-3320

Weaknesses