BROP - Blind Return Oriented Programming
Last updated
Last updated
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、実践する: HackTricks Training GCP Red Team Expert (GRTE)
この攻撃の目的は、脆弱なバイナリに関する情報なしにバッファオーバーフローを介してROPを悪用できるようにすることです。 この攻撃は次のシナリオに基づいています:
スタックの脆弱性とそれをトリガーする方法の知識。
クラッシュ後に再起動するサーバーアプリケーション。
これらのプロセスに関する詳細情報は、こちら (BF Forked & Threaded Stack Canaries)およびこちら (BF Addresses in the Stack)で見つけることができます。
このガジェットは基本的に、ROPガジェットによって何か興味深いものが実行されたことを確認することを可能にします。なぜなら、実行がクラッシュしなかったからです。通常、このガジェットは実行を停止するものであり、特定のROPガジェットが実行されたことを確認するためにROPチェーンの最後に配置されます。
この技術はret2csuガジェットを使用します。これは、いくつかの命令の途中でこのガジェットにアクセスすると、**rsi
およびrdi
**を制御するガジェットが得られるからです:
これらがガジェットです:
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ガジェットのアドレスを知っていると、rsi
およびrdi
を制御するガジェットのアドレスを推測することが可能です。
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
-> クラッシュしない
strcmp
関数は、比較される文字列の長さをレジスタrdx
に設定します。rdx
は3番目の引数であり、後でwrite
を使用してプログラムを漏洩させるために0より大きい必要があります。
**strcmp
**のPLT内の位置を、その動作に基づいて見つけることが可能です。なぜなら、私たちは今、関数の最初の2つの引数を制御できるからです:
strcmp(<非読み取りアドレス>, <非読み取りアドレス>) -> クラッシュ
strcmp(<非読み取りアドレス>, <読み取りアドレス>) -> クラッシュ
strcmp(<読み取りアドレス>, <非読み取りアドレス>) -> クラッシュ
strcmp(<読み取りアドレス>, <読み取りアドレス>) -> クラッシュしない
これは、PLTテーブルの各エントリを呼び出すか、PLTスローパスを使用して確認できます。これは基本的にPLTテーブルのエントリを呼び出し + 0xb(これは**dlresolve
を呼び出します)で、スタック内に調べたいエントリ番号**(ゼロから始まる)を続けて、最初のエントリからすべての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より大きい値を保持しているため、このステップは必要ないかもしれません。
最後に、バイナリを漏洩させるためにデータを外部に送信するガジェットが必要です。そして、この時点で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が見つかりました
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、実践する: HackTricks Training GCP Red Team Expert (GRTE)