USB Fuzzing for the Masses

We began our USB research at MWR Labs approximately 3 years ago with the intention of quantifying risk associated with the use of this type of technology. The primary focus of this research was to attack the software that handles USB input, such as USB device drivers, which are implemented within commonly used operating systems. We wanted to understand whether there was a problem that our clients should be worrying about and if so how big it was. We also knew that it was important to detect and report exploitable vulnerabilities and not just denial of service conditions which are clearly not always as significant when you have a degree of physical access.

Over the past 3 years we have developed the methods we use for identifying vulnerabilities in USB software and have used these to identify a number of vulnerabilities in different platforms, including both Linux and Microsoft Windows. More importantly, we identified that any environment where a USB port is exposed should be reviewed as there are lots of potential vulnerabilities lurking below the surface of any Operating System, just waiting to be found. We have also realised that this isn’t an area of research that we can investigate on our own and a wider effort within the community is required. Therefore, we have decided to share some of the methods and techniques that we have successfully used to discover and exploit vulnerabilities in USB software so that a wider effort can be utilised to continue research in this area.

Fuzzing USB Software

The use of fuzzing techniques is very efficient for vulnerability discovery when source code is not available and when testing is needed from a black box perspective. Throughout this research, we have looked at different approaches for fuzzing USB software, from a bizarre USB over IP approach (that we don’t discuss here), to a virtualised toolkit and a USB hardware fuzzer. In previous presentations on this subject we have been keen to stress that the talk was not specifically about designing and implementing USB fuzzers; however, we have now decided to share some of the work we have already completed in this area. The benefits and weaknesses of each of these approaches are described here alongside some of the findings that have been publicly disclosed up to this point.

Qemu Fuzzing Approach

For this fuzzing technique, Qemu is used to run the environment which is to be subject of the fuzzing. Qemu is an open source machine emulator and virtualiser, which allows the emulation of USB devices from the Host system to the guest system 1

In this set up, the Host system continuously emulates malicious USB devices within the target system (the guest) which is running the OS which is currently being tested.

usb4-2

The emulation of devices can be done in Qemu by accessing the guest system control console via raw tcp sockets:

-monitor tcp:192.168.1.1:5555,server,nowait

Devices can then be emulated by sending commands to this console:

usb_add fuzz-device 

“fuzz-device” is the emulated USB device, which is defined in Qemu in the following locations:

  • vl.c – use for ‘usb_add’ in control console
  • hw/usb.h – header file
  • hw/usb-fuzz.c – emuled device

“usb-fuzz.c” needs to be specifically programmed to meet your fuzzing requirements and for the transmission of USB descriptors. This is where we also need to specify if the fuzzer is targeting a specific device driver or if it will run through multiples drivers, e.g. a list of all USB drivers available on Windows 7. A good starting point would be to based your fuzzer in one of the existing devices provided by Qemu, such as “usb-wacom.c”.

The demo included here shows the emulation of a USB device using Qemu that triggers a segmentation fault. The example used is based on the Auerswald Linux device driver buffer overflow vulnerability identified by MWR in Oct 2009 2

The diagram below illustrates the Qemu fuzzing process:

usb

The main advantages of this approach are:

  • Fast and Powerful. As it is software based it is possible to run multiple instances and speed up the process by adding computational power.
  • Low Resources Required. Qemu is open source and freely available and only some development is required to get a fuzzer running.
  • Effective Debugging. Fuzzers can be implemented that are verbose in their output by simply adding print outs into the fuzzer code, recording details of data that is submitted and the responses that are received. This is particularly helpful when analysing crashes that are identified during the fuzzing process.
  • Portable and Versatile. As it is software based it is easy to add functionality, make changes and adapt fuzzer to different target platforms. Additionally, it is possible to integrate with existing fuzzing engines for the generation of test case data, such as Peach or Sulley.
  • Real World Deployment friendly. Where an organisation has a specific build or system that requires testing it can be converted to a Virtual Machine and loaded directly into the test harness providing the organisation with assurance about the robustness of their deployment.

The limitation of this approach would be encountered when facing a system that cannot be run on Qemu. If this is the case, you may be required to look for other alternatives, such as building a USB hardware fuzzer.

USB Hardware Fuzzer

