wpa_supplicant: fix CVE-2018-14526
authorRISCi_ATOM <bob@bobcall.me>
Mon, 20 Aug 2018 16:32:33 +0000 (12:32 -0400)
committerRISCi_ATOM <bob@bobcall.me>
Mon, 20 Aug 2018 16:32:33 +0000 (12:32 -0400)
Unauthenticated EAPOL-Key decryption in wpa_supplicant

Published: August 8, 2018
Identifiers:
- CVE-2018-14526
Latest version available from: https://w1.fi/security/2018-1/

Vulnerability

A vulnerability was found in how wpa_supplicant processes EAPOL-Key
frames. It is possible for an attacker to modify the frame in a way that
makes wpa_supplicant decrypt the Key Data field without requiring a
valid MIC value in the frame, i.e., without the frame being
authenticated. This has a potential issue in the case where WPA2/RSN
style of EAPOL-Key construction is used with TKIP negotiated as the
pairwise cipher. It should be noted that WPA2 is not supposed to be used
with TKIP as the pairwise cipher. Instead, CCMP is expected to be used
and with that pairwise cipher, this vulnerability is not applicable in
practice.

When TKIP is negotiated as the pairwise cipher, the EAPOL-Key Key Data
field is encrypted using RC4. This vulnerability allows unauthenticated
EAPOL-Key frames to be processed and due to the RC4 design, this makes
it possible for an attacker to modify the plaintext version of the Key
Data field with bitwise XOR operations without knowing the contents.
This can be used to cause a denial of service attack by modifying
GTK/IGTK on the station (without the attacker learning any of the keys)
which would prevent the station from accepting received group-addressed
frames. Furthermore, this might be abused by making wpa_supplicant act
as a decryption oracle to try to recover some of the Key Data payload
(GTK/IGTK) to get knowledge of the group encryption keys.

Full recovery of the group encryption keys requires multiple attempts
(128 connection attempts per octet) and each attempt results in
disconnection due to a failure to complete the 4-way handshake. These
failures can result in the AP/network getting disabled temporarily or
even permanently (requiring user action to re-enable) which may make it
impractical to perform the attack to recover the keys before the AP has
already changes the group keys. By default, wpa_supplicant is enforcing
at minimum a ten second wait time between each failed connection
attempt, i.e., over 20 minutes waiting to recover each octet while
hostapd AP implementation uses 10 minute default for GTK rekeying when
using TKIP. With such timing behavior, practical attack would need large
number of impacted stations to be trying to connect to the same AP to be
able to recover sufficient information from the GTK to be able to
determine the key before it gets changed.

Vulnerable versions/configurations

All wpa_supplicant versions.

Acknowledgments

Thanks to Mathy Vanhoef of the imec-DistriNet research group of KU
Leuven for discovering and reporting this issue.

Possible mitigation steps

- Remove TKIP as an allowed pairwise cipher in RSN/WPA2 networks. This
can be done also on the AP side.

- Merge the following commits to wpa_supplicant and rebuild:

WPA: Ignore unauthenticated encrypted EAPOL-Key data

This patch is available from https://w1.fi/security/2018-1/

- Update to wpa_supplicant v2.7 or newer, once available

Pulled from upstream commit : b3983323a1f25c936ddfcc129c454b282e90eeed

package/network/services/hostapd/files/hostapd.sh
package/network/services/hostapd/patches/0001-WPA-Ignore-unauthenticated-encrypted-EAPOL-Key-data.patch [new file with mode: 0644]
package/network/services/hostapd/patches/340-reload_freq_change.patch
package/network/services/hostapd/patches/463-add-mcast_rate-to-11s.patch [new file with mode: 0644]
package/network/services/hostapd/patches/600-ubus_support.patch

