Computer hardware on its own is pretty much useless. An Operating System is needed which loads into memory a huge set of instructions / commands.
These instructions typically are then tied into events that originate from the hardware.
These event notifications typically include two elements: the notification of something happening and the data that goes with it.
Examples of event notifications include a key press on a keyboard (and which keys were pressed), receiving a packet on network card (and the contents of the packet), moving a mouse (direction, velocity, distance), or even a device attached to the USB port (including the device description).
These events and the code which respond to events are generically referred to as Drivers (or Device Drivers). Device Drivers operate at the highest privilege level of a computer (think something above administrator).
The USB infection process works because of a flaw in the USB device driver, which we will describe herein.
Processes and Threads
In order to make sense of a few elements in this document, one needs to have a basic understanding of two key concepts related to modern multi-threaded operating systems: Processes and Threads.
A process, as most people would see it, is the same as an application (such as Microsoft Word or Adobe Photoshop).
Processes are summarized as a collection of data and instruction (code) elements which function together but without awareness of or concern for other processes (or application). They are effectively isolated.
A thread is a construct that executes a set of instructions within a process. A process contains at least 1 thread, but can (and usually does) contain multiple threads. Threads inside a process share the same data (or memory) which is both good (one thread can be retrieving data and placing it so other threads can see it) and bad (one thread could be updating data to memory while another is reading it causing all sorts of chaos).
Best analogy I can offer is to think of operating systems like a subdivision, a process (or application) like a house, and a thread like the people inside the house running around doing things. In this model, the operating system would be the local government (laws, enforcement, trash, sewer, water, etc).
Memory Can Be Both Data and Instructions
Another key bit to understand about computers is that our humanistic notion of a distinction between data and code is not consistent with the reality of the way computers function.
When the operating system is called upon to run an application, the process is fairly simple. A block of memory is allocated (in RAM), the combined data and code is loaded into the space allocated for that process, and then a new process (independent and isolated instruction executing construct) is started and directed to begin with the first instruction for the loaded code (called the entry point).
Memory within the operating system, or a given process, can be updated during code execution. One moment it might contains a series of data elements, then next it might contain a set of instructions. Within a given process, the instructions are located at a specific location within the memory allocated for that instruction, which is not the same as the physical location in RAM.
Computer instructions are fairly simple and can be grouped into simple operations and jumps.
A simple operation may include basic arithmetic, logical operations (comparisons), and memory management (copy or load data from location X to location Y, or load new instructions from disk drive to memory location Z).
Typically, when an application is executing instructions in memory, it executes the instruction in the current memory location and then moves to the next adjacent memory location and executes the instruction there. Jumps are important to understand as they allow one instruction to tell the process/thread executing it to continue its execution at an instruction at another location in memory (jump to this other location).
Jumps can be one-way or two-way (like flights).
In one-way jumps, the executing jumps to a new instruction elsewhere in memory never to return to the jump off point. In two-way jumps, the code leaps off to the new location, executes to completion the code there, and then returns back to the jump off point and continues with the instruction just after it.
Yes, if you are thinking computer programs are a lot like a game of Chutes and Ladders, you are right.
Pulling It Together
Understanding the USB exploit requires understanding how event notifications, device drivers, processes and threads, and memory instructions.
Generic Setup Steps:
- The operating system, on startup, loads a bunch of instructions into a memory location within its own Process space that allows it to interact with USB devices (known as a USB device driver).
- The operating system, knowing that the hardware may eventually and repeatedly send a notification, ties the event notification for “New USB Device Connected” to the USB device driver (with some special magic we will discuss later).
- User plugs USB memory stick into the USB port on computer.
- USB controller on the motherboard detects the insertion of the USB device.
- USB controller interrogates the USB device by calling hardware based instructions (per the USB interface specification) to retrieve the “USB Device Description”.
- The USB controller then passes to the Southbridge via the PCI Bus (see separate document on those) an event notification (also known as interrupt) that includes an event notification type (“New USB Device Attached”) and the data (“USB Device Description”(.
- The Southbridge passes along the event notification to the Northbridge.
- The Northbridge then places the data (“USB Device Description”( into memory (RAM” and records the memory location (address).
- The Northbridge then passes to the Operating System process the event notification (“New USB Device Attached”( and the memory address of the data (“USB Device Description”(.
- The Operating System process sees that the event notification is related USB device and that some action is needed.
- The Operating System process starts a new Thread. It passes to the new thread the memory location of the “USB Device Description” and the starting point in memory for the USB Device Driver instructions for dealing with the event “New USB Device Attached”.
- The newly spawned thread then proceeds by calling the USB Device Driver instructions which includes copying the “USB Device Description” to a known location in memory and then calling an instruction to interact with that data.
- The instruction then interrogates the “USB Device Description” data that has been copied into its own memory space and proceeds with a long set of instructions how to make that device usable to the rest of the operating system and the user.
NOTE: “New USB Device Attachedâ€ event is typically some hexadecimal data values which, by convention or specification allow any operating system to know that the event is in fact a â€œNew USB Device Attachedâ€ as opposed to something like â€œKeyboard Key Pressâ€.
The Hack: Malformed USB Description as Injector
- Evil hacker examines specifications for how hardware, operating system, and device drivers are expected to interact.
- Hacker determines that the specification states that the “USB Device Description” is expected to only no more than XX Bytes in length.
- Hacker then crafts a custom “USB Device Description” and examines how it passes from the USB device through the computer and into the â€œUSB Device Driverâ€ with special attention paid to where in memory that data is placed in relation to the of instructions that occur after it is loaded.
- Hacker then crafts a malformed and abnormally long “USB Device Description” and watches how it effects the operating system (hoping it causes a crash). His hope is that the overly long description will overwrite existing instructions with whatever is contained in the data.
- If the malformed “USB Device Description” causes a device driver or operating system crash, the hacker has likely found an exploitable bug.
- The hacker replaces the “USB Device Description” with his malware injection code.
- The hacker then works to ensure that the starting instruction in his malware injection code (a jump instruction to the start his evil code that is encapsulated in the “USB Device Description”) happens to land in a location in memory that will be reached, eventually, by the thread executing the USB Device Driver code. This process of having an abnormally long blob of data overwriting critical instructions is known as a Buffer Overrun attack.
- With any luck, plugging the USB device in with the malformed and overly long “USB Device Description” will result in the execution of whatever code has been embedded in the description (a.k.a. the injector).
- The injector code, upon triggering, will be operating as if it were part of the operating system (highest possible privileges). In general, its next step is to load and start the execution of malware hidden on the USB memory stick (and potentially even protected by encryption).
- Our injector, having been loaded as a “USB Device Description”, run by the “USB Device Driver” as a result of the “New USB Device Attached” notification will then load the malware from the USB memory stick.
- Note, USB memory sticks, although they can be viewed as regular disk drives by the operating system, can also be accessed as raw disk devices (i.e. doesnâ€™t appear in the operating system as a drive yet contains data that properly written code can read).
- Our injector, executing on the thread that was the USB Device Driver thread, within the Operating System Process, can access locations on the USB memory stick that do not appear as part of the file system on the USB stick.
- It loads from the stick an encrypted blob of data into memory.
- It then either starts execution on its own thread that malware OR it forces the operating system to start a new process that runs the encrypted blob as if it were its own application.
- The injector thread then does one of two things:
- Either it cleans itself out of memory and returns the USB Device Driver to its original state.
- Or it then continues on its merry rampage
- It might write itself into the permanent copy off the device driver on the hard disk to ensure it gets loaded after the compute reboot.
- It engages the operating system in such a way to cloak the process that the malware is running from anti-virus, malware, or other observation
- The malware, having being loaded as an encrypted blob of data into memory (to ensure even if the cloaking fails, the evil code will not be detected by any defensive software) by the injector from the hidden disk segments on the USB stick then starts running (hopefully completely cloaked from observation thanks to its partner, the injector).
- The first step the malware may take, upon execution, is to go generate the decryption key for the rest of the code that was loaded.
- Next, the malware then decrypts the data and instructions that were loaded into memory and executes them (with the full privileges of the operating system / administrator) allowing it to do fun things including propagating through the network, patching software of the operating system, etc.
The important note is that the injector is typically incredibly small and light and its role is to leverage an exploit to trigger an escalation of privileges or execution at a privileged level and then in turn load and run the real malware (from the USB fob or even from the network).