Pwn・Exploitを成し遂げる上での最終目標のメモ

adventar.org

の12日目と

adventar.org

の11日目(時間逆行)です。

これはPwnをやっていくうえでの @megumish の雑なメモになります。

このドキュメントは amd64 ( x86_64 とも呼ぶ)アーキテクチャのExploitのメモということで書いていきます。

プログラム・カウンタのレジスタの書き換え方

Pwnをする上で最終的にしなければならないことは

システムコールを使って、 execve("/bin/sh", NULL, NULL)を実行することです。

これをするためには複数のレジスタを書き換えなければいけません。

この中でもとくに重要になってくるのが、プログラム・カウンタのレジスタを書き換えることです。

プログラム・カウンタのレジスタを書き換える方法は命令レベルで見ると amd64 では次の2つに大別されます。

  • ret 命令を使ってrspの指すポインタの値をプログラム・カウンタに代入する
  • call 命令を使って任意のレジスタまたはメモリの値をプログラム・カウンタに代入する

またこれらの命令は高級言語からの視点でみると下のように考えることもできます。

  • 関数の呼び出し元アドレスを書き換える
  • vtable または hook に登録してある関数を書き替える。

関数の呼び出し元アドレスを書き換える

この手法のことをPwnではROP(Return Oriented Programming)と呼びます。

retを用いて、バイナリ中の様々な命令の塊(これをガジェットと呼ぶ)を呼び出すことで、任意の命令を実行可能にする手法です。

また、このROPを実行するための方法は次の2つに大別できます。

  • rspレジスタを書き換える
  • rspの指しているアドレスの領域を書き換える

この2つの違いはrspを直接書き換えるか、戻り先アドレスのメモリを書き換えてしまうかの違いです。

vtableまたはhookなどに登録してある関数を書き換える

この手法は様々な場面で用いられます。この手法をある程度使いこなすことができれば、Pwnがそこそこできるようになります。

関数が登録されているアドレスは色々なところにありますが、代表的な例としてはGOT領域があります。

一度、呼びだされたライブラリ関数をGOT領域に登録しておくことで、呼び出し(というかバイナリの紐付)のコストを減らすものです。

こういった関数アドレスが書かれた領域は以下になります。(すべて挙げられているわけではないので指摘してくださるとありがたいです。)

  • バイナリのセキュリティ機構がFull RELROで無い場合のGOT領域
  • バイナリが動的に呼び出すアドレスを決定している場合の呼び出し先のアドレス(書き換え可能の時)
  • _IO_FILE_plus 構造体のインスタンスvtable_IO_list_all の連結先にある時)
  • C++ のクラスのインスタンスvtable (何らかの形で呼び出されることがわかっている時)
  • __*_hook 系のアドレス( __malloc_hook__realloc_hook など)

用いる脆弱性

さて上記のような方法を実際に用いていくためには使う脆弱性があります。

これも先程と同じようにリスト形式で並べていきます。

まとめ

以上のような手法と脆弱性を利用して、Pwnをすることができます。

セキュリティ機構については書いていませんが、セキュリティ機構はPwnのバイナリの中に張り巡らされる数ある縛りの中の数種類に過ぎないので

そのときそのときで対処していくのがいいかと思われます。

以上がメモになります。

最後に宣伝をしておきます。

僕の所属するチームHarekazeの開催するCTFが2018年2月に開催されます。 よろしくお願いします。

ctftime.org: https://ctftime.org/event/549 公式ページ: https://harekaze.com/ctf-jp.html