index e3fc0366d5f8e5a06792cbf810506f386dafdc13..1f286611175fa9fdfd4f7317f328d2ca940371ce 100644 (file)
@@ -154,6 +154,7 @@ hostapd_common_add_bss_config() {
        config_add_string acct_server
        config_add_string acct_secret
        config_add_int acct_port
+       config_add_int acct_interval
 
        config_add_string dae_client
        config_add_string dae_secret
@@ -211,8 +212,8 @@ hostapd_set_bss_options() {
                wps_pushbutton wps_label ext_registrar wps_pbc_in_m1 wps_ap_setup_locked \
                wps_independent wps_device_type wps_device_name wps_manufacturer wps_pin \
                macfilter ssid wmm uapsd hidden short_preamble rsn_preauth \
-               iapp_interface eapol_version acct_server acct_secret acct_port \
-               dynamic_vlan ieee80211w
+               iapp_interface eapol_version dynamic_vlan ieee80211w nasid \
+               acct_server acct_secret acct_port acct_interval
 
        set_default isolate 0
        set_default maxassoc 0
@@ -252,11 +253,14 @@ hostapd_set_bss_options() {
                [ -n "$wpa_master_rekey" ] && append bss_conf "wpa_gmk_rekey=$wpa_master_rekey"  "$N"
        }
 
+       [ -n "$nasid" ] && append bss_conf "nas_identifier=$nasid" "$N"
        [ -n "$acct_server" ] && {
                append bss_conf "acct_server_addr=$acct_server" "$N"
                append bss_conf "acct_server_port=$acct_port" "$N"
                [ -n "$acct_secret" ] && \
                        append bss_conf "acct_server_shared_secret=$acct_secret" "$N"
+               [ -n "$acct_interval" ] && \
+                       append bss_conf "radius_acct_interim_interval=$acct_interval" "$N"
        }
 
        local vlan_possible=""
@@ -377,9 +381,8 @@ hostapd_set_bss_options() {
        }
 
        if [ "$wpa" -ge "1" ]; then
-               json_get_vars nasid ieee80211r
+               json_get_vars ieee80211r
                set_default ieee80211r 0
-               [ -n "$nasid" ] && append bss_conf "nas_identifier=$nasid" "$N"
 
                if [ "$ieee80211r" -gt "0" ]; then
                        json_get_vars mobility_domain r0_key_lifetime r1_key_holder \
@@ -720,6 +723,7 @@ wpa_supplicant_add_network() {
        }
        local beacon_int brates mrate
        [ -n "$bssid" ] && append network_data "bssid=$bssid" "$N$T"
+       [ -n "$beacon_int" ] && append network_data "beacon_int=$beacon_int" "$N$T"
 
        local bssid_blacklist bssid_whitelist
        json_get_values bssid_blacklist bssid_blacklist
@@ -742,10 +746,6 @@ wpa_supplicant_add_network() {
                append network_data "mcast_rate=$mc_rate" "$N$T"
        }
 
-       local ht_str
-       [[ "$_w_mode" = adhoc ]] || ibss_htmode=
-       [ -n "$ibss_htmode" ] && append network_data "htmode=$ibss_htmode" "$N$T"
-
        cat >> "$_config" <<EOF
 network={
        $scan_ssid
diff --git a/package/network/services/hostapd/patches/0001-WPA-Ignore-unauthenticated-encrypted-EAPOL-Key-data.patch b/package/network/services/hostapd/patches/0001-WPA-Ignore-unauthenticated-encrypted-EAPOL-Key-data.patch
new file mode 100644 (file)
index 0000000..b4ccf47
--- /dev/null
@@ -0,0 +1,43 @@
+From 3e34cfdff6b192fe337c6fb3f487f73e96582961 Mon Sep 17 00:00:00 2001
+From: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
+Date: Sun, 15 Jul 2018 01:25:53 +0200
+Subject: [PATCH] WPA: Ignore unauthenticated encrypted EAPOL-Key data
+
+Ignore unauthenticated encrypted EAPOL-Key data in supplicant
+processing. When using WPA2, these are frames that have the Encrypted
+flag set, but not the MIC flag.
+
+When using WPA2, EAPOL-Key frames that had the Encrypted flag set but
+not the MIC flag, had their data field decrypted without first verifying
+the MIC. In case the data field was encrypted using RC4 (i.e., when
+negotiating TKIP as the pairwise cipher), this meant that
+unauthenticated but decrypted data would then be processed. An adversary
+could abuse this as a decryption oracle to recover sensitive information
+in the data field of EAPOL-Key messages (e.g., the group key).
+(CVE-2018-14526)
+
+Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
+---
+ src/rsn_supp/wpa.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/src/rsn_supp/wpa.c
++++ b/src/rsn_supp/wpa.c
+@@ -2157,6 +2157,17 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, c
+       if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
+           (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
++              /*
++               * Only decrypt the Key Data field if the frame's authenticity
++               * was verified. When using AES-SIV (FILS), the MIC flag is not
++               * set, so this check should only be performed if mic_len != 0
++               * which is the case in this code branch.
++               */
++              if (!(key_info & WPA_KEY_INFO_MIC)) {
++                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
++                              "WPA: Ignore EAPOL-Key with encrypted but unauthenticated data");
++                      goto out;
++              }
+               if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
+                                                   ver, key_data,
+                                                   &key_data_len))
index 086ade9cedec664ee566764f8f09bd2fd730ba53..0dcd7c913e829738648b2513fc027ffd60a494cd 100644 (file)
@@ -1,6 +1,6 @@
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -80,6 +80,16 @@ static void hostapd_reload_bss(struct ho
+@@ -80,6 +80,25 @@ static void hostapd_reload_bss(struct ho
  #endif /* CONFIG_NO_RADIUS */
  
        ssid = &hapd->conf->ssid;
 +                       hapd->iconf->vht_oper_chwidth,
 +                       hapd->iconf->vht_oper_centr_freq_seg0_idx,
 +                       hapd->iconf->vht_oper_centr_freq_seg1_idx);
++
++      if (hapd->iface->current_mode) {
++              if (hostapd_prepare_rates(hapd->iface, hapd->iface->current_mode)) {
++                      wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
++                      hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
++                                     HOSTAPD_LEVEL_WARNING,
++                                     "Failed to prepare rates table.");
++              }
++      }
 +
        if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
            ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
                /*
-@@ -179,21 +189,12 @@ int hostapd_reload_config(struct hostapd
+@@ -158,6 +177,7 @@ int hostapd_reload_config(struct hostapd
+       struct hostapd_data *hapd = iface->bss[0];
+       struct hostapd_config *newconf, *oldconf;
+       size_t j;
++      int i;
+       if (iface->config_fname == NULL) {
+               /* Only in-memory config in use - assume it has been updated */
+@@ -179,21 +199,20 @@ int hostapd_reload_config(struct hostapd
        oldconf = hapd->iconf;
        iface->conf = newconf;
  
++      for (i = 0; i < iface->num_hw_features; i++) {
++              struct hostapd_hw_modes *mode = &iface->hw_features[i];
++              if (mode->mode == iface->conf->hw_mode) {
++                      iface->current_mode = mode;
++                      break;
++              }
++      }
++
 +      if (iface->conf->channel)
 +              iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
 +
diff --git a/package/network/services/hostapd/patches/463-add-mcast_rate-to-11s.patch b/package/network/services/hostapd/patches/463-add-mcast_rate-to-11s.patch
new file mode 100644 (file)
index 0000000..da146ba
--- /dev/null
@@ -0,0 +1,68 @@
+From: Sven Eckelmann <sven.eckelmann@openmesh.com>
+Date: Thu, 11 May 2017 08:21:45 +0200
+Subject: [PATCH] set mcast_rate in mesh mode
+
+The wpa_supplicant code for IBSS allows to set the mcast rate. It is
+recommended to increase this value from 1 or 6 Mbit/s to something higher
+when using a mesh protocol on top which uses the multicast packet loss as
+indicator for the link quality.
+
+This setting was unfortunately not applied for mesh mode. But it would be
+beneficial when wpa_supplicant would behave similar to IBSS mode and set
+this argument during mesh join like authsae already does. At least it is
+helpful for companies/projects which are currently switching to 802.11s
+(without mesh_fwding and with mesh_ttl set to 1) as replacement for IBSS
+because newer drivers seem to support 802.11s but not IBSS anymore.
+
+Signed-off-by: Sven Eckelmann <sven.eckelmann@openmesh.com>
+Tested-by: Simon Wunderlich <simon.wunderlich@openmesh.com>
+
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -1426,6 +1426,7 @@ struct wpa_driver_mesh_join_params {
+ #define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004
+ #define WPA_DRIVER_MESH_FLAG_AMPE     0x00000008
+       unsigned int flags;
++      int mcast_rate;
+ };
+ /**
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -8997,6 +8997,18 @@ static int nl80211_put_mesh_id(struct nl
+ }
++static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
++{
++      if (mcast_rate > 0) {
++              wpa_printf(MSG_DEBUG, "  * mcast_rate=%.1f",
++                         (double)mcast_rate / 10);
++              return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
++      }
++
++      return 0;
++}
++
++
+ static int nl80211_put_mesh_config(struct nl_msg *msg,
+                                  struct wpa_driver_mesh_bss_params *params)
+ {
+@@ -9055,6 +9067,7 @@ static int nl80211_join_mesh(struct i802
+           nl80211_put_basic_rates(msg, params->basic_rates) ||
+           nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+           nl80211_put_beacon_int(msg, params->beacon_int) ||
++          nl80211_put_mcast_rate(msg, params->mcast_rate) ||
+           nl80211_put_dtim_period(msg, params->dtim_period))
+               goto fail;
+--- a/wpa_supplicant/mesh.c
++++ b/wpa_supplicant/mesh.c
+@@ -379,6 +379,7 @@ int wpa_supplicant_join_mesh(struct wpa_
+       os_memset(&params, 0, sizeof(params));
+       params.meshid = ssid->ssid;
+       params.meshid_len = ssid->ssid_len;
++      params.mcast_rate = ssid->mcast_rate;
+       ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+       wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled;
+       wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled;
index 0d9aca9aa25d5d3866c3501c9fc1a242449b9411..dd962b47a6db9714fca011144f2aedb17d982fab 100644 (file)
@@ -41,7 +41,7 @@
                HAPD_IFACE_DISABLED,
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -284,6 +284,7 @@ static void hostapd_free_hapd_data(struc
+@@ -302,6 +302,7 @@ static void hostapd_free_hapd_data(struc
        hapd->started = 0;
  
        wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
@@ -49,7 +49,7 @@
        iapp_deinit(hapd->iapp);
        hapd->iapp = NULL;
        accounting_deinit(hapd);
-@@ -1142,6 +1143,8 @@ static int hostapd_setup_bss(struct host
+@@ -1160,6 +1161,8 @@ static int hostapd_setup_bss(struct host
        if (hapd->driver && hapd->driver->set_operstate)
                hapd->driver->set_operstate(hapd->drv_priv, 1);
  
@@ -58,7 +58,7 @@
        return 0;
  }
  
-@@ -1665,6 +1668,7 @@ static int hostapd_setup_interface_compl
+@@ -1683,6 +1686,7 @@ static int hostapd_setup_interface_compl
        if (err)
                goto fail;
  
@@ -66,7 +66,7 @@
        wpa_printf(MSG_DEBUG, "Completing interface initialization");
        if (iface->conf->channel) {
  #ifdef NEED_AP_MLME
-@@ -1844,6 +1848,7 @@ dfs_offload:
+@@ -1862,6 +1866,7 @@ dfs_offload:
  
  fail:
        wpa_printf(MSG_ERROR, "Interface initialization failed");
@@ -74,7 +74,7 @@
        hostapd_set_state(iface, HAPD_IFACE_DISABLED);
        wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
  #ifdef CONFIG_FST
-@@ -2292,6 +2297,7 @@ void hostapd_interface_deinit_free(struc
+@@ -2310,6 +2315,7 @@ void hostapd_interface_deinit_free(struc
                   (unsigned int) iface->conf->num_bss);
        driver = iface->bss[0]->driver;
        drv_priv = iface->bss[0]->drv_priv;