ROP - Return Oriented Programing
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
リターン指向プログラミング (ROP) は、No-Execute (NX) や Data Execution Prevention (DEP) のようなセキュリティ対策を回避するために使用される高度なエクスプロイト技術です。攻撃者は、シェルコードを注入して実行するのではなく、バイナリやロードされたライブラリに既に存在するコードの断片、いわゆる 「ガジェット」 を利用します。各ガジェットは通常 ret
命令で終わり、レジスタ間でデータを移動したり、算術演算を行ったりする小さな操作を実行します。これらのガジェットを連結することで、攻撃者は任意の操作を実行するペイロードを構築し、NX/DEP保護を効果的に回避します。
制御フローのハイジャック: まず、攻撃者はプログラムの制御フローをハイジャックする必要があります。通常は、バッファオーバーフローを利用してスタック上の保存された戻りアドレスを上書きします。
ガジェットの連結: 次に、攻撃者は目的のアクションを実行するためにガジェットを慎重に選択し、連結します。これには、関数呼び出しの引数を設定し、関数を呼び出し(例: system("/bin/sh")
)、必要なクリーンアップや追加の操作を処理することが含まれます。
ペイロードの実行: 脆弱な関数が戻るとき、正当な場所に戻るのではなく、ガジェットの連鎖を実行し始めます。
通常、ガジェットは ROPgadget、ropper または pwntools から直接見つけることができます (ROP)。
cdecl: 呼び出し元がスタックをクリーンアップします。関数引数は逆順(右から左)でスタックにプッシュされます。引数は右から左にスタックにプッシュされます。
stdcall: cdeclに似ていますが、呼び出し先がスタックをクリーンアップする責任があります。
まず、バイナリまたはそのロードされたライブラリ内で必要なガジェットを特定したと仮定します。私たちが興味を持っているガジェットは次のとおりです:
pop eax; ret
: このガジェットはスタックのトップ値を EAX
レジスタにポップし、その後戻ります。これにより EAX
を制御できます。
pop ebx; ret
: 上記と同様ですが、EBX
レジスタ用で、EBX
を制御できるようにします。
mov [ebx], eax; ret
: EAX
の値を EBX
が指すメモリ位置に移動し、その後戻ります。これはしばしば write-what-where gadget と呼ばれます。
さらに、system()
関数のアドレスも利用可能です。
pwntools を使用して、system('/bin/sh')
を実行するためにスタックをROPチェーン実行のために準備します。チェーンは次のように始まります:
アライメント目的のための ret
命令(オプション)
system
関数のアドレス(ASLRが無効で、libcが既知であると仮定、詳細は Ret2lib を参照)
system()
からの戻りアドレスのプレースホルダー
"/bin/sh"
文字列のアドレス(system関数のパラメータ)
System V AMD64 ABI 呼び出し規約を使用し、Unix系システムでは 最初の6つの整数またはポインタ引数がレジスタ RDI
, RSI
, RDX
, RCX
, R8
, および R9
に渡されます。追加の引数はスタックに渡されます。戻り値は RAX
に置かれます。
Windows x64 呼び出し規約では、最初の4つの整数またはポインタ引数に RCX
, RDX
, R8
, および R9
を使用し、追加の引数はスタックに渡されます。戻り値は RAX
に置かれます。
レジスタ: 64ビットレジスタには RAX
, RBX
, RCX
, RDX
, RSI
, RDI
, RBP
, RSP
, および R8
から R15
までが含まれます。
私たちの目的のために、RDI レジスタを設定することを可能にするガジェット("/bin/sh" 文字列を system() に引数として渡すため)に焦点を当て、次に system() 関数を呼び出します。以下のガジェットを特定したと仮定します:
pop rdi; ret: スタックの最上位の値を RDI にポップし、次に戻ります。system() の引数を設定するために不可欠です。
ret: 単純なリターンで、いくつかのシナリオでスタックの整列に役立ちます。
そして、system() 関数のアドレスを知っています。
以下は、pwntools を使用して system('/bin/sh') を x64 で実行することを目的とした ROP チェーンを設定し、実行する例です:
In this example:
pop rdi; ret
ガジェットを利用して RDI
を "/bin/sh"
のアドレスに設定します。
RDI
を設定した後、チェーン内の system() のアドレスで system()
に直接ジャンプします。
ターゲット環境が必要とする場合、ret_gadget
がアライメントのために使用されます。これは、関数を呼び出す前に適切なスタックアライメントを確保するために x64 でより一般的です。
x86-64 ABI は、call命令 が実行されるときに スタックが16バイトアライメント されることを保証します。LIBC はパフォーマンスを最適化するために、SSE命令(例えば movaps)を使用し、これにはこのアライメントが必要です。スタックが正しくアライメントされていない場合(つまり RSP が16の倍数でない場合)、ROPチェーン での system などの関数への呼び出しは失敗します。これを修正するには、ROPチェーンで system を呼び出す前に ret gadget を追加します。
x64は最初のいくつかの引数にレジスタを使用するため、 簡単な関数呼び出しにはx86よりも少ないガジェットを必要とすることが多いですが、レジスタの数が増え、アドレス空間が大きくなるため、適切なガジェットを見つけてチェーンすることはより複雑になる可能性があります。x64 アーキテクチャのレジスタの数の増加とアドレス空間の拡大は、特にリターン指向プログラミング(ROP)の文脈において、エクスプロイト開発にとって機会と課題の両方を提供します。
この情報については、以下のページを確認してください:
スタックカナリア: BOFの場合、ROPチェーンを悪用するためにリターンポインタを上書きするためにスタックカナリアをバイパスする必要があります。
ガジェットの不足: 十分なガジェットがない場合、ROPチェーンを生成することは不可能です。
ROPは任意のコードを実行するための技術に過ぎないことに注意してください。ROPに基づいて多くのRet2XXX技術が開発されました:
Ret2lib: ROPを使用して、任意のパラメータでロードされたライブラリから任意の関数を呼び出します(通常は system('/bin/sh')
のようなもの)。
Ret2Syscall: ROPを使用して、execve
などのシステムコールを呼び出す準備をし、任意のコマンドを実行させます。
EBP2Ret & EBPチェイニング: 最初はEIPの代わりにEBPを悪用してフローを制御し、2つ目はRet2libに似ていますが、この場合は主にEBPアドレスでフローを制御します(ただしEIPも制御する必要があります)。
64ビット、PIEとnxが有効、カナリアなし、vsyscall
アドレスでRIPを上書きし、スタック内の次のアドレスに戻ることを目的とした部分的な上書き
arm64、ASLRなし、スタックを実行可能にし、スタック内のシェルコードにジャンプするためのROPガジェット
AWSハッキングを学び、練習する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、練習する:HackTricks Training GCP Red Team Expert (GRTE)