Exploiting Embedded Devices (Part 1)

Recently we have been assessing an increasing number of embedded devices. Seeing as the methods for carrying out this type of assessment are not at all well defined, I am starting a series of posts discussing vulnerabilities and exploitation on embedded platforms.

In recent years the exploitation of common vulnerability classes on the most popular platforms has become increasingly difficult. Although vulnerabilities are still common, especially in client side applications, exploiting these vulnerabilities often becomes a complex matter of bypassing multiple protection mechanisms including stack cookies, heap verification, and data execution prevention. However, with the move towards miniaturization, products are increasingly giving up these protections and moving to largely untested platforms. Often the base libraries and operating systems chosen for these devices contain trivially exploitable vulnerabilities.

Several months ago I assessed a product which included a networked device based on Nut/OS. This minimal operating system describes itself as follows:

Nut/OS is an intentionally simple RTOS for the ATmega128, which provides a minimum of services to run Nut/Net, the TCP/IP stack. It's features include:
+  Non preemptive multithreading.
+  Events.
+  Periodic and one-shot timers.
+  Dynamic heap memory allocation.
+  Interrupt driven streaming I/O.

Main features of the TCP/IP stack are:
+  Base protocols ARP, IP, ICMP, UDP and TCP.
+  User protocols DHCP, DNS and HTTP.
+  Socket API.
+  Host, net and default routing.
+  Interrupt driven Ethernet driver.

While assessing the device, one of the most exposed components was the network stack. Some discussion of the network stack of this minimal operating system is in order. The vulnerability I will discuss has now been patched but the vulnerable version of the operating system can be downloaded here. An incoming IP packet is passed from the device driver into NutEtherInput() and on into NutIpInput() where it is demuxed to determine its protocol and passed to the appropriate component. Within NutIpInput(), on line 187 of net/ipin.c, the length of the IP header is calculated and used without being verified for sanity. The length of the IP header is a 4 bit value which is multiplied by 4 to determine the length in 32 bit words. Later on lines 250, 251, and 252 several lengths are calculated based on this value as well as the unchecked length for the entire packet. This vulnerability leads to a number of interesting conditions throughout the network stack where pointers to protocol headers and data are calculated based on incorrect IP header lengths.

