Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Thu, 13 Oct 2016 11:51:26 +0800
From: freener <freener.gdx@...il.com>
To: oss-security@...ts.openwall.com
Subject: Re: CVE Request -- Broadcom Wifi Driver Brcmfmac brcmf_cfg80211_start_ap
 Buffer Overflow

hi,
    I found a stack buffer overflow vulnerability in Broadcom wifi driver
brcmfmac, this issue has been fixed, I would like to
request a CVE-ID for this issue.

Description
=========

Cfg80211 module in kernel is the main interface to operate on wifi.
This module defines an operation data structure which stores many
commands and callback functions to control the wifi, and those
callback functions are implemented in wifi driver finally, executables
can communicate with this module by netlink socket.

To trigger the bug the exploit should send a NL80211_CMD_START_AP or
NL80211_CMD_NEW_BEACON command to nl80211 socket in kernel.
NL80211_CMD_START_AP is equal with NL80211_CMD_NEW_BEACON according
with the definition in nl80211.h. Hostapd uses NL80211_CMD_NEW_BEACON.

       static struct genl_ops nl80211_ops[] = {
                 ...
                 {
                         .cmd = NL80211_CMD_START_AP,
                         .policy = nl80211_policy,
                         .flags = GENL_ADMIN_PERM,
                         .doit  = nl80211_start_ap,
                         .internal_flags = NL80211_FLAG_NEED_NETDEV_UP
| NL80211_FLAG_NEED_RTNL,
                 },
                 ...
       }

When kernel receives the NL80211_CMD_START_AP command then it will
call nl80211_start_ap function,  but it requries executable owns
CAP_NET_ADMIN permission.

In nl80211_start_ap, it will parse the data received from user, and
store the result in cfg80211_ap_settings structure。

       static int nl80211_start_ap( struct sk_buff *skb, struct
genl_info *info ) {
                ...
                struct cfg80211_ap_settings params;
                ...
                memset( &params,  0, sizeof(params) );
                ...
                err = nl80211_parse_beacon( info, &params.beacon );

                if ( info->attrs[NL80211_ATTR_SSID] ) {
                         params.ssid = nla_data(
info->attrs[NL80211_ATTR_SSID] );
                         params.ssid_len = nla_len(
info->attrs[NL80211_ATTR_SSID] );
                         if ( params.ssid_len == 0 || params.ssid_len
> IEEE80211_MAX_SSID_LEN )
                                 return -EINVAL;
                }

               ...
               err = rdev_start_ap( rdev, dev, &params );
               ...
       }

      struct cfg80211_ap_settings {
	    struct cfg80211_chan_def chandef;

	    struct cfg80211_beacon_data beacon;

	     int beacon_interval, dtim_period;
	     const u8 *ssid;
	     size_t ssid_len;
	     enum nl80211_hidden_ssid hidden_ssid;
	     struct cfg80211_crypto_settings crypto;
	     bool privacy;
	     enum nl80211_auth_type auth_type;
	     int inactivity_timeout;
	     u8 p2p_ctwindow;
	     bool p2p_opp_ps;
	     const struct cfg80211_acl_data *acl;
	     bool radar_required;
       };

       struct cfg80211_beacon_data {
              const u8 *head, *tail;
              const u8 *beacon_ies;
              const u8 *proberesp_ies;
              const u8 *assocresp_ies;
              const u8 *probe_resp;

             size_t head_len, tail_len;
             size_t beacon_ies_len;
             size_t proberesp_ies_len;
	     size_t assocresp_ies_len;
	     size_t probe_resp_len;
        };

It also does many checks, the interface type must be NL80211_IFTYPE_AP
or NL80211_IFTYPE_P2P_GO, and data must contain informations about
NL80211_ATTR_BEACON_INTERVAL, NL80211_ATTR_DTIM_PERIOD,
NL80211_ATTR_BEACON_HEAD. Finally it will call rdev_start_ap function.

NL80211_ATTR_SSID is optional, user can send a netlink packet which
does not contain information about NL80211_ATTR_SSID, so params.ssid
and params.ssid_len will be 0. It's the key point in the exploit.

       static inline int rdev_start_ap( struct
cfg80211_registered_device *rdev, struct net_device *dev, struct
cfg80211_ap_settings *settings ) {
                ...
                ret = rdev->ops->start_ap( &rdev->wiphy, dev, settings );
                ...
       }

rdev_start_ap will call the callback function defined in brcmfmac driver.

      static struct cfg80211_ops wl_cfg80211_ops = {
              ...
              .start_ap = brcmf_cfg80211_start_ap;
              ...
      }

If the netlink packet does not contian info about NL80211_ATTR_SSID,
brcmf_cfg80211_start_ap will call brcmf_parse_tlvs to parse head data
further. The data format is TLV (Type, Length, Value ),
it will parse type WLAN_EID_SSID info in data, all those datas are
controlled by user. It does not
check the length of data before calling memcpy to copy the data to
stack buffer. The length of stack buffer ssid_le.SSID is 32, so we can
construct a malicous data packet in NL80211_CMD_START_AP command, and
make WLAN_EID_SSID's length large then 32. When it copies the data, it
will overflow the stack buffer.

       brcmf_cfg80211_start_ap(   struct cfg80211_ap_settings *settings ) {
               s32  ie_offset;
               struct brcmf_tlv *ssid_ie;
               struct brcmf_ssid_le ssid_le;

               memset( &ssid_le, 0, sizeof(ssid_le) );

               if  ( settings->ssid == NULL || settings->ssid_len == 0 ) {
                        ie_offset = DOT11_MGMT_HDR_LEN +
DOT11_BCN_PRB_FIXED_LEN;
                        ssid_ie = brcmf_parse_tlvs( (u8
*)&settings->beacon.head[ie_offset], settings->beacon.head_len -
ie_offset, WLAN_EID_SSID );
                       if ( !ssid_ie )
                              return -EINVAL;

                      memcpy( ssid_le.SSID, ssid_ie->data,
ssid_ie->len );   //overflow here.
                      ssid_le.SSID_len = cpu_to_le32( ssid_ie->len );
               }
               else {
                      memcpy( ssid_le.SSID, settings->ssid,
settings->ssid_len );
                      ssid_le.SSID_len = cput_to_le32( (u32)settings->ssid_len);
               }
               ...
      }


Credit
=====
This issue was discovered by Daxing Guo of Tencent's Xuanwu Lab


Patch
=====
https://git.kernel.org/cgit/linux/kernel/git/davem/net.git/commit/?id=ded89912156b1a47d940a0c954c43afbabd0c42c

2016-10-12 14:59 GMT+08:00 freener <freener.gdx@...il.com>:

> hi,
>
>     This is a buffer overflow vulnerability in wifi driver brcmfmac.
>
>     The vulnerability has been patched in in Linux kernel 4.7.7 and 4.8.1.
>
>      https://patchwork.kernel.org/patch/9313305/
>
>
>      thanks.
>

Powered by blists - more mailing lists

Your e-mail address:

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Powered by Openwall GNU/*/Linux - Powered by OpenVZ