Buffer Overflows

GCC 2022

boat chick

What's been covered

x86-64 assembly, linux reverse-engineering!

Time to weaponise our knowledge

What is exploitation?

Binary exploitation is a broad topic within cybersecurity that comes down to finding a vulnerability in the program and exploiting it to ... modify the program’s functions

~infosecwriteups.com

A vulnerability as old as time

Let's try to understand

Buffer overflows

Revisiting the stack

Example #1

Example #2

With the stack, each function call maintains its local state!

function stacks

Are locals protected from each other?

Consider the following:


int foo(int index, int val) {
    int arr[4] = {1, 2, 3, 4};
    int untouched = 0x1337;
    arr[index] = val;
    return untouched;
}

void main() {
    foo(0, 0xdead);
}
					

Function locals

What if we went out of bounds?


int foo(int index, int val) {
    int arr[4] = {1, 2, 3, 4};
    int untouched = 0x1337;
    arr[index] = val;
    return untouched;
}

void main() {
    foo(7, 0xdead);
}
					

Out of bounds locals

Hold on, this isn't a buffer overflow

Consider the following:


int main() {
    char buf[16];
    int untouched = 0xdead;
    gets(buf);
    return untouched;
}
					

Buffer overflow (variable)

Pretty cool!

What happens when we overwrite saved rip

Code


int foo(int index, int val) {
    int arr[4] = {1, 2, 3, 4};
    int untouched = 0x1337;
    arr[index] = val;
    return untouched;
}

void main() {
    foo(?, 0xdead);
}
					

Overwrite saved rip (OOB)

Code


int main() {
    char buf[16];
    int untouched = 0xdead;
    gets(buf);
    return untouched;
}
					

Overwrite saved rip (Buffer)

Where to now?

Any h4x in real life?

CVE-2015-7504

Heap-based buffer overflow in the pcnet_receive function in hw/net/pcnet.c in QEMU allows guest OS administrators to cause a denial of service (instance crash) or possibly execute arbitrary code via a series of packets in loopback mode.

QEMU is a free and open-source hypervisor

Normally used to run different guest operating systems, e.g. IOT firmware, Linux

emulating other CPU architectures, e.g. ARM, MIPS

Vulnerability in the AMD PCNET network card emulator

This piece of code in pcnet_receive appends a CRC checksum to the end of a packet.


uint8_t *src = s->buffer;	/* this is the packet */

/* ... */
else if (s->looptest == PCNET_LOOPTEST_CRC ||
			!CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {     /* just one of these conditions need to be true */
	uint32_t fcs = ~0;              /* the accumulated checksum */
	uint8_t *p = src;

	/* go through the packet byte by byte, and keep updating the checksum */
	while (p != &src[size])         /* stop after reading up to the given size */
		CRC(fcs, *p++);               /* update fcs with the CRC checksum, also increments p */

	*(uint32_t *)p = htonl(fcs);    /* save the checksum at the end of the packet */

	size += 4;
}
					

Hmm, everything looks ok so far?

PCNetState structure


struct PCNetState_st {
	NICState *nic;
	NICConf conf;
	QEMUTimer *poll_timer;
	int rap, isr, lnkst;
	uint32_t rdra, tdra;
	uint8_t prom[16];
	uint16_t csr[128];
	uint16_t bcr[32];
	int xmit_pos;
	uint64_t timer;
	MemoryRegion mmio;
	uint8_t buffer[4096];          /* the packet */
	qemu_irq irq;                  /* can we write the checksum into here 🤔 */
	void (*phys_mem_read)(void *dma_opaque, hwaddr addr,
							uint8_t *buf, int len, int do_bswap);
	void (*phys_mem_write)(void *dma_opaque, hwaddr addr,
							uint8_t *buf, int len, int do_bswap);
	void *dma_opaque;
	int tx_busy;
	int looptest;
};
					

This is the structure of s in the previous slide

Btw, the program accepts a packet (buffer) with size of 4kB (4096 bytes) 😏

IRQState structure


typedef void (*qemu_irq_handler)(void *opaque, int n, int level);

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

typedef struct IRQState *qemu_irq;
					

struct PCNetState_st {
	/* ... */
	uint8_t buffer[4096];          /* the packet */
	qemu_irq irq;                  /* can we write the checksum into here 🤔 */
	/* ... */
};
					

qemu_irq is a pointer to an IRQState

Importantly, there is a function pointer (qemu_irq_handler handler) in an IRQState

When is handler used?

A few times... e.g. pcnet_receive --> pcnet_update_irq --> qemu_set_irq


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

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

So, what if we can control handler?

To summarize what is going on

Attacker sends a packet of 4096 bytes


/*
*     pcnet_receive()
* appends checksum to end of Buffer
*           |
*           |
* if buffer size is 4096
*           |           PCNetState_st
*           |             +--------+
* write the checksum      |  ...   |
*    out-of-bounds        +--------+
*           |             | buffer |
*           |             +--------+
*           +------------>|  irq   +----+        IRQState
*                         +--------+    |       +---------+
*                         |  ...   |    |       |   ...   |
*                         +--------+    |       +---------+
*                                       +------>| handler |
*                                               +---------+
*                                               |   ...   |
*                                               +---------+
*/
					

The irq field is overwritten

To summarize what is going on

The attacker can create a fake IRQState somewhere in memory. Then, set the packet contents so that its checksum is the address of the fake IRQState


/*
*     pcnet_receive()
* appends checksum to end of buffer
*           |                                     Fake IRQState
*           |                                    +--------------+
* if buffer size is 4096                         |     ...      |
*           |           PCNetState_st            +--------------+
*           |             +--------+    +------->|   handler    |
* write the checksum      |  ...   |    |        +--------------+
*    out-of-bounds        +--------+    |        |     ...      |
*           |             | buffer |    |        +--------------+
*           |             +--------+    |
*           +------------>|  irq   +----+        IRQState
*                         +--------+    x       +---------+
*                         |  ...   |    x       |   ...   |
*                         +--------+    |       +---------+
*                                       +------>| handler |
*                                               +---------+
*                                               |   ...   |
*                                               +---------+
*/
					

The handler field is now attacker-controlled

To summarize what is going on

At the end of pcnet_receive, it calls pcnet_update_irq, which calls qemu_set_irq, which calls the attacker-controlled handler


/*
*                                                                            pcnet_receive
*                                                                                 |
*     pcnet_receive()                                                             v
* appends checksum to end of buffer                                         pcnet_update_irq
*           |                                     Fake IRQState                   |
*           |                                    +--------------+                 v
* if buffer size is 4096                         |     ...      |            qemu_set_irq
*           |           PCNetState_st            +--------------+                 |
*           |             +--------+    +------->|   handler    |<----------------+
* write the checksum      |  ...   |    |        +--------------+
*    out-of-bounds        +--------+    |        |     ...      |
*           |             | buffer |    |        +--------------+
*           |             +--------+    |
*           +------------>|  irq   +----+        IRQState
*                         +--------+    x       +---------+
*                         |  ...   |    x       |   ...   |
*                         +--------+    |       +---------+
*                                       +------>| handler |
*                                               +---------+
*                                               |   ...   |
*                                               +---------+
*/
					

Attacker now has arbitrary code execution

Erm, but how does the attacker know the address of the fake IRQState

Need to chain this bug with a memory leak vulnerability, e.g. out-of-bounds read/copy

For more detailed info, check out this Phrack article