Who doesn’t love IoT? Based on real life. Flag is on /flag.txt . (QEMU running on Ubuntu 16.04 latest for setting environment)

nc internet-of-seat.pwn.seccon.jp 1337

500 Pts, 0 solves, pwn. files.zip

I didn’t have enough time to solve this during the CTF, but it looked interesting so decided to try it afterwards when I had a bit more time on my hands.

We are given a zip with a set of files, one of which is a MIPS binary and the others for lauching a qemu system.

$ ls files
initramfs.cpio.gz
main
wrapper.py
xinetd.conf
zImage

A bit strange as it’s launching an arm system and the httpd server runing is a 32 bit arm so not sure why we were given a MIPS binary, I ended up just ignoring it.

$ file httpd
httpd: ELF 32-bit LSB executable, ARM, version 1 (ARM), dynamically linked, interpreter /lib/ld-uClibc.so.0, not stripped

The wrapper finds a free port, starts up qemu and forwards that port to the guest httpd server. After a bit of investigation, it looks like a very simple server with three routes, /, /version and /echo. We can make requests to the server, and we also get the debug messages from qemu which is great as they contain the heap address used for the request.

echo -e "GET /version HTTP/1.0\n\n\n"| nc internet-of-seat.pwn.seccon.jp 41793
HTTP/1.1 200 OK
Content-Length: 5
X-Powered-By: ios-daemon 0.0.1

0.0.1
---- qemu ---
login[237]: root login on 'tty1'
Listening on 8888...
recv: 00004 (0x4015743c) -> 0x00000064 B

After a fair bit of RE to try and work out how everything works and a bit of random testing, I came across an issue in process_chunked when dealing with the chunk data:

realloc(request->chunkData, chunkSize);
...
if ( bodySize >= request->chunkSize )
      bodySize = request->chunkSize;
memcpy((request->chunkData + request->chunkStart), (bodyStart + request->body), bodySize);

So if we add a few extra headers to increase chunkStart we can overwrite anywhere past the current chunk.

The binary uses uClibc and so the heap is a bit simpler than what we are used to to. The initial heap storage is actually allocated inline which is great for us as we can overwrite __malloc_heap! It’s a simple structure that is stored at the end of the arena and looks like this:

struct heap_free_area
{
	size_t size;
	struct heap_free_area *next, *prev;
};

So we can overwrite size with a large number to make the next alloc return any lower address, like the GOT. All the pages ar rwx was well, so I decided to overwrite memchr with the address of our buffer and put the shellcode there. The only other slight hurdle was making sure the shellcode worked with ARMv6.

SECCON{5ea4f1ee2820cf8d6151937236f8f69e}

Full exploit