Contributors

David Barksdale and Alex Wheeler

1. Background

Earlier this year we reported 3 vulnerabilities in VxWorks to Wind River. Each of these vulnerabilities can be exploited by anonymous remote attackers on the same network without user interaction to take control of the affected device. VxWorks is widely used in Aerospace and Defense, Automotive, Industrial, Medical, Consumer Electronics, Networking and Communication Infrastructure applications (https://en.wikipedia.org/wiki/VxWorks#Notable_uses).

2. Summary

As of this writing the flaws have not been assigned CVE numbers, they are:

  1. DHCP client heap overflow in handle_ip() affecting VxWorks 6.4 and prior
  2. DHCP server stack overflow in ipdhcps_negotiate_lease_time() affecting VxWorks 6.9 versions prior to 6.9.3.1, VxWorks 6.8, VxWorks 6.7, VxWorks 6.6, and VxWorks 6.5 and prior versions
  3. DNS client stack overflow in ipdnsc_decode_name() affecting VxWorks 7, VxWorks 6.9, VxWorks 6.8, VxWorks 6.7, VxWorks 6.6, and VxWorks 6.5

Please login to your support account on windriver.com or contact your Wind River support representative for mitigation of these issues.

3. Vulnerabilities

A. DHCP IP Address Option Client Heap Overflow

VxWorks 6.4 and prior fail to properly handle the lengths of IP addresses in DHCP Options in  handle_ip() and handle_ips().  handle_ip() contains a trivial overflow and will be the focus of this section. The flaw was initially found while auditing the network stack of IN_DISCLOSURE. Below is the disassembly describing the flaw in handle_ip() from the IN_DISCLOSURE firmware.

RAM:803D6D38 handle_ip: # DATA XREF: RAM:80F18AC8o
RAM:803D6D38            # RAM:80F18B04o ...
RAM:803D6D38
RAM:803D6D38 addiu $sp, -0x28
RAM:803D6D3C sw    $s3, 0x28+var_C($sp)
RAM:803D6D40 sw    $s2, 0x28+var_10($sp)
RAM:803D6D44 sw    $s0, 0x28+var_18($sp)
RAM:803D6D48 sw    $ra, 0x28+var_8($sp)
RAM:803D6D4C sw    $s1, 0x28+var_14($sp)
RAM:803D6D50 move  $s3, $a0
RAM:803D6D54 lb    $s1, 0($s3)
RAM:803D6D58 move  $s2, $a1
RAM:803D6D5C li    $v0, 0x36
RAM:803D6D60 beq   $s1, $v0, __copy_option__ # code == 36h
RAM:803D6D64 addiu $s0, $s2, 0x98
RAM:803D6D68 li    $v0, 0x20
RAM:803D6D6C beq   $s1, $v0, __copy_option__ # code == 20h
RAM:803D6D70 addiu $s0, $s2, 0xB8
RAM:803D6D74 li    $a0, 1 # num
RAM:803D6D78 jal   my_calloc # 4 byte buffer
RAM:803D6D7C li    $a1, 4 # size
RAM:803D6D80 move  $s0, $v0
RAM:803D6D84 beqz  $s0, __exit__ # calloc() == ERROR
RAM:803D6D88 li    $v0, 0xFFFFFFFF
<...SNIP...>
RAM:803D6E28 __copy_option__: # CODE XREF: handle_ip+28j
RAM:803D6E28 lbu   $a2, 1($s3) # len (1 BYTE FROM PACKET)
RAM:803D6E2C move  $a1, $s0 # dst (4 BYTE BUFFER)
RAM:803D6E30 jal   my_bcopy
RAM:803D6E34 addiu $a0, $s3, 2 # src (OptionPtr + 2)
RAM:803D6E38 move  $v0, $zero
RAM:803D6E3C __exit__: # CODE XREF: handle_ip+4Cj
RAM:803D6E3C lw    $ra, 0x28+var_8($sp)
RAM:803D6E40 lw    $s3, 0x28+var_C($sp)
RAM:803D6E44 lw    $s2, 0x28+var_10($sp)
RAM:803D6E48 lw    $s1, 0x28+var_14($sp)
RAM:803D6E4C lw    $s0, 0x28+var_18($sp)
RAM:803D6E50 jr    $ra
RAM:803D6E54 addiu $sp, 0x28
RAM:803D6E54 # End of function handle_ip

As described in the disassembly above, the vulnerability is caused by using a DHCP option length from the packet to copy into a 4 byte heap buffer, resulting in a heap overflow. This vulnerability can be exploited by responding to an affected device’s DHCP request with a malicious response containing a DHCP option length larger than 4 for the following DHCP option codes: 1, 16, 28, 32, and 54.

B. DHCP Option Lease Time Negotiation Server Stack Overflow

VxWorks 6.5 through VxWorks 6.9.3 fail to properly validate a lease time length when a DHCP server parses DHCP option 51 in ipdhcps_negotiate_lease_time(), which results in a stack overflow. The flaw is caused by using a DHCP IP Address Time option length from the packet to copy into a 4 byte stack buffer, resulting in a stack overflow. In either a DHCP Discovery or Request packet, the attacker simply includes an option of type 51 (the lease time option) that is larger than the expected 4 bytes. The entire contents of the option record (up to 255 bytes) will be copied into a buffer on the stack that is only 4 bytes.

C. DNS Response Decompression Stack Overflow

VxWorks 6.5 through VxWorks 7 fail to properly bound the decompression of names in ipdnsc_decode_name() which results in a stack overflow. The following is a snippet of the affected code for your review.

IP_STATIC Ip_s32
ipdnsc_decode_name(Ip_u8 *name, Ip_u8 *buf, Ip_u8 *start, Ip_u8 *end)
{
  Ip_u8 *ptr, *prev;
  Ip_s32 i, len, tot_len = 0, compress = 0;

  ptr = buf;
  while (*ptr && ptr < end)
  {
    /* Loop until we find a non-pointer */
    while ((*ptr & 0xc0) == 0xc0 && ptr < end)
    {
      prev = ptr;
      ptr = start + (IP_GET_NTOHS(ptr) & 0x3fff);
      if (ptr >= prev)
        return -1; /* Do not allow forward jumps (avoid loops) */
      if (!compress)
        tot_len += 2;
      compress = 1;
    }
    /* Store the length of the label */
    if (ptr >= end)
      return -1;
    len = *ptr++;
    if (len > IPDNSC_MAXLABEL)
      return -1;
    if (!compress) 
      tot_len = tot_len + len + 1;
    if (tot_len > IPDNSC_MAXNAME)
      return -1;
    /* Copy the label to name */
    for (i=0; i<len; i++)
    {
      if (ptr >= end)
        return -1;
      *name++ = *ptr++;
    }
    *name++ = '.';
  }

  if (!compress)/* Increment for the last zero */
    tot_len++;

  /* Null terminate the name string */
  if (tot_len)
    name--;
  *name = 0;
  return tot_len;
}

In the above code, the programmer fails to properly bound the decoded name to IPDNSC_MAXNAME when decompression is involved.  The only caller to this function, ipdnsc_parse_response(), passes the address of a 255-byte stack buffer as the output buffer name. When an attacker causes the target to process a DNS response with a name record that decompresses to larger than 255 bytes, the stack buffer will be overflowed.

4. Exploitation

Attack Vectors

All 3 vulnerabilities may be exploited by anonymous remote attackers on the same network as the target. Since the DHCP vulnerabilities are reachable over UDP and we found no TTL enforcement, in theory, an anonymous remote attacker may be able to exploit them while not on the same network by spoofing packets. Non-local network exploitation seems more plausible against the DHCP Option IP Lease Time Server Stack Overflow than the DHCP Option IP Client Heap Overflow – mainly because you need to guess the client’s 2 byte Transaction ID to trigger the client overflow (spoof, spray, and pray). The DNS Decompression Stack Overflow may be exploited by attackers that are on the same network, in control of a name server, or MITM between the target and a legit name server.

The remainder of this post discusses exploitation of the DHCP IP Option Client Heap Overflow. The stack overflows are left as an exercise for the reader.

Exploiting the Heap Overflow in handle_ip()

The DHCP client heap overflow occurs when parsing option records in the DHCP Offer packet normally sent to clients from a DHCP server during start-up and periodically afterwards. DHCP option records which correspond to IP address values (type 1, 16, 28, 32, and 54) are assumed to have a length of four bytes and the function which processes these options (named handle_ip) allocates a 4-byte buffer on the heap. However when copying the contents of the option record into the buffer, the function uses the length value in the option record for the number of bytes to copy. An attacker can provide up to 255 bytes to copy into the 4-byte heap buffer.

While we weren’t able to test all affected versions on all platforms, we were able to develop an exploit for two IP Deskphones from two different vendors both running VxWorks 5.5 on MIPS32.

In broad strokes our exploit needs to corrupt heap metadata in such a way that gives us control of execution, then it needs to flush our exploit code from the data cache to main memory (MIPS has separate data and instruction caches) so it can be executed, then it needs to jump to that code. The exploit code then needs to repair the heap and for convenience start an OS task that executes whatever payload we may have.

The heap allocator maintains a doubly-linked list of free chunks which it scans when allocating memory. The previous and next pointers are stored in the chunk header along with the size of the chunk, a flag indicating if the chunk is free or allocated, and a pointer to the previous chunk in memory.

Previous Chunk Free Chunk Next Chunk
Previous chunk pointer Chunk size and free flag Next free chunk pointer Previous free chunk pointer Data

Our exploit overwrites the previous and next pointers of a free chunk and then allocates that chunk. During allocation the free chunk is removed from the doubly-linked list, giving us the ability to write an arbitrary 4-byte value to an arbitrary location in memory. In order to get control of execution we overwrite the function pointer in the table of DHCP option handling functions for option type 48, then cause that function to be called by adding an option of that type to our DHCP Offer packet.

To accomplish this we need to arrange the heap so that our buffer is adjacent to a free heap chunk of a known size, overflow that chunk’s header, and then allocate that chunk. This turns out to be easier than it sounds. The following DHCP option list does the job in most cases:

    Code   Len
   +-----+-----+
   |  3  |  0  |
   +-----+-----+
   |  4  |  0  |
   +-----+-----+
   \\    \\    \\
   +-----+-----+
   | 11  |  0  |
   +-----+-----+
   |  1  |  0  |
   +-----+-----+---\\---+
   |  1  | 32  |  Data  |
   +-----+-----+---\\---+
   | 28  | ... |
   +-----+-----+
   | 48  | ... |
   +-----+-----+

Option codes 3-11 cause two small allocations from the heap each, this helps defragment the heap and makes it more likely that the next chunk on the free list is large enough for our next two allocations. Assuming the next free chunk is large enough, the heap allocator will split it into two smaller chunks and return the one at the end for our first option 1. When handle_ip() processes the second option 1 record, it will allocate the heap buffer (which will be before and adjacent to the one we just allocated), notice that a buffer for option 1 was already allocated and free it (adding it to the head of the free list), then write our 32 bytes of data into the buffer which overflows into the metadata of the first free chunk on the free list. Option 28 then allocates that corrupt chunk and in doing so overwrites the function pointer for handling option 48. Option 48 then calls that pointer and we have control of execution.

We will post more details about exploitation of this issue in the near future, after IN_DISCLOSURE have had a chance to publish a fix. If you have a VxWorks-based device and would like us to develop a PoC for it, please contact info@exodusintel.com with the details.

5. Detection

A. DHCP Option IP Address Client Heap Overflow

Detection of attempts to exploit this vulnerability can be accomplished by examining the length field of DHCP Option Codes 1, 16, 28, 32, and 54 for values greater than 4 in DHCP Offers.

B. DHCP Option Lease Time Server Stack Overflow

Detection of attempts to exploit this vulnerability can be accomplished by examining the length field of DHCP Option Code 51 for values greater than 4 in DHCP Discover and Request packets.

C. DNS Response Decompression Stack Overflow

Detection of attempts to exploit this vulnerability can be accomplished by examining names in DNS responses for compression that results in a decoding of a name to larger than 255 bytes.