Description
Break the Secret Holder and find the secret.
nc 52.68.31.117 5566
SecretHolder
TL;DR
- Free the chunk of others to create use after free.
- Overlapping three chunks to overwrite chunk headers.
- Unsafe unlink.
- Change
free
GOT entry toputs
PLT address to leaklibc
base. - Change
atoi
GOT entry tosystem
inlibc
to get the shell.
Exploit
Hey! Do you have any secret? |
The program allows us to conduct three kinds of operations on three different sizes of secrets.
Three operations:
- Keep secret:
calloc
corresponding size of memory and read some input to it. - Wipe secret:
free
the corresponding memory. - Renew secret: Overwrite the content of the corresponding memory.
Three secrets:
- Small secret: 40 bytes
- Big secret: 4000 bytes
- Huge secret: 400000 bytes
We can only keep at most one secret per size. Three pointers of the secret are store on the .bss
section with three flags which indicate whether the secret has been allocated.
The vulnerability comes from the wipe
function. The program will check whether the secret has been created in keep
and renew
but not in wipe
, so we can free
an already freed secret. free
a secret twice consecutively would only make the program crash due to the double free detection. However, if we keep another secret on the same address after the first free
, we can free
this new secret through the old pointer. Then, since the new secret doesn’t aware of this free
, we can still write data to it and cause an use after free vulnerability.
For example, if we invoke functions in the following sequence:keep('small') -> wipe('small') -> keep('big') -> wipe('small') -> keep('small')
The heap layout would be like:
We can now overflow the header of the top chunk. There is a technique call “House of force”, which is related to modify the header of the top chunk. However, it also requires the ability to malloc
arbitrary size, so this program is not the case.
I was stuck in here for a while until I accidentally found an interesting fact of heap. The huge secret is too large to fit in the main arena, so it supposed to be allocated by mmap
. It does call mmap
at the first time, however, if I free
the memory and malloc
again, the new memory chunk will surprisingly appear in the main arena. I can’t find the description of this property on related heap exploit document. Maybe I should check it out in the source code someday. Anyway, this property turns out to be the key point of solving this problem.
We first invoke functions as the previous example with additional keep(huge) -> wipe(huge) -> keep(huge)
. The heap layout would become:
Then, we are able to modify the content of the small secret and the header of the huge secret. These abilities plus the pointer of the secrets on .bss
section enable a classical attack call “unsafe unlink”. I create a fake freed chunk in the small secret, whose fd
and bk
points to small secret pointer - 0x18
and small secret pointer - 0x10
respectively, and change the PREV_INUSE bit of the huge secret to 0. After wiping the modified huge secret, the small secret pointer would point to a little offset before itself, allowing us to overflow three secret pointers and further get the arbitrary write.
Before modifying GOT entry to system
, we have to leak the base of libc
first. I achieve it by changing the free
GOT entry to the address of puts
in .plt
section and make the big secret pointer point to read
GOT entry. Then, calling wipe on the big secret would print the address of read
and leak the address of libc
(I guessed the libc
binary is the same as another pwn problem. libc.so).
Finally, we can change the atoi
GOT entry to system
, and input "sh"
to get the shell. The whole exploit is as follows.
1 | from pwn import * |
Flag:
hitcon{The73 1s a s3C7e+ In malloc.c, h4ve y0u f0Und It?:P}
Note
What I’ve learned:
The behavior of malloc
when the requested size is greater than the main arena limit.