Solution

In this level, the goal is to overwrite the content of the GOT entry of printf() (in fact puts()) with the memory address of winner()
The program creates 3 values on the heap which are the same size (that’s important). Then it copies the three arguments given to the program with these values.
Each value can be overwrited because the len of the arguments isn’t checked. And finally the program frees() the values from the last to the first one.

At this point, it is clear that I need to overwrite a value for it to change the heap header of the next one (by something which says to free() that the next chunk is free) following by a fake chunk. So, when free() is called on this value, it is fooled and it calls unlink(). Unlink() is applied to the fake chunk. This allows me to overwrite the content of a memory address with whatever I want. As I will not give much details, Here are good links to read :

As said in the documentation above, a free chunk has a FD pointer (forward free chunk) and a BK pointer (backward free chunk). During the unlink() process, FD is copied to the memory location of BK+8 and BK is copied to the memory location of FD+12. Knowing that, I can’t put the GOT entry of puts() to FD and the memory address of winner() to BK. Otherwise, the content of GOT puts() will be overwrited (that’s good) by winner() but winner() will be overwrited by GOT puts()…

In BK, I need to use a shellcode as follows : JMP+NOPs + PUSH winner() + RET
I just need to store this shellcode in one of the three values (a, b, c).

To accomplish this challenge, I need to store in the three values the following content :

Now, the value of ‘a’ starts with a JMP because during the unlink FD is copied to BK+8:

<free+226>: mov DWORD PTR [eax+0x8],edx ; BK->fk = FD

Here is how the memory location of ‘a’ will look like in theory :

0x0804c000: 00000000 00000029 41414141 41414141
0x0804c010: 0804b11c

A JMP instruction takes 2 bytes, so I need a JMP of 10 bytes to jump over the FD address

metasm > jmp $+10
"\xeb\x08"

This instruction is following by 10 Nops. After that, I need to push the memory address of winner() :

metasm > push 0x08048864
"\x68\x64\x88\x04\x08"

Finaly, I need a RET instruction :

metasm > ret
"\xc3"

To fulfill ‘a’, I need 32 bytes. I’ve got so far 18 bytes (JMP+10NOPs+PUSH winner()+RET), so I complete with 14 « A ». (don’t use NOPs here)
Now, I need to overwrite the heap header of ‘b’. The first four bytes of the heap header is the prev_size and the fourth others are the size.
The last two bits of size are used to know if the next chunk is in use and if it was allocated via mmap. Those last two bits need to be set to 0.

size needs to be negative (-4) to avoid null byte.
size is 0xfffffffc

prev_size also needs to be negative because of this in Chunk_free() :

p = chunk_at_offset(p, -(long)prevsz);

So it determines the memory location of the free chunk by soustracting its location by the previous one. If I want to move forward in ‘b’, I need a negative value of -8 (size of the header of ‘b’).
prev_size is 0xfffffff8