Sunday, September 23, 2007

Memory-Constrained Code Injection

by Travis Goodspeed <travis at utk.edu>
at the Extreme Measurement Communications Center
of the Oak Ridge National Laboratory

Introduction


When injecting code into an embedded system, as has demonstrated in the prior article, entitled MSP430 Buffer Overflow Exploit for Wireless Sensor Nodes, the limitation of code size frequently comes up. The following will explain how a 128-byte packet can be used to inject an exploit much longer than itself. This method would also work in workstation and server attacks, but is less valuable in such environments because such platforms lack the prohibitive memory constraints that are to be found in embedded systems.

It is assumed that the reader is familiar with the previously referenced article, and it is further assumed that a method for injecting short fragments of machine code exists. These examples are specific to TinyOS 2.x on the MSP430, but the principles in question should be of relevance for any resource-constrained target over a datagram channel of limited packet size.

General


The method which will be presented makes use of unallocated memory as a buffer into which a large payload, one that is larger than any individual packet, is populated by a series of code injections, each of which loads a short piece of the larger payload before returning to normal execution.

Each packet will set a single word of memory to a word from its payload, thus copying as many words as are required from the attacker to the victim, loading them at whatever address is specified. So long as the target address lies beneath the stack and above the heap, it will not interfere with the operation of the victim's firmware and will not be damaged or overwritten by another subroutine.

The memory layout looks something like this:

(Top of Memory, 0xFFFF)
Internal Flash{
Interrupt Vector Table
Data/Code
}
Internal Ram{
Stack (grows down)
Unused (between heap and stack)
Heap (grows up, often empty)
Globals
Memory-mapped I/O
}
(Bottom of Memory, 0x0000)


The payload will be housed in the unused region between the stack, which grows downward from the top of memory, and the heap, which grows upward from the bottom of memory.

Specific


Suppose that an attacker is capable of broadcasting packets which allow for a six-byte payload to be executing on a victim. Further, suppose that the attacker wishes to execute a single block PB of 256 bytes of machine code at address TA, within a contiguous region and without interruption.

The attacker can craft a memory-injection (MI) packet which sets an address to a value. In MSP430 assembly, this is expressed as
MOV.W #val, &addr
which sets the word at memory location addr to val. To place the value DEAD at the memory location BEEF, one would use
MOV.W #0xdead, &0xbeef
As machine language using absolute addressing, this would be
{0x40b2, 0xdead, 0xbeef}
The latter two words may be substituted as required, making it trivial to have a function write injection code on the fly, such as


/*! Takes a pointer to a six-byte region which is populated
* with machine code for setting the value at the address.
*/
void attackcode_set(uint16_t *code,
uint16_t address,
uint16_t value){
code[0]=0x40b2;
code[1]=value;
code[2]=address;
}


Thus to copy an expanse of code to the victim, the attacker would compose 128 injection attacks by composing payloads with the following loop:

//Populate the buffer MI with memory injections to place all of PB at TA
for(int i=0;i<0x50;i++)
attackcode_set(MI[i], TA+2*i, PB[i]);


Each packet of MI[] is then broadcast in any order whatsoever. As each packet is received, another two bytes near TA, the target address, are set. Thus, two bytes at a time, the whole payload is transfered to the victim. Once they've been delivered, a new injection is passed but one that doesn't execute itself. Rather, it jumps to TA to begin the previously loaded code, all 256 bytes of it.

Injection of Complete Firmware


Once this longer payload has been installed, it can be used to copy a portion of itself to external flash. This can be repeated until a complete firmware--that is to say software which resides in internal flash--exists on external flash. Then a short loader routine could copy it from external to internal flash, thereby replacing the victim's firmware with the attacker's. If this new firmware were to begin broadcasting its own installation routine, the result would be a self-propagating worm.

Conclusion


One should never assume that an embedded platform is safe from a sophisticated injection behavior because of the limitations imposed by a datagram networking framework, such as 802.15.4. Even without streaming or the buffering of prior packets, it's possible--in fact rather trivial--to inject a payload significantly larger than the packet size.

Please contact me if you know of any prior implementation or discussion of this technique. I would be much obliged.

No comments: