BROP - Blind Return Oriented Programming

HackTricksをサポートする

基本情報

この攻撃の目的は、脆弱なバイナリに関する情報なしにバッファオーバーフローを介してROPを悪用できるようにすることです。 この攻撃は次のシナリオに基づいています:

  • スタックの脆弱性とそれをトリガーする方法の知識。

  • クラッシュ後に再起動するサーバーアプリケーション。

攻撃

1. 脆弱なオフセットを見つける サーバーの異常が検出されるまで1文字追加で送信する

2. カナリアをブルートフォース して漏洩させる

3. スタック内の保存されたRBPとRIP アドレスをブルートフォースして漏洩させる

これらのプロセスに関する詳細情報は、こちら (BF Forked & Threaded Stack Canaries)およびこちら (BF Addresses in the Stack)で見つけることができます。

4. 停止ガジェットを見つける

このガジェットは基本的に、ROPガジェットによって何か興味深いものが実行されたことを確認するためのもので、実行がクラッシュしなかったことを示します。通常、このガジェットは実行を停止するものであり、特定のROPガジェットが実行されたことを確認するためにROPチェーンの最後に配置されます。

5. BROPガジェットを見つける

この技術はret2csuガジェットを使用します。これは、いくつかの命令の途中でこのガジェットにアクセスすると、**rsirdi**を制御するガジェットが得られるためです:

これらがガジェットです:

  • pop rsi; pop r15; ret

  • pop rdi; ret

これらのガジェットを使用すると、関数を呼び出すための2つの引数を制御することが可能です。

また、ret2csuガジェットは非常にユニークなシグネチャを持っていることに注意してください。なぜなら、スタックから6つのレジスタをポップするからです。したがって、次のようなチェーンを送信します:

'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP

STOPが実行される場合、これは基本的にスタックから6つのレジスタをポップするアドレスが使用されたことを意味します。または、使用されたアドレスもSTOPアドレスであったことを意味します。

この最後のオプションを排除するために、次のような新しいチェーンが実行され、前のものが6つのレジスタをポップしたことを確認するためにSTOPガジェットを実行してはなりません:

'A' * offset + canary + rbp + ADDR

ret2csuガジェットのアドレスを知っていると、rsirdiを制御するガジェットのアドレスを推測することが可能です。

6. PLTを見つける

PLTテーブルは0x400000から、またはスタックからの漏洩したRIPアドレスから検索できます(PIEが使用されている場合)。テーブルのエントリ16B(0x10B)で区切られており、1つの関数が呼び出されると、引数が正しくなくてもサーバーはクラッシュしません。また、PLTのエントリのアドレス + 6Bもクラッシュしません。これは最初に実行されるコードです。

したがって、次の動作を確認することでPLTテーブルを見つけることができます:

  • 'A' * offset + canary + rbp + ADDR + STOP -> クラッシュしない

  • 'A' * offset + canary + rbp + (ADDR + 0x6) + STOP -> クラッシュしない

  • 'A' * offset + canary + rbp + (ADDR + 0x10) + STOP -> クラッシュしない

7. strcmpを見つける

strcmp関数は、比較される文字列の長さをレジスタrdxに設定します。rdxは3番目の引数であり、後でwriteを使用してプログラムを漏洩させるためには0より大きい必要があります。

**strcmp**のPLT内の位置を、その動作に基づいて見つけることが可能です。これは、関数の最初の2つの引数を制御できることを利用します:

  • strcmp(<非読み取りアドレス>, <非読み取りアドレス>) -> クラッシュ

  • strcmp(<非読み取りアドレス>, <読み取りアドレス>) -> クラッシュ

  • strcmp(<読み取りアドレス>, <非読み取りアドレス>) -> クラッシュ

  • strcmp(<読み取りアドレス>, <読み取りアドレス>) -> クラッシュしない

これは、PLTテーブルの各エントリを呼び出すか、PLTスローパスを使用して確認できます。これは基本的にPLTテーブルのエントリ + 0xbdlresolveを呼び出す)を呼び出し、スタック内に調査したいエントリ番号(ゼロから始まる)を配置して、最初のエントリからすべてのPLTエントリをスキャンします:

  • strcmp(<非読み取りアドレス>, <読み取りアドレス>) -> クラッシュ

  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP -> クラッシュする

  • strcmp(<読み取りアドレス>, <非読み取りアドレス>) -> クラッシュ

  • b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

  • strcmp(<読み取りアドレス>, <読み取りアドレス>) -> クラッシュしない

  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

覚えておいてください:

  • BROP + 0x7は**pop RSI; pop R15; ret;**を指します。

  • BROP + 0x9は**pop RDI; ret;**を指します。

  • PLT + 0xbはdl_resolveへの呼び出しを指します。

strcmpを見つけることで、**rdx**を0より大きい値に設定することが可能です。

通常、rdxにはすでに0より大きい値が格納されているため、このステップは必要ないかもしれません。

8. Writeまたは同等のものを見つける

最後に、バイナリを漏洩させるためにデータを外部に送信するガジェットが必要です。そして、この時点で2つの引数を制御し、rdxを0より大きく設定することが可能です。

これに悪用できる一般的な関数は3つあります:

  • puts(data)

  • dprintf(fd, data)

  • write(fd, data, len(data)

ただし、元の論文では**write**のみが言及されているため、これについて話しましょう:

現在の問題は、PLT内のwrite関数の位置がわからないことと、データをソケットに送信するためのfd番号がわからないことです。

しかし、PLTテーブルの位置はわかっており、その動作に基づいてwriteを見つけることが可能です。また、サーバーとの複数の接続を作成し、高いFDを使用して、いくつかの接続と一致することを期待できます。

これらの関数を見つけるための動作シグネチャ:

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP -> データが印刷される場合、putsが見つかりました

  • 'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> データが印刷される場合、dprintfが見つかりました

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> データが印刷される場合、writeが見つかりました

自動エクスプロイト

参考文献

HackTricksをサポートする

Last updated