Rating:
## The Bug & The Exploit
Contact is a struct that contains 2 pointers - One for the name and one for the bio.
This is the function that creates a new contact:
```C
void create_contact(char *name){
if (num_contacts == MAX_CONTACTS){
puts("Too many contacts! Delete one first!");
return;
}
struct contact *contact = (struct contact *)malloc(sizeof(struct contact));
if (contact == NULL){
puts("Could not allocate new contact.");
exit(-1);
};
/* make a copy of the name on the heap */
contact->name = strdup(name);
if (contact->name == NULL){
puts("Could not duplicate name.");
exit(-1);
}
contacts[num_contacts++] = contact;
}
```
There is no initialization of the bio field, which means it will still have the value this heap chunk previously had. It was only after seeing the flag that I realized I should've used double free, which would make things easier for me.
I used the combination of create_contact and delete_contact in order to control the bio pointer of new contacts.
```C
void delete_contact(struct contact *contact){
free(contact->name);
/* if the bio is set, free it as well */
if (contact->bio != NULL){
free(contact->bio);
}
free(contact);
/* replace the corresponding index with the last contact and decrement num_contacts */
for (int i = 0; i < num_contacts; i++){
if (contacts[i] == contact){
contacts[i] = contacts[num_contacts - 1];
num_contacts--;
break;
}
}
}
```
Think about the following scenario:
1. Create a new chunk with a name the size of contact struct (0x10) and set its second qword to an arbitrary pointer.
2. Set it's bio to a new chunk of the same size as the contact struct.
3. Delete it. Free order is name, bio, chunk.
4. Add a new chunk with a name the size of the contact struct.
```new_chunk = previous_chunk, new_name = previous_name.```
5. Add a new chunk.
```new_chunk = previous_name.```
As we said before, the bio field is not initialized, which means it will contain the value that was previously stored here - name's second qword (a pointer of our choice).
We can now use print_contacts to print the value pointed by our pointer!!
Let's use it to leak an address in libc (using the GOT_PLT table) and a heap address (using the contacts array).
We can use our leaked heap address in order to allocate a fake chunk in the heap (forge a chunk header in the contents of one of the chunks we allocate) and perform a fastbin attack to hijack __malloc_hook.
By the time we get the pointer to __malloc_hook-0x13 from malloc, it's over - We just need to overwrite it with one_gadget and enjoy our shell :)