void NutIpInput(NUTDEVICE * dev, NETBUF * nb) {
...
ip_hdrlen = ip->ip_hl * 4;
if (ip_hdrlen < sizeof(IPHDR)) {
NutNetBufFree(nb);
return;
}
...
nb->nb_nw.sz = ip_hdrlen;
nb->nb_tp.vp = ((char *) ip) + (ip_hdrlen);
nb->nb_tp.sz = htons(ip->ip_len) - (ip_hdrlen);

The most interesting of these, from the perspective of exploitation, are strangely in one of the simplest protocol handlers; namely ICMP. This arises largely from the fact that the buffers allocated for incoming echo requests are reused for the responses. The responses are sent through NutIcmpOutput() in net/icmpout.c. To exploit this we need data to be written to a pointer which can be pushed forward into another chunk of heap memory by specifying an incorrect IP header length. Only two writes meet these criteria. The first is the type field of the ICMP packet and in this case will always be NULL. Although it may be possible to gain execution in some cases with the ability to overwrite heap memory with a null, in this case, there is a more interesting alternative. The second field which is written is a checksum of the ICMP portion of the packet (which is data that we control at least parts of). So, by specifying an IP header length which is larger than the true length (typically 5) and controlling the calculated checksum, we can write an arbitrary 2 bytes to any 32 bit boundary within 9 (the largest IP header length 15 minus (the smallest IP header = 5 plus the size of the ICMP header = 1)) words of the end of our packet in memory.

int NutIcmpOutput(uint8_t type, uint32_t dest, NETBUF * nb) {
ICMPHDR *icp;
uint16_t csum;
icp = (ICMPHDR *) nb->nb_tp.vp;
icp->icmp_type = type;
icp->icmp_cksum = 0;
csum = NutIpChkSumPartial(0, nb->nb_tp.vp, nb->nb_tp.sz);
icp->icmp_cksum = NutIpChkSum(csum, nb->nb_ap.vp, nb->nb_ap.sz);
return NutIpOutput(IPPROTO_ICMP, dest, nb);
}

This leads us to another difficulty, the Nut/OS heap implementation (specifically the use of singley linked lists). I will go into this in more detail in another post but for now I want to talk about another vector for the exploitation of this vulnerability. In many cases the rather limited memory of an embedded device contains information that would be useful to an attacker. Things like encryption keys, and passwords are all stored in the same address space that the network stack is operating on. If you have been following along you may see where I am going with this. When we specify a packet length (not ip->ip_hl but instead ip->ip_len) in the IP header of an ICMP echo request that is larger than the actual packet sent, a condition results where the excess length for the echo response is pulled from the memory directly after the allocated buffer. By sending a ICMP echo request with no data and a long length we can effectively read chunks of memory from the vulnerable device. To obtain the maximum amount of memory it is possible, by forcing allocations and deallocations using particulars of the TCP stack, to change the location where the packet buffer is allocated.

Visualizing the attack with ninjas

Visualizing the attack with ninjas

By manipulating the heap it is possible to rebuild large sections of the vulnerable devices memory based on the data segment of the returned ICMP responses. In many cases this will give the attacker everything they need to further compromise the system. Even when no critical encryption key or password exists in memory which can be leaked this attack is extremely useful in helping to facilitate a more typical heap corruption exploit.

I want to touch briefly on the steps that can be taken by device manufacturers to avoid this type of vulnerability. It is not sufficient to assume that because it is an embedded device it will not be attacked. As the popularity of deploying this type of system on the internet continues to grow greater numbers of attackers will focus on these platforms simply because exploitation is often easier. When deploying internet enabled devices the same precautions should be taken as with more conventional platforms like Windows and Linux. During the design process, base libraries and operating systems should be vetted through security review prior to inclusion in a product. I expect to see much more research into these platforms as ethernet adapters and wireless interfaces are added to more and more devices.

3 thoughts on “Exploiting Embedded Devices (Part 1)

  1. I can definitely see how this vulnerability can lead to arbitrary memory read, though even that seems like it requires extensive manipulation to get heap pointers exactly where you want them. What I can’t see is how to do arbitrary memory writes. How is the heap laid out on this OS? Is it possible to hijack something akin to the flink pointer on Windows heap implementations? Furthermore, is 2 bytes enough to inject any kind of useful shellcode? Can this vulnerability be used to hijack flow control?

  2. I don’t want to go into detail about actually getting code execution yet but I can briefly discuss your questions.

    Pertaining to reading memory, you are right it isn’t absolutely trivial to get exactly what you want from memory. This is greatly facilitated though by the TCP implementation. Specifically, the way that the OS identifies the owning socket for a TCP datagram and how it treats high sequence numbers. Basically you can manipulate the heap fairly well if you can either create TCP connections to a socket (listening port on device) or even if you know about an existing outbound connection (a device connected to a server somewhere else).

    You can grab the source to the OS that I linked in the article (the heap implementation is quite simple and shouldn’t take long to grok) but basically the heap layout is two singley linked lists of these

    typedef struct _HEAPNODE {
    size_t hn_size; /*!< \brief Size of this node. */
    struct _HEAPNODE *hn_next; /*!< \brief Link to next free node. */
    } HEAPNODE;

    So hn_next is roughly equivalent to Window’s flink (windows heap is doubley linked though). It is more difficult than, for example, a win 2k heap overflow because you can not simply overwrite both the forward and backward ptrs to control both address to be written and the data to be written.

    The general idea, really any time you can overwrite a pointer, is to find a place where it is written (or preferably jumped to or called) and point it at something interesting at that point. Of course you also need to control the data which will be written to the pointer which is a bit more difficult in this case than in a standard doubley linked heap implementation (not to mention the fact that blowing out the heap will have pretty immediate and drastic consequences).

    I hope that makes some sense. Basically the two bytes you can overwrite in this context will be the address of some interesting memory which you would like to overwrite not a payload. The payload could be put into memory in any number of ways (the simplest is using TCP datagrams, in the way I discussed above, that you can force to be held indefinitely in memory). The trick is not payload injection but

    1. getting the payload executed and
    2. fixing the heap so the device doesn’t spectacularly crash

  3. Pingback: Interesting Information Security Bits for 11/18/2008 at Infosec Ramblings

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