質問: バッファオーバーフローの勉強をしています。 ASLRは無効。SSPも無効にしてコンパイルしてあります。 cのプログラムです。 ![イメージ説明](https://teratail.storage.googleapis.com/uploads/contributed_images/bd4b6f4d58499804b62d2f0e8928231d.png) 上のプログラムには脆弱性があり、bufferで定義されている32byteよりも大きい値を入力されるとオーバーフローがおきます。 プログラムを実行し、入力に、 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB と与えてみたところ、下の画像のように EIPがBBBBで書き換えられました。 ![イメージ説明](https://teratail.storage.googleapis.com/uploads/contributed_images/1f67be41885e1cdf4bfa49c9addb5cac.png) これで自分の好きな場所に実行位置を移すことが出来ると思ったので、main関数に飛ばしてもう一度Helloと表示させたいと思いました。下の画像のように逆アセンブリしてみてみると、どうやらmain関数はメモリの000005b4に格納されているみたいなので、 ![アセンブリ](https://teratail.storage.googleapis.com/uploads/contributed_images/a4c5c2306ec1e9366ba7f6941d23fc34.png) もう一度、プログラムを実行して入力に AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAb4050000 (インテルCPUのためリトルエンディアンです。) と入力してみましたが、うまく行きません。 下の画像のようになってしまいます。 ![イメージ説明](https://teratail.storage.googleapis.com/uploads/contributed_images/f2b3e31563a8d97e74a4679b120dc5ef.png) なんとなく16進数に変換しないとだめかなと思いましたが、 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0xb40x050x000x00 のように0xをつけてみてもダメでした。 調べてもどのような入力を渡せば良いか分かりませんでした。 環境はkali linux2017.3です。 どなたかご教授お願いいたします。 追記 回答してくださった方の方法でうまくいくと思うけど、もう少し回答いただきたいので一様追記。 EIPに0x000005b4を格納できれば良いみたい。 python3で000005b4をバイト文字列に変換してみると、 \x00\x00\x05\xb4となった。 echo -e 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xb4\x05\x00\x00' | ./一番上の画像のcのプログラム としてみても上手くいきませんでした。 書くの忘れていましたが、gcc -m32 でコンパイルしているのでレジスタのサイズは32bitなのでそもそも\x00\x00\x05\xb4がEIPに入りきらない気がする・・・。 (\x00\x00\x05\xb4は16byteもあるので) BBBBは4byteだから入るけど・・・ asm様のコードがいまいち理解できていません・・・ キーボードでは普通の文字列しか入力できないから、バイナリで入力を渡せるようにファイルにバイナリ形式で書き込んでそれをfgetsで読み込んでいるコードのようだけれど、コンパイルして実行すると、input: となって入力を待たれて何を入れてもWrong anserとなって コードが進まないです。 ついでに教えて頂けると嬉しいです。
回答: そういえば、input: や Wrong anser と表示される件についてはどうなりましたか? 詳しくメモリ配置諸々について説明しろと言われると知識不足なので答えられませんが... とりあえずやってみました。環境は Arch Linux, 64-bit です。 まず、 > > EIPに0x000005b4を格納できれば良い > > > がどうも怪しいです。 実際に同様のプログラムを書き、 `-m32 -fno-stack-protector` オプションを付けてコンパイルしたバイナリを obbjdump と gdb を使って逆アセンブルしてみました。 `objdump -d` で表示されるアドレスは、NieR さんと同じように非常に小さいものでした (3ケタとか) 。一方、`gdb` で **プログラムを開始した後** に `disassemble main` で表示されたアドレスは、その3ケタの上に5ケタくらいくっついて8ケタくらいになっていました。 つまり、バイナリは相対アドレスになっていて、実行時にどこぞに配置されているようなのです。つまりは position-independent executable になっているようなのです。おそらく ASLR を行うため、デフォルトで (私の環境では) PIE になるようです。 NieR さんの環境もおそらくそうなのでは? というわけで、明示的に `-fno-PIE -no-pie` と指定して PIE でないバイナリをコンパイルするか、PIE なバイナリの場合は (ALSR を解除した状態であることは前提になりますが) 実際に配置されるアドレスを取得する必要があります。 PIE でないバイナリをコンパイルした場合は objdump で見れば分かります。 PIE な場合は 1. 実行してみて pid を確認し、`/proc/{pid}/maps` で確認する 2. gdb で disassemble コマンドを実行して見る 3. プログラム自信に main() 関数のアドレスを取って表示させる などして得られます。 PIE が実際どこに配置されるかなどのルールは私には分からないので、得られたアドレスがどれくらいの間有効なのかは分かりませんが、とりあえず実験して確かめる間くらいは同じアドレスになってくれました。 そして正しいアドレスが分かったら、 `echo -e 'AA....A\x00\x00\x00\x00'` を使う方法で再現させられます。うまく普通の文字列の範囲におさまってくれたなら、手入力でも同様です。 以下、実際に私がやって再現させたときの手順です。いろいろ関係のない部分もありますが。 ### コンパイル ファイルは vulnerable.c です。中身は質問文に貼られていた画像の通りに写しました。 ``` % gcc -o vulnerable vulnerable.c -m32 -fno-stack-protection ``` これで、 PIE なバイナリができます。あるいは ``` % gcc -o vulnerable vulnerable.c -m32 -fno-stack-protection -fno-PIE -no-pie ``` これで、 PIE でないバイナリができます。 ### アドレス割り出し gdb を使います。 ``` % gdb vulnerable ``` この直後に disassemble main してもまだ配置されていないので例の相対アドレスが出るのみです。なのでまずは実行。止まってほしいので run でなく start します。 ``` gdb-peda$ start ``` 続いて、 ``` gdb-peda$ disassemble main ``` とすると、main() 関数が逆アセンブルされます。このときの先頭の `<+0>` となっているところのアドレスが main() の先頭アドレスです。メモするなりなんなり。 ### EIP を書き換えるためのオフセットを探す 続いて文字列のどこにアドレスを入れると EIP を書き換えられるかを探します。それに関しては NieR さんは既に分かっていらっしゃるようなので不要ですが一応。 ``` gdb-peda$ pattern create 200 input ``` 200文字のパターンを input というファイルに保存しておきます。これを標準入力にリダイレクトするという算段です。パターンを標準入力に与える方法、もっと普通のやり方があるのかもしれないですが、とりあえずこれでできたのでよしとします。 ``` gdb-peda$ run < input ``` すると当然落ちます。落ちたときの EIP に入っている文字列を覚えて次のようにコマンドを唱えると ``` gdb-peda$ pattern offset {XXXX: EIP に入っていた文字列} XXXX found at offset: ** ``` となって、オフセットが表示されます。この `**` にある数字の数が求めるオフセットです。 ### 実行 ここまでくると (**ASLR をシステムワイドで無効にしているならば**) gdb は終了しても大丈夫です。`echo -e` を使って入力を作り、パイプします。 ``` % echo -e 'AA...AAA\x00\x00\x00\x00' | ./vulnerable Hello Hello segmentation fault ``` 私はシステムワイドでは無効にしていないので gdb の無効化に頼りましたが。 gdb 内でファイルを経由して入力し、成功することを確認しました。また、 PIE でないプログラムについては、上のようにパイプを使って行う方法で同様に確認できました。 おつかれさまでした。 繰り返しになりますが、私はあまり詳しくないので、詳細な質問にはお答えできないかもしれません。