The Implementation of a hardware based fuzzer was the first thing that we thought about when we were faced with the challenge of fuzzing USB software for a client with a “black-box”. “Something” that you plug into a system and sends random USB data seemed to be the most rational option as our purpose was to reproduce USB traffic. Plus it seemed much cooler!

Over the course of the research project we have come to realise that a lot of limitations exist with this approach and although developing a hardware fuzzer is the only route to go down in very specific testing scenarios, the following aspects should be considered before building your fuzzer, particularly when implementing your fuzzer in a microcontroller.

  • There is limited visibility on the data that is sent and received (ie the verbosity of the fuzzer), subsequently affecting the effort required for debugging and crash analysis
  • Computational power limitations in the fuzzing hardware, affecting the speed of performing the fuzzing
  • Computational power limitations in the fuzzing hardware, affecting the generation of USB test case data
  • It is more difficult to integrate with existing fuzzing engines for the creation of test cases

Although it is possible to overcome some of these limitations by implementing the fuzzer in a more powerful system and by adding extra functionality, this should be assessed beforehand, as the effort to build this may not be time and cost effective for the situation you are in. Our advice is to look at the other options first before going down this route!

In the case where you are facing a system that you don’t have enough technical specifications to review fully or even when you have but it is not possible to run the OS in Qemu, the only method to send malicious USB data to the target system may be using a hardware based fuzzer. In such a “black box” scenario, automatically sending malicious USB data is the first phase of the fuzzing process and you will face other challenges to achieve your objectives, such as the detection and logging of crashes and the restart of the target OS or software in the case of a crash or when an anomaly has been identified. Other challenges that you may be facing are the debugging of identified crashes and the development of exploit code in a scenario with such limited visibility of the internals of the target system.

Nevertheless, when it comes to the exploitation phase you will be required to develop you own USB hardware device in order to trigger the vulnerability that you have discovered. See the “Exploitation” section for details on how to exploit USB vulnerabilities.

Crash Debugging

So if we have implemented our fuzzer correctly we WILL have a bunch of crashes and its now time to do some debugging to find out which ones are exploitable. There are different debugging approaches that can be taken and some of these are explained here.

KGDB – Serial Port

This technique can be used on Linux systems for debugging issues in USB device drivers. For our debugging environment we have KDGB installed and two computers connected via the serial port. KGDB is a Linux kernel debugger based on GDB 3

The kernel to be debugged runs on the target machine, into which the USB hardware with the test data that triggers the issue is plugged. GDB runs on the development machine and a serial line is used by GDB to communicate to the kernel being debugged.

The diagram include below illustrates the set up of the environment:

usb5-2

The demo included here shows the debugging of the Auerswald Linux device driver buffer overflow vulnerability 2

Qemu – USB emulation

This technique can be used on Linux and Windows systems for debugging issues in USB drivers and other USB software. For our debugging environment we will run our target system in Qemu with a debugger, such as GDB or WinDbg attached to the target software, and then the USB device that triggers the issue will be emulated in the target system.

The example included below was debugged using the techniques described here. This example is based in a vulnerability identified by MWR in usbhub.sys on Windows XP Service Pack 2 in the function:

; int __stdcall USBH_GetSerialNumberString(PDEVICE_OBJECTDeviceObject, int, int, __int16, int)_USBH_GetSerialNumberString@20 proc near

This function requests the SerialNumberString string descriptor from the USB device using the SBH_SyncGetStringDescriptor function. Data returned from this function is then used for a memory allocation and a subsequent copy operation. The length of the allocation can be 0 as this data is under control of the attacker.

The copy operation is implemented as a sequence of “rep movsb” operations:

movzx   ecx, byte ptr [ebx]
mov     esi, ecx
shr     ecx, 2
xor     eax, eax
mov     edi, edx
rep stosd
mov     ecx, esi
and     ecx, 3
rep stosb
movzx   ecx, byte ptr [ebx]
dec     ecx
dec     ecx
mov     eax, ecx
shr     ecx, 2
lea     esi, [ebx+2]
mov     edi, edx
rep movsd
mov     ecx, eax
mov     eax, [ebp+arg_4]
and     ecx, 3
rep movsb
mov     ecx, [ebp+arg_8]
mov     [eax], edx
movzx   ax, byte ptr [ebx]
mov     [ecx], ax
jmp     short loc_1989D

