xorl %eax, %eax

CVE-2008-5134: Linux Wireless Remote Buffer Overflow

leave a comment »

This is an interesting vulnerability that affects Linux kernel up to 2.6.27.5 release and can be found on the wireless subsystem. The bug was disclosed and patched by Johannes Berg on 29 October 2008 as a fix to a buffer overflow. The vulnerable code can be found at drivers/net/wireless/libertas/scan.c. The following code is part of the Linux kernel 2.6.27.4 release.

508 static int lbs_process_bss(struct bss_descriptor *bss,
509                           uint8_t **pbeaconinfo, int *bytesleft)
510 {

This function is used to parse the BSS (Basic Service Set) returned from the firmware while performing a BSS scan (BSS probe response) or responses from a beacon when using the ‘scan‘ command. Finally, it stores these information to a bss_descriptor structure. Here is what we need to understand the vulnerability:

538     /* Initialize the current working beacon pointer for this BSS iteration */
539     pos = *pbeaconinfo;
540     end = pos + beaconsize;
       ...
589     /* process variable IE */
590     while (pos <= end - 2) {
591             struct ieee80211_info_element * elem = (void *)pos;
       ...
599                switch (elem->id) {
600                case MFIE_TYPE_SSID:
601                        bss->ssid_len = elem->len;
602                        memcpy(bss->ssid, elem->data, elem->len);
603                        lbs_deb_scan("got SSID IE: '%s', len %u\n",
604                                     escape_essid(bss->ssid, bss->ssid_len),
605                                     bss->ssid_len);

Here, you can see at lines 539-540 two variables that contain a beacon pointer and the end of the structure respectively. At line 591 is an initialization of the user controlled ieee80211_info_element structure. Finally, if user sets its ID to be equal to MFIE_TYPE_SSID which is 0 as defined in include/net/ieee80211.h:

510 /* Management Frame Information Element Types */
511 enum ieee80211_mfie {
512        MFIE_TYPE_SSID = 0,
     ...
539 };
540

Now, the user controlled length is copied to bss->ssid_len. This user controlled variable can have maximum value of unsigned 255 since it is defined as:

609 struct ieee80211_info_element {
610         u8 id;
611         u8 len;
612         u8 data[0];
613 } __attribute__ ((packed));

Moreover, the next instruction executed is a memcpy() call that copies user controlled elem->data to kernel structure bss_descriptor. Since user can control both the size limit and the data, he can generate a specially crafted packet that exceeds the limit of IW_ESSID_MAX_SIZE + 1 that is the maximum SSID on bss_descriptor structure as it is being defined at drivers/net/wireless/libertas/dev.h:

327 struct bss_descriptor {
328         u8 bssid[ETH_ALEN];
329 
330         u8 ssid[IW_ESSID_MAX_SIZE + 1];
331         u8 ssid_len;
      ...
359         struct list_head list;
360 };

Which is simply:

442 /* Maximum size of the ESSID and NICKN strings */
443 #define IW_ESSID_MAX_SIZE       32

This means that we are dealing with a common stack based buffer overflow on the kernel side. However, exploitation of the above vulnerability is not something trivial since you have to generate malicious packets through an AP (Access Point) and thus anyone vulnerable that will process those data (scan for networks) can be exploited. Unfortunately, this is almost impossible to lead to a remote code execution situation as Eugene Teo pointed out because the sent buffer cannot be large enough to overwrite outside the bss_descriptor structure bounds. Of course, by manipulating the internal structures of the kernel using this vulnerability you might be able to perform other attacks and gain remote access in the context of the kernel but I wasn’t able to do so. However, it is clearly possible. Finally, here is the patch:

       case MFIE_TYPE_SSID:
-              bss->ssid_len = elem->len;
-              memcpy(bss->ssid, elem->data, elem->len);
+              bss->ssid_len = min_t(int, 32, elem->len);
+              memcpy(bss->ssid, elem->data, bss->ssid_len);
               lbs_deb_scan("got SSID IE: '%s', len %u\n", 

Personally, I don’t find the use of min_t() macro a good idea. I’m saying this because both elem->len as well as bss->ssid_len are of type unsigned char which is 1 byte long. Here these variables are promoted to signed integers. I know that this still fixes the bug but it makes it look like length variables are signed integers instead of unsigned characters and in the future it might lead to possible bugs. I suggest that it should be:

    bss->ssid_len = min_t(u8, 32, elem->len);

I can’t see a reason why you should have the type promotion on min_t() in this case. Anyway, to trigger this bug just construct a packet with SSID of type MFIE_TYPE_SSID (which is 0 as stated earlier) and fill the rest of the structure with garbage. You can easily do this using SCAPY or any other packet generator.

Written by xorl

January 26, 2009 at 14:09

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s