ゼロからの学習の最初のパスで理解できなかった部分を記録します。
長い議論:#
現代のオペレーティングシステムでは、通常、メモリページの単位でプロセスのメモリ使用権限を設定するための MPU(Memory Protection Unit)メカニズムがあります。メモリの権限は、読み取り可能(R)、書き込み可能(W)、実行可能(X)の 3 つに分かれています。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
の引数であり、アドレスが出力されます。