[picoCTF] babygame01

Monitor In Secure☃️·2024년 3월 27일

wargame_pwn

목록 보기
2/11

위 사진처럼 게임같이 wasd로 위치를 이동시켜 X까지 도달하는 게임이다.

X로 이동시키면 'You win!'이라고만 나오고 게임이 끝나고 플래그는 출력되지 않는 것을 확인할 수 있다.
이를 통해 특정 위치로 이동을 해야 플래그값이 나오는 등의 다른 방법을 시도해야 할 필요성을 깨달아, 이를 ida로 한번 분석해보려고 한다.

[ida]


[디컴파일시킨 결과_main]

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+1h] [ebp-AA5h]
  int v5[2]; // [esp+2h] [ebp-AA4h] BYREF
  char v6; // [esp+Ah] [ebp-A9Ch]
  char v7[2700]; // [esp+Eh] [ebp-A98h] BYREF
  unsigned int v8; // [esp+A9Ah] [ebp-Ch]
  int *p_argc; // [esp+A9Eh] [ebp-8h]

  p_argc = &argc;
  v8 = __readgsdword(0x14u);
  init_player(v5);
  init_map(v7, v5);
  print_map(v7, v5);
  signal(2, sigint_handler);
  do
  {
    do
    {
      v4 = getchar(p_argc);
      move_player(v5, v4, v7);
      print_map(v7, v5);
    }
    while ( v5[0] != 29 );
  }
  while ( v5[1] != 89 );
  puts("You win!");
  if ( v6 )
  {
    puts("flage");
    win();
    fflush(stdout);
  }
  return 0;
}

v5[0], v5[1]은 각 행열의 위치를 나타냈다는 것을 유추해볼 수가 있고, 일치할 시 "You win!"이라는 문구가 뜨고 flag는 v6의 조건을 만족해야 뜬다는 것을 디컴파일 코드를 통해 확인해볼 수 있다.

[디컴파일시킨 결과_move_player 함수 코드 분석]

_BYTE *__cdecl move_player(_DWORD *a1, char a2, int a3)
{
  _BYTE *result; // eax

  if ( a2 == 108 )
    player_tile = getchar();
  if ( a2 == 112 )
    solve_round(a3, a1);
  *(_BYTE *)(a1[1] + a3 + 90 * *a1) = 46;
  switch ( a2 )
  {
    case 'w':
      --*a1;
      break;
    case 's':
      ++*a1;
      break;
    case 'a':
      --a1[1];
      break;
    case 'd':
      ++a1[1];
      break;
  }
  result = (_BYTE *)(a1[1] + a3 + 90 * *a1);
  *result = player_tile;
  return result;
}

이 코드에서 윗 부분에 조건문이 먼저 나타나게 되는데, a2가 108이 되면, 'player_tile'변수에 새로운 문자를 설정한 후에 getchar() 함수를 이용하여 사용자가 입력한 문자가 값으로 들어가게 된다.
또한 a2가 112면 solve_around(a3, al) 함수가 실행된다는 것을 확인할 수 있다.
여기서 조건에 있는 숫자 108, 112는 char 형식으로 나타나져있는 것을 확인하였기 때문에, 이를 아스키코드로 바꿔보면 각각 'l', 'p'라는 사실을 알 수 있다.

따라서 p를 입력하여 solve_around 함수가 실행되도록 해보았다.

-> You win! 만 나오고 플래그가 나오지 않았다.

main을 디컴파일한 것중에 win함수가 있어 살펴보니,

이렇게 나왔다.. 아무래도 플래그값은 nc서버로 접속해서 문제를 풀어야만 출력이 되는 것 같다.

아무리 봐도 다른 방법이 떠오르지 않아 구글링의 힘을 빌려봤다.
(이 원인을 이해하고 해결하는데 약 3일정도 걸렸다..)


이 문제는 버퍼(메모리) 구조가 어떻게 되어있는지 잘 파악해야하는 것과 p를 입력해야 게임을 이길 수 있는 것이 핵심이다.

위에 있는 메모리 구조를 보고 위치를 보면

v6 : var_A9C
v7[2700] : var_A98

이를 통해 v6와 v7 사이에 4byte 공간이 생성되어있다는 것을 알 수 있다.

move_player 함수 코드 중 switch문에 이동 범위를 지정해주지 않았기 때문에 v6에 접근이 가능해지게 된다는 취약점이 존재한다. 때문에 맨 처음 위치로 이동하고 a를 v6와 v7와의 차이인 4만큼 입력하게 되면 v6에 접근할 수 있게 되어 여기서 강제로 게임을 진행하고 이기게 만들기 위해 p를 누르면 게임이 이겨지면서 플래그가 도출하게 된다.

[플래그 도출과정]
1) 원점으로 이동

2) 4번 앞(a)으로 이동

3) 플래그 확인

[Reference]
https://www.ta-oot.page/posts/ctf-writeups/picoctf2023/babygame01/

0개의 댓글