For the third “rep movsd” the length in “ecx” has 2 subtracted from it which will result in a buffer overflow. The subsequent memory can lead to arbitrary code execution.

MWR discovered this vulnerability when testing an embedded device with Windows SP2 installed. Further investigation and communication with Microsoft revealed that this issue did not affect SP3, however SP2 remains vulnerable.
Source Code Review

In situations where you have access to the source code of drivers, always take advantage of this to look for vulnerabilities that may have been introduced at some point within the development lifecycle. A good example of this is the buffer overflow vulnerability in the caiaq USB drivers discovered by MWR in March 2011 4

The vulnerability affects the code handling the USB product name in the following drivers:

/sound/usb/caiaq/audio.c
/sound/usb/caiaq/midi.c

The affected code is included here for the “audio.c” driver. The vulnerability is in the “strcpy” function shown below, as the product name that the USB device sends (“dev→product_name”) can be larger than the buffer of “dev→pcm→name” and “rmidi→name”, where the data is being copied to (80 bytes).

Source code from /linux-2.6.38/sound/usb/caiaq/audio.c

int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
{
  ...

  ret = snd_pcm_new(dev->chip.card, dev->product_name, 0,
                    dev->n_audio_out, dev->n_audio_in, &dev->pcm);

  ...

  dev->pcm->private_data = dev;
  strcpy(dev->pcm->name, dev->product_name);

Source code from /linux-2.6.38/include/sound/pcm.h

struct snd_pcm {
  struct snd_card *card;
  struct list_head list;
  int device; /* device number */
  unsigned int info_flags;
  unsigned short dev_class;
  unsigned short dev_subclass;
  char id[64];
  char name[80];
  struct snd_pcm_str streams[2];
  struct mutex open_mutex;
  wait_queue_head_t open_wait;
  void *private_data;
  void (*private_free) (struct snd_pcm *pcm);
  struct device *dev; /* actual hw device this belongs to */
  #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
    struct snd_pcm_oss oss;
  #endif
};

Exploitation

You have now found the vulnerabilities that you WILL find if you look. You now need to implement your exploit code in a microcontroller for use in the real world. There are a different manufacturers and devices you can opt for. From PIC family microcontrollers 5 to Atmel microcontrollers 6, such as the ATUSB162 Micro Controller used for the PS3 jailbreak, and the Arduino board 78

PIC family microcontrollers are a good starting point for developing your malicious USB hardware device, they are easy to program and there is plenty of information and development kits available for programming your own USB device.

For example the code included below defines the Device descriptor of our USB by modifying the Vendor id and Product id, these specifies the driver that will be loaded when the USB is plugged into the target system.

const USB_DEVICE_DESCRIPTOR DeviceDescriptor = { 
  sizeof(USB_DEVICE_DESCRIPTOR), /* bLength */
  TYPE_DEVICE_DESCRIPTOR,        /* bDescriptorType */
  0x0110,                        /* bcdUSB USB Version 1.1 */
  0,                             /* bDeviceClass */
  0,                             /* bDeviceSubclass */
  0,                             /* bDeviceProtocol */
  8,                             /* bMaxPacketSize 8 Bytes */
  0xBEEF,                        /* idVendor */                                            
  0x1337,                        /* idProduct */
  0x0000,                        /* bcdDevice */
  1,                             /* iManufacturer String Index */
  0,                             /* iProduct String Index */
  0,                             /* iSerialNumber String Index */
  1                              /* bNumberConfigurations */
};  

Refer to Microchip Technology Inc. Low Pin Count USB Development Kit User’s Guide for more details of the source code.

For example the code included below defines the String descriptor of our USB by modifying the Vendor id and Product id, these specifies the USB device manufacturer name and product name in human readable format.

//Manufacturer string descriptor
  ROM struct{BYTE bLength;BYTE bDscType;WORD string[12];}
  sd002={sizeof(sd002),USB_DESCRIPTOR_STRING,
  {
  'M','A','N','U','F','A','C','T','U','R','E','R'
  }};
  //Product string descriptor
  ROM struct{BYTE bLength;BYTE bDscType;WORD string[7];}
  sd003={sizeof(sd003),USB_DESCRIPTOR_STRING,
  {    
  'P','R','O','D','U','C','T'
  }};

For example, if a vulnerability was to be identified in a USB device driver affecting the handling of the String descriptor, we could use the code above to first specify the affected driver and then deliver our shell code in the manufacturer and/or string descriptor. This approach was used for the exploitation of the Auerswald Buffer overflow vulnerability 2

Bypassing USB Restrictions Software

Software solutions to restrict access to computer systems from unauthorised USB devices are widely used in corporate environments. A number of weaknesses have previously been identified with this software that could allow an attacker to use an unauthorised USB device. These should be considered when reviewing the controls implemented around USB in any environment that relies on this type of technology.

Enumeration is the first phase of communication between a USB device and a computer system. In this phase, information about the device is obtained by the host computer and the USB device drivers that are to be loaded in the host are specified. In this phase of communication, the vendor id (VID) and product id (PID) are also transferred to the host; this information determines the drivers to be loaded and it is also used by the lockdown software to identify the device that has been plugged in.

Many implementations of USB lockdown software use a white listing approach based on the VID and PID of the plugged in device. This mechanism for validating authorised devices has been found to be flawed, as it has been discovered that an unauthorised device could be used with the pre-installed USB drivers by setting the device class type to the desired one (such as HID or mass storage device) and the VID and PID of a white listed USB device.

It has been possible to build USB hardware devices with a VID and PID set to one of the white listed devices but with the class set to a mass storage device, which then bypasses the software security restrictions. This has been observed in multiple software products that are commercially available although these will not be named here. This approach allows the USB validation to be bypassed and an unauthorised USB mass storage device to be used in the environment.

Conclusions

Our USB research project has been running for a number of years now and we have reached a number of conclusions as a result of it:

  • USB fuzzing and review is within the reach of security testers and researchers and can be achieved with little investment and resources
  • A careful assessment should be made of each scenario you encounter to ensure you use the best approach
  • Device driver software across platforms is not as robust as you might hope and there are still many bugs to be found
  • The effort required to test, review and prove exploitability of the problems that are out there is considerable and effort is required from researchers through to vendors to address and resolve the problems

References

1 Qemu
http://wiki.qemu.org/Main_Page

2 Linux Auerswald USB Device Driver Buffer Overflow
http://labs.mwrinfosecurity.com/files/Advisories/mwri_linux-usb-buffer-overflow_2009-10-29.pdf

3 KGDB
http://kgdb.linsyssoft.com/

4 Linux Kernel caiaq USB Drivers Buffer Overflow
http://labs.mwrinfosecurity.com/files/Advisories/mwri_caiaq-usb-drivers-buffer-overflow_2011-03-07.pdf

5 PIC microcontrollers
http://www.microchip.com/

6 Atmel
http://www2.atmel.com/

7 Arduino
http://www.arduino.cc/

8 Physical Computing, Virtual Security: Adding the Arduino Microcontroller Development Environment to Your Security Toolbox
http://defcon.org/html/defcon-18/dc-18-speakers.html#Honeywell

Further Reading

Defcon 17: Fun with Plug & 0wn
http://labs.mwrinfosecurity.com/files/Publications/mwri_usb-attacks-defcon17_2009-08-02.pdf

T2’09: USB Attacks: Fun with Plug and 0wn
http://labs.mwrinfosecurity.com/files/Publications/mwri_t2-usb-fun-with-plug-and-0wn_2009-10-29.pdf

Moritz Jodeit – Evaluating Security Aspects of the Universal Serial Bus
http://www.informatik.uni-hamburg.de/SVS/archiv/slides/09-01-13-OS-Jodeit-Evaluating_Security_Aspects_of_USB.pdf

Moritz Jodeit – USB Device Drivers: A Stepping Stone into your Kernel
http://www.jodeit.org/research/DeepSec2009_USB_Device_Drivers.pdf
http://www.jodeit.org/research/EC2ND_USB_Device_Drivers.pdf

Jon Larimer – Beyond Autorun: Exploiting vulnerabilities with removable storage
https://media.blackhat.com/bh-dc-11/Larimer/BlackHat_DC_2011_Larimer_Vulnerabiliters%20w-removeable%20storage-Slides.pdf