QEMU escape: Part 3 Information Leakage (CVE-2015-5165)

Screenshot from 2018-03-08 11-14-48

Introduction

This post will give some more debugging details on CVE-2015-5165. Based on the poc code in [1], we make some modification to the code according to the information of local machine.
As we know, QEMU is an application running on the host machine. The goal of the VM escape in the guest machine is that we have to retrieve the base address of text segment of QEMU application on the host machine and the base address of the virtual memory that are mapped to emulate the physical memory of guest machine.
The final result of the information leakage is given as the cover image of this post.

Vulnerability Analysis

The vulnerable code is located in the /hw/net/rtl8139.c

uint8_t *saved_buffer  = s->cplus_txbuffer;
int      saved_size    = s->cplus_txbuffer_offset;
int      saved_buffer_len = s->cplus_txbuffer_len;

/* ip packet header */
ip_header *ip = NULL;
int hlen = 0;
uint8_t  ip_protocol = 0;
uint16_t ip_data_len = 0;

int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
if (proto == ETH_P_IP)
{
    DPRINTF("+++ C+ mode has IP packet\n");

    /* not aligned */
    eth_payload_data = saved_buffer + ETH_HLEN;
    eth_payload_len  = saved_size   - ETH_HLEN;

    ip = (ip_header*)eth_payload_data;

    if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
        DPRINTF("+++ C+ mode packet has bad IP version %d "
            "expected %d\n", IP_HEADER_VERSION(ip),
            IP_HEADER_VERSION_4);
        ip = NULL;
    } else {
        hlen = IP_HEADER_LENGTH(ip);
        ip_protocol = ip->ip_p;
        ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
	}
}

With the following debugging script:

set pagination off

break hw/net/rtl8139.c:2173
commands
x/10gx saved_buffer
p/x ip
x/8gx ip
p/x *ip
end

cont

Together with the debugging info:

Thread 3 "qemu-system-x86" hit Breakpoint 1, rtl8139_cplus_transmit_one (s=0x5644f0222140) at /home/dango/Security/qemu/hw/net/rtl8139.c:2173
warning: Source file is more recent than executable.
2173	                if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
0x7fd094060980:	0x5452563412005452	0x0045000856341200
0x7fd094060990:	0x06400040adde1300	0xa8c0010108c0adde
0x7fd0940609a0:	0xfecaefbeadde0201	0x1050bebafecabeba
0x7fd0940609b0:	0x00000000addeadde	0x0000000000000000
0x7fd0940609c0:	0x0000000000000000	0x0000000000000000
$1 = 0x7fd09406098e
0x7fd09406098e:	0x0040adde13000045	0x010108c0adde0640
0x7fd09406099e:	0xefbeadde0201a8c0	0xbebafecabebafeca
0x7fd0940609ae:	0x0000addeadde1050	0x0000000000000000
0x7fd0940609be:	0x0000000000000000	0x0000000000000000
$2 = {ip_ver_len = 0x45, ip_tos = 0x0, ip_len = 0x1300, ip_id = 0xadde, ip_off = 0x40, ip_ttl = 0x40, ip_p = 0x6, ip_sum = 0xadde, ip_src = 0x10108c0, ip_dst = 0x201a8c0}

At this point, we can observe that the variable saved_buffer is actually the malformed packet sent in our exploit.
According to the macro defined as below

#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len) & 0xf) << 2)

We can find that the value of variable ip_data_len will be 0xffff since it is of type uint16_t.

Furthermore, variable ip_data_len is later used to compute the length of TCP data that are copied into a buffer:

int tcp_data_len = ip_data_len - tcp_hlen;
int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;

int is_last_frame = 0;

for (tcp_send_offset = 0; tcp_send_offset = tcp_data_len) {
        is_last_frame = 1;
        chunk_size = tcp_data_len - tcp_send_offset;
    }

    memcpy(data_to_checksum, saved_ip_header + 12, 8);

    if (tcp_send_offset) {
        memcpy((uint8_t*)p_tcp_hdr + tcp_hlen,
                (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset,
                chunk_size);
    }

    /* more code follows */
}

With the following debugging script:

set pagination off

break hw/net/rtl8139.c:2243
commands
p/x tcp_data_len
cont
end

break hw/net/rtl8139.c:2267
commands
p/x tcp_send_offset
p/x chunk_size
end

cont

Together with the debugging info:

Thread 3 "qemu-system-x86" hit Breakpoint 1, rtl8139_cplus_transmit_one (s=0x5644f0222140) at /home/dango/Security/qemu/hw/net/rtl8139.c:2243
2243	                    for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
$1 = 0xffeb

Thread 3 "qemu-system-x86" hit Breakpoint 2, rtl8139_cplus_transmit_one (s=0x5644f0222140) at /home/dango/Security/qemu/hw/net/rtl8139.c:2267
2267	                            memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
$2 = 0x5b4
$3 = 0x5b4

We can find that the actual length to be sent it 0xffeb, which will leak almost 64KB data into the buffer.
The next problem is how we are going to get the base address of text segment and mapped PHY_MEM.

Information Disclosure

In my exploit, my strategy to leak the base address of text segment and the base address of PHY_MEM is different from the strategy in [1].
To leak the base address of text segment, I did not seek to search for a complete object of class Object_Property in memory. Instead I turn to search for one specific function pointer, which is supposed to be a member variable of object of class Object_Property. In my exploit, I pick function property_get_str to leak the base address.
To leak the base address of text segment, I get the base address of PHY_MEM based on the weak ASLR provided by the system. According to my multiple trials, the 12th least significant byte of the base address of PHY_MEM is 7. More importantly, all addresses that satisfy this condition point belong to next adjacent heap of PHY_MEM.

Based on the observations discussed as above, I rewrite the code of original exploit and get the final result as demonstrated in the cover image.

Exploit

The full code of my exploit is given on my github repository [2].

Conlusion

Though I finally rewrite the exploit based on my understanding on the vulnerability, but I do not think that I have a deep understanding on how QEMU emulates the drive operation in the guest machine. I think I need to refer to the source code of Linux Kernel and give a deep discussion on QEMU internals.

Reference

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

Leave a comment

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