I participated in the Aliyun CTF competition recently and solved an interesting challenge based on CVE-2020-11102, which is a vulnerability in qemu that allows guest OS to escape and execute arbitrary code on the host OS. In this article, I would like to share some detail about the challenge and what I learned from it.
The vulnerability
Take a look at tulip_copy_tx_buffers() first. It copies the desc->buf_addr1 to the tx_frame + s->tx_frame_len. Notice that there is no check for the s->tx_frame_len as well as the len1. And s->tx_frame_len will be increased by len1 after copying. When we call this function multiple times, the s->tx_frame_len can be increased to a very large value, which can cause a buffer overflow.
The same applies to tulip_copy_rx_bytes().
The tulip_copy_rx_bytes() function copies the s->rx_frame to the desc->buf_addr1. And there is no check for the s->rx_frame_len and s->rx_frame_size. This results in possible memory disclosure.
Exploitation
Leak something first
To leak QEMU base address and heap address we need to control s->rx_frame_size and s->rx_frame_len. Consider the following code:
The tx_frame is a fixed size buffer, which is 2048 bytes. By triggering tulip_copy_tx_buffers() multiple times, we can control tx_frame_len, rx_frame_len and rx_frame_size. Then we can call tulip_copy_rx_bytes() and copy the heap memory back to the user space of the guest OS. With some calculation, we can retrieve the QEMU base address and heap address very easily.
After leaking the memory, we need to figure out how to get arbitrary code exection within the context of QEMU. This piece of code caught my attention:
What if we can control the function pointer in tulip_ops? Unfortunately, the tulip_ops is not writable.
When initializing the memory region of TULIPState, the pointer to tulip_ops will be assigned to struct MemoryRegion.ops.
And struct MemoryRegion is allocated on the heap. So we can overwrite the struct MemoryRegion.ops with the address of tx_frame and craft a fake struct MemoryRegionOps.
Also, notice that the type of tx_frame_len, rx_frame_len and rx_frame_size are all int, which means we can write backward if we overwrite these fields with negative value.
Exploitation
The exploitation should be pretty straightforward and the comments in the code should be self-explanatory.