ROP - Return Oriented Programing
Last updated
Last updated
AWSハッキングの学習と練習:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングの学習と練習: HackTricks Training GCP Red Team Expert (GRTE)
**Return-Oriented Programming (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ガジェットと呼ばれます。
さらに、system()
関数のアドレスが利用可能です。
pwntoolsを使用して、ROPチェーンの実行のためにスタックを準備します。system('/bin/sh')
を実行することを目指し、チェーンが次のように始まることに注意してください:
アラインメント目的のret
命令(オプション)
system
関数のアドレス(ASLRが無効で既知のlibcを仮定、詳細はRet2libにあります)
system()
からの戻りアドレスのプレースホルダ
"/bin/sh"
文字列のアドレス(system関数のパラメータ)
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 レジスタを設定し(system() に "/bin/sh" 文字列を引数として渡すため)、その後 system() 関数を呼び出すためのガジェットに焦点を当てます。以下のガジェットを特定したと仮定します:
pop rdi; ret: スタックのトップの値を RDI にポップしてから戻ります。system() の引数を設定するために必要です。
ret: 単純なリターンで、一部のシナリオでスタックの整列に役立ちます。
そして、system() 関数のアドレスを知っているとします。
以下は、x64 で system('/bin/sh') を実行するためのROPチェーンを設定して実行するための例です。
x86-64 ABI は、call命令が実行される際にスタックが16バイトに整列されることを保証します。LIBC は、パフォーマンスを最適化するために、この整列が必要な SSE命令(例: movaps)を使用します。スタックが適切に整列されていない場合(つまり RSP が16の倍数でない場合)、system のような関数の呼び出しは ROPチェーン で失敗します。これを修正するには、ROPチェーンで system を呼び出す前に retガジェット を追加するだけです。
x64は最初の数値引数にレジスタを使用するため、単純な関数呼び出しにはx86よりも少ないガジェットが必要ですが、適切なガジェットを見つけてチェーンすることは、レジスタの数の増加とアドレス空間の拡大により、より複雑になる可能性があります。 x64 アーキテクチャの増加したレジスタ数と大きなアドレス空間は、特にReturn-Oriented Programming(ROP)の文脈でのエクスプロイト開発において、機会と課題の両方を提供します。
この情報については、次のページをチェックしてください:
スタックキャナリー: BOFの場合、ROPチェーンを悪用するためには、ストアされたスタックキャナリーをバイパスして、リターンポインタを上書きする必要があります。
ガジェットの不足: 十分なガジェットがない場合、ROPチェーンを生成することはできません。
ROPは単なる任意のコードを実行するための技術であることに注意してください。ROPを基に、多くのRet2XXX技術が開発されました:
Ret2lib: ROPを使用して、ロードされたライブラリから任意のパラメータ(通常は system('/bin/sh')
のようなもの)を持つ任意の関数を呼び出します。
Ret2Syscall: ROPを使用して、システムコール(例: execve
)を呼び出す準備をし、任意のコマンドを実行します。
EBP2Ret & EBP Chaining: 最初のものはEIPの代わりにEBPを悪用してフローを制御し、2番目のものはRet2libに似ていますが、この場合、フローは主にEBPアドレスで制御されます(ただし、EIPの制御も必要です)。
64ビット、Pieおよびnx有効、キャナリなし、vsyscall
アドレスでRIPを上書きし、スタック内の次のアドレスに戻ることで、フラグを漏洩させる関数の一部を取得するための部分的な上書きを行います。
arm64、ASLRなし、スタックを実行可能にするROPガジェットとスタック内のシェルコードにジャンプします