Virtial Box上のCentOSで
scanf関数にecho・perlコマンドを使って
パイプを作りバッファを溢れさせる入力をさせたいです。
c
1#include <stdio.h> 2 3int sub(); 4 5int main(void){ 6sub(); 7return 0; 8} 9 10int sub(){ 11char moji[15]; 12scanf("%s\n",moji); 13int * p; 14int *subp; 15subp = main; 16printf("This is main ReturnAddress: %p\n",subp); 17printf("%d\n",sizeof(subp)); 18int *p; 19p = (int*)moji; 20int ebpofs; 21for(ebpofs=-32;ebpofs<80;ebpofs++){ 22printf("addr%p price:%x",p + count,p[count]); 23} 24return 0; 25} 26
このプログラム adr に対して
cmd
1echo -e "AAAAAAAAAAAAAAAAAAA" | .adr 2または 3perl -e 'print "A"x20' | ./adr
のように文字列を流してやると・・・
41のところ
15バイト分しか受け取られていません。
なぜでしょうか?
ホストのwindows上ではechoコマンドを使ってバッファを溢れさせることができたのですが・・・
CentOSではできないようでもなっているのでしょうか?
分からないのでお願いします。
sub関数逆アセンブル
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/11/25 10:56
回答2件
0
ベストアンサー
Centos7で試しましたが、40で違反落ちしました。
objdump -d
の結果
000000000040058d <main>: 40058d: 55 push %rbp 40058e: 48 89 e5 mov %rsp,%rbp 400591: 48 83 ec 20 sub $0x20,%rsp 400595: 48 8d 45 e0 lea -0x20(%rbp),%rax 400599: 48 89 c6 mov %rax,%rsi 40059c: bf a0 06 40 00 mov $0x4006a0,%edi 4005a1: b8 00 00 00 00 mov $0x0,%eax 4005a6: e8 d5 fe ff ff callq 400480 <__isoc99_scanf@plt>
からも、0x20+push %rbp分の8バイトで計40バイト必要な事がわかります。
リターンアドレスだと更に8バイト先ですかね
追記:
- scanfに改行いれるのはやめましょう。
- printfの方に改行いれないとSS通りになりませんが実行しているプログラムは正しいのでしょうか?
投稿2019/11/26 00:21
編集2019/11/26 00:25総合スコア15149
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/11/26 03:01
2019/11/26 03:02
2019/11/26 03:33 編集
2019/12/10 04:41
2019/12/10 06:44
2019/12/10 06:49
2019/12/10 08:31
2019/12/10 09:53
2019/12/10 10:42
2019/12/10 14:04
2019/12/10 14:08
2019/12/10 14:14
2019/12/11 00:08
2019/12/11 00:11
2019/12/11 00:22
0
その実行結果は、示されたコードで表示したものではない。別のプログラムが表示したのだから、中身も違うのだろう。
考えられることは、例えば scanf() で入力したのではなく、fgets() で入力した、とか。それなら普通は配列のサイズ分しか読み込まれない。
私は Ubuntu, GCC で
C
1 printf("addr%p price:%08x\n", p + count, p[count]);
で表示、"A"x30 辺りでコアダンプに至りました。リターンアドレスを上書きされたからに他なりません。
CentOSを使ってないので断言はしないけど、scanf() はCライブラリ関数にふくまれ、仕様が違うとは考えにくい。scanf() を使う限り溢れると思いますが。
そうですか。溢れないとすると、私には事情がわかりかねます。
sub() 内の変数の割当がわかり、すっきり原因がわかった。asm さんと違って私は原因を特定することに興味があるので、こちらに書かせてもらう。
15バイトの配列に echo -e "AAAAAAAAAAAAAAAAAAA" | .adr と20バイトほど送り込んでいるが
- 20バイトだと溢れたメモリは sub() の中の p
と subp変数に相当する。この変数に代入した時点で上書きされるので、溢れたようにみえない。20バイトでは慎ましすぎた。 - 40バイトほど送り込めば、溢れた痕跡が見えるだろう。変数と変数の間に4バイトの未使用領域があるので、そこは上書きされずに溢れた値が残るはずだから。
- 60バイトほど送り込めばリターンアドレスを上書きするので、sub() からリターンした時点でセグメントフォールトを起こすだろう。
sub() が使う変数の割当は次のようです。
変数名 | オフセット | サイズ | 累積バイト数 |
---|---|---|---|
char moji[15] | -0x27〜-0x19 | 15 | 15 |
int *p | -0x18〜-0x11 | 8 | 23 |
int *subp | -0x10〜-0x09 | 8 | 31 |
(空き) | -0x08〜-0x05 | 4 | 35 |
int ebpofs | -0x04〜-0x01 | 4 | 39 |
old %rbp | 0x00〜0x07 | 8 | 47 |
ret address | 0x08〜0x0F | 8 | 55 |
- オフセットは %rbp のオフセット
- 累積バイト数は、その変数まで塗りつぶすために流し込むバイト数
46バイト流し込んでコアダンプしたなら old %rbp の8バイトの内、7バイトを書き換えた、ということです。old %rbp の値は sub() からリターンする時に leaveq 命令で %rbp にpopして、(一般的には)呼出し元関数のローカル変数をアクセスするので、この時不正アドレスをアクセスしてしまうのですが、、この場合はたぶんmain() の最後の leaveq 命令が不正アクセスするのでしょう。leaveq 命令はちょっとトリッキーな命令です、いつも使われるとは限らないけど覚えておかないと。
コアダンプさせるだけならテキトーな値を流しこめば済むが、乗っ取るにはリターンアドレス(オフセット+8から8バイト)を”正しい”値で上書きする必要があります。その値は普通、moji配列の先頭アドレスでしょうね。スタック上のアドレスです。表示結果を見ると 0x7fffe5320be9 でしょうか。
このように、スタック領域のメモリアドレス、変数の配置をピンポイントで確定させないと(この方式の)シェルコードは作りようがありません。この表から、この場合のシェルコードのサイズは 55バイトだとわかります。だけどこれらの変数は、コードを修正すればもちろん変化するうえに、コンパイルする都度、配置が変化する位に思ったほうが良いですよ。
ようやくこういう具体的な話ができるようになった感じですね。そうは言っても、まだ先は長そうだけどね。
Enjoy!
投稿2019/11/25 10:47
編集2019/12/11 04:53総合スコア1382
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/11/25 10:57
2019/11/25 13:08
2019/11/25 22:43
2019/12/11 07:39
2019/12/14 12:33
2019/12/14 13:34
2019/12/14 13:45
2019/12/14 14:30
2019/12/14 14:52 編集
2019/12/14 15:31
2019/12/14 16:34
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。