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