ASISで見事に爆死したので記念に投稿します。
https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/を参考にしました。
x64でのpwn
基本的なテクニックや原理はx86と同じように使えるけど細かいところで違いがある。
引数の渡し方
引数の渡し方が変わってる。
x86では関数呼び出しするときにスタックに値を積んで引数を渡していたため、バッファオーバーフローなどでそのまま引数を渡せた。
x64ではレジスタに引数の値を指定するため、引数の操作がやりにくくなっている。
引数を渡すときは、まずスタックに値を設定してからROPで正しいレジスタに値をpopする必要がある。
ropperとかrp++とかを使って、目当てのものを探そう。
ちなみにレジスタと引数の対応はgccだと下になる。
第一引数 rdi
第二引数 rsi
第三引数 rdx
第四引数 rcx
第五引数以降もあるけど、だいたいは引数3つくらいなので憶えてない。
レジスタ
64bit長になった。
それに伴い、pushやpopは8バイト単位で動作するようになり、メモリのアドレス長も8バイトになっている。
当然のことと言えば当然のことなんだけど、ものの見事に嵌った。
リターンアドレス
バッファオーバーフローでリターンアドレスを書き換えEIPを奪うのはpwnでは基本テクニックである。
x86では長い文字列を入力しバッファを溢れさせると、eipの値が0x41414141になっていたりするのがgdbなどで確認できる。
一方x64ではアドレス空間が64bitに拡張されたが、有効な命令アドレスの範囲は0x00007FFFFFFFFFFFまでになっている。
そのためリターンアドレスを書き換えるときに0x414141...としてしまうと、不正アドレスとしてripが書き換えられず、プログラムがSIGSEGVされてしまう。
SIGSEGVされたときのスタックトップがリターンアドレスなので、その値からオフセットを割り出し入力を適切な長さに調節して書き換えを試みると成功する。
ここでも嵌ってた。
おわり
x64は強敵でしたね。
ASISではバイナリ中の文字列に気付けずに突破できませんでした。