ゼロから一までの最初のパスを理解できなかった部分をメモしておきます。
長い議論:#
現代のオペレーティングシステムでは、一般的には MPU(メモリ保護ユニット)メカニズムがあり、メモリページの単位でプロセスのメモリ使用権限を設定できます。メモリの権限には読み取り(R)、書き込み(W)、実行(X)があります。CPU が実行権限のないメモリ上のコードを実行すると、オペレーティングシステムはプログラムを即座に終了します。
デフォルトでは、脆弱性緩和のルールに基づいて、プログラムには書き込みと実行の両方の権限を持つメモリは存在しません。そのため、プログラムのコードセグメントやデータセグメントを変更して任意のコードを実行することはできません。このような脆弱性緩和メカニズムに対して、特定の命令シーケンスにプログラムを戻すことでプログラムの実行フローを制御する攻撃技術があり、それがリターンオリエンテッドプログラミング(Return-Oriented Programming、ROP)と呼ばれています。
ret(0xc3)命令で終わる命令フラグメント(ガジェット)を使用して ROP チェーンを構築し、任意の命令の実行を実現し、最終的に任意のコードの実行を実現します。具体的な手順は次のとおりです:プログラムで実行可能なメモリセグメント内のすべての ret 命令を検索し、ret の前のバイトに有効な命令が含まれているかどうかを確認します。有効なフラグメントとしてマークされた場合、一連の ret で終わるこのような命令を見つけた後、これらの命令のアドレスを順番にスタックに配置します。これにより、対応する命令を実行した後、その終わりの ret 命令がスタックのトップにある新しい Gadget にプログラムの制御フローを渡します。スタック上のこの連続した Gadget は ROP チェーンを構成し、任意の命令の実行を実現します。
手順:#
サンプルコード:https://buuoj.cn/challenges#[第六章 CTF 之 PWN 章] ROP
ROPgadget --binary rop
を使用してすべてのガジェットを取得します。
ガジェットが少なすぎるため、libc のロードアドレスを取得し、その中から syscall を見つける必要があります。
このペイロードを使用すると、puts のアドレスを取得できます(pop_rdi は pop rdi のガジェットです)。
具体的な原理:
リターンアドレスは 0x12 バイト後にあります。
leave
を実行するときにpop_rdi
がスタックのトップにあります(上記のものはクリアされました)。
ret
を実行すると、pop_rdi
が存在するアドレスにジャンプし、スタックのトップのpop_rdi
をポップします。その後、スタックトップはputs_got
です。
puts_got
はputs
の実際のアドレスを指すポインタです。
次に、pop_rdi
を実行して、puts_got
をrdi
に配置します。
puts
に ret し、呼び出し規約に従って(既存の puts 呼び出しを参照)、rdi
はputs
の引数なので、アドレスが出力されます。