TokyoWestern CTF 2018 BBQ
1 September 2018BBQ — Tokyo Western CTF 2018
Hi, I am Ne0. Long time no see. Last weekend I played Tokyo Western CTF as a member of r3kapig. Obviously the quality and difficulty of this year’s challenges got great improvement. When I got the first blood of BBQ
, I thought that no more than 3
team can solve this challenge , but there are 4 in the end.. orz.. Thanks my teammates for carrying me!
Program Info
This program is simple : You can buy food ,grill it and eat it ,and of course , say bye-bye.
Today is BBQ Party!
1. Buy
2. Grill
3. Eat
0. Break up
Choice:
Details can be found in the binary.
The food and cooked food structure is like this:
struct Food{
struct Food* next;
size_t amount;
char* name;
};
struct Cooked_food{
struct Food* food;
unsigned long useless;
unsigned long tag;
};
Vulnerabilities
The old binary has two bugs , one is out of bound and the other is uninitialized stack pointer , while the new binary has only the latter. As it is the uninitialized stack pointer bug that matters, let’s just focus on it.
Out of bound
printf("griddle index >> ", v0);
v3 = getint();
if ( cook_list[v3] )
{
puts("now cooking...");
}
// and others....
Uninitialized stack pointer
struct Cooked_food *ptr; // [rsp+8h] [rbp-28h]
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v4 = __readfsqword(0x28u);
show_cook_list();
printf("griddle index >> ");
v2 = getint();
if ( cook_list[v2] )
{
ptr = cook_list[v2];
puts("found food.");
}
else
{
puts("empty..."); // if goes here, the ptr will be uninitialized....ooops...
}
if ( ptr )
{
v0 = ptr->tag;
if ( v0 == 0xBADF00D22LL )
{
puts("I don't want to eat charcoal...");
}
else if ( v0 == 0xDEADBEEF11LL )
{
ptr->tag = 0xCAFEBABE33LL;
free(ptr);
puts("Yummy!");
cook_list[v2] = 0LL;
}
}
And after some debugging , I found that the ptr
can be one of the following:
- a file structure pointer
- if
grill()
is called, it will be the newly allocateCooked_food
structure pointer. - if
buy
is called, it will be the name of the food if the name is longer than40
bytes.
The first one is useless , but with the last two, we can own the world!
Exploit Steps
- Create a freed
0x20
sized chunk. Likegrill
andeat
- call
grill()
, andptr
will be a newly allocated chunk. - call
buy
, input a name that is 40-byte long. This partial-overwrites the lowest byte ofptr
, making it point to a food name. - call
grill()
to freeptr
, then the first 8 bytes of the freed food name chunk now has afd
pointer. That leaks the heap address. - With leaked heap address, we can now free arbitrary address, as long as the
address+0x10
contains the stupid tag0xDEADBEEF11
- Fake a
0xe0
sized chunk ,and free it , leaving some libc pointers in the heap. - Try making a name pointer point to these libc pointers, and leak it . Libc address get~~
- With libc address , we can fake a
Food
structure in&__malloc_hook-0x18
. And by changing the amount of the food, we can leave a0x21
in&__malloc_hook-0x10
- Now it is obvious that we should use
fastbin attack
to modify thefd
of a0x20
sizedfastbin
chunk to point to&__malloc_hook-0x18
But this is not as easy as it looks, because the only thing we can control is the name ofFood
, and the name can’t contain any null bytes ! LOL - So my approach is : Fake a
Food
structure inmain_arena
! We only need to free a0x30
sized chunk and a0x20
sized chunk , and make them to be the count and the name of the fakeFood
,leavingnext
be empty. Then change the amount ,make it point to somewhere in the heap that we can control. And thefastbin
looks likemodified_fd---->somewhere_controled---> &__malloc_hook-0x18
- Finally! Use
fastbin
attack to modify the__malloc_hook
toone_gadget
. Pwn!!!
The final script can be found in here. If you like the writeup , please follow me on github !
Conclusion
The logic of this program is really simple ,and the vulnerability is also obvious. But it is extremely hard to exploit…. Maybe there are simpler ways? If you have better ways to exploit it , please share it with me ^_^.