From ed02e3a5036fd0d127a71f8cd7ef18d0106a9cc4 Mon Sep 17 00:00:00 2001 From: Mathy Vanhoef Date: Mon, 24 May 2021 15:23:58 +0400 Subject: [PATCH 2/3] net80211: mitigation against A-MSDU design flaw Mitigate A-MSDU injection attacks (CVE-2020-24588) by detecting if the destination address of a subframe equals an RFC1042 (i.e., LLC/SNAP) header, and if so dropping the complete A-MSDU frame. This mitigates known attacks, although new (unknown) aggregation-based attacks may remain possible. This defense works because in A-MSDU aggregation injection attacks, a normal encrypted Wi-Fi frame is turned into an A-MSDU frame. This means the first 6 bytes of the first A-MSDU subframe correspond to an RFC1042 header. In other words, the destination MAC address of the first A-MSDU subframe contains the start of an RFC1042 header during an aggregation attack. We can detect this and thereby prevent this specific attack. For details, see Section 7.2 of "Fragment and Forge: Breaking Wi-Fi Through Frame Aggregation and Fragmentation". --- sys/net80211/ieee80211_adhoc.c | 2 +- sys/net80211/ieee80211_hostap.c | 2 +- sys/net80211/ieee80211_input.c | 21 +++++++++++++++++++-- sys/net80211/ieee80211_input.h | 3 ++- sys/net80211/ieee80211_sta.c | 2 +- sys/net80211/ieee80211_wds.c | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c index a23f138802d..e2164bbb46a 100644 --- a/sys/net80211/ieee80211_adhoc.c +++ b/sys/net80211/ieee80211_adhoc.c @@ -558,7 +558,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 15d42a68235..75fa1c0f7b3 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -744,7 +744,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 9e2189c7e43..f405a99410c 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -308,7 +308,8 @@ ieee80211_deliver_data(struct ieee80211vap *vap, } struct mbuf * -ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) +ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, + uint8_t qos) { struct ieee80211_qosframe_addr4 wh; struct ether_header *eh; @@ -330,7 +331,9 @@ ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 && /* NB: preserve AppleTalk frames that have a native SNAP hdr */ !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) || - llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) { + llc->llc_snap.ether_type == htons(ETHERTYPE_IPX)) && + /* don't want to touch A-MSDU frames */ + !(qos & IEEE80211_QOS_AMSDU)) { m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh)); llc = NULL; } else { @@ -378,6 +381,10 @@ ieee80211_decap1(struct mbuf *m, int *framelen) #define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc)) struct ether_header *eh; struct llc *llc; + const uint8_t llc_hdr_mac[ETHER_ADDR_LEN] = { + /* MAC address matching the 802.2 LLC header */ + LLC_SNAP_LSAP, LLC_SNAP_LSAP, LLC_UI, 0, 0, 0 + }; /* * The frame has an 802.3 header followed by an 802.2 @@ -390,6 +397,16 @@ ieee80211_decap1(struct mbuf *m, int *framelen) if (m->m_len < FF_LLC_SIZE && (m = m_pullup(m, FF_LLC_SIZE)) == NULL) return NULL; eh = mtod(m, struct ether_header *); /* 802.3 header is first */ + + /* + * Detect possible attack where a single 802.11 frame is processed + * as an A-MSDU frame due to an adversary setting the A-MSDU present + * bit in the 802.11 QoS header. See Section 7.2 of the paper + * https://papers.mathyvanhoef.com/usenix2021.pdf + */ + if (memcmp(eh->ether_dhost, llc_hdr_mac, 6) == 0) + return NULL; + llc = (struct llc *)&eh[1]; /* 802.2 header follows */ *framelen = ntohs(eh->ether_type) /* encap'd frame size */ + sizeof(struct ether_header) - sizeof(struct llc); diff --git a/sys/net80211/ieee80211_input.h b/sys/net80211/ieee80211_input.h index 8ec82eef736..ae2b4644cc9 100644 --- a/sys/net80211/ieee80211_input.h +++ b/sys/net80211/ieee80211_input.h @@ -311,7 +311,8 @@ void ieee80211_deliver_data(struct ieee80211vap *, struct mbuf *ieee80211_defrag(struct ieee80211_node *, struct mbuf *, int, int); struct mbuf *ieee80211_realign(struct ieee80211vap *, struct mbuf *, size_t); -struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int); +struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int, + uint8_t); struct mbuf *ieee80211_decap1(struct mbuf *, int *); int ieee80211_setup_rates(struct ieee80211_node *ni, const uint8_t *rates, const uint8_t *xrates, int flags); diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 6d24eadc11a..60a5ea10055 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -827,7 +827,7 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c index f59a92b992d..f88871ca4ae 100644 --- a/sys/net80211/ieee80211_wds.c +++ b/sys/net80211/ieee80211_wds.c @@ -621,7 +621,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = ieee80211_decap(vap, m, hdrspace, qos); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ -- 2.31.1