質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.64%

echo perl入力を溢れさせられない

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 618

kazuyakazuya

score 160

Virtial Box上のCentOSで
scanf関数にecho・perlコマンドを使って
パイプを作りバッファを溢れさせる入力をさせたいです。

#include <stdio.h>

int sub();

int main(void){
sub();
return 0;
}

int sub(){
char moji[15];
scanf("%s\n",moji);
int * p;
int *subp;
subp = main;
printf("This is main ReturnAddress: %p\n",subp);
printf("%d\n",sizeof(subp));
int *p;
p = (int*)moji;
int ebpofs;
for(ebpofs=-32;ebpofs<80;ebpofs++){
printf("addr%p price:%x",p + count,p[count]);
}
return 0;
}


このプログラム adr に対して

echo -e "AAAAAAAAAAAAAAAAAAA" | .adr
または
perl -e 'print "A"x20' | ./adr


のように文字列を流してやると・・・

イメージ説明

41のところ
15バイト分しか受け取られていません。

なぜでしょうか?
ホストのwindows上ではechoコマンドを使ってバッファを溢れさせることができたのですが・・・
CentOSではできないようでもなっているのでしょうか?
分からないのでお願いします。
イメージ説明
イメージ説明
sub関数逆アセンブル
イメージ説明
イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • rubato6809

    2019/11/25 19:31

    そのプログラムコードをうごかしても、その実行結果の表示にはなりません。コードと結果を一致するものにしてください。

    キャンセル

  • kazuyakazuya

    2019/11/25 19:56

    訂正しました。

    キャンセル

回答 2

checkベストアンサー

+1

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/12/11 09:08

    46バイト入力に流し込んだところでコアダンプが発生しました。

    キャンセル

  • 2019/12/11 09:11

    >p = (int*) moji; の行。
    これが原因だったんですね。ありがとうございます。

    キャンセル

  • 2019/12/11 09:22

    なるほど、想定より短いですね
    おそらくrbpが壊れただけで、リターンアドレスの書き換えには至っていないのではないかと思います。
    あとはデバッガで実行して調整するといいでしょう。

    キャンセル

0

その実行結果は、示されたコードで表示したものではない。別のプログラムが表示したのだから、中身も違うのだろう。
考えられることは、例えば scanf() で入力したのではなく、fgets() で入力した、とか。それなら普通は配列のサイズ分しか読み込まれない。
私は Ubuntu, GCC で

  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/12/14 23:52 編集

    ありがとうございます。
    なるほど・・・
    64bitプロセスでも、「スタックで~」って方法を使うことはできるんですか?

    >ここで言う「割込み」とは ・・・
    ソフトウエア割り込み・・・今回で言うcall printf
    ですよね?
    今になって混乱し始めています(笑)。

    キャンセル

  • 2019/12/15 00:31

    スタック渡しで使うことはできなくないが・・・
    そういうコンパイラを誰が用意するのだろう。
    詳しいことは知らないが、プロセッサメーカーから、64bitCPUは引数に使うレジスタはこれこれ・・・という提案があって、世の中のコンパイラ(GCCもClangもVCも…)はそれに従っている、よってスタック渡しのコンパイラは存在しないようである。そうするとスタック渡しコンパイラは他との互換性という壁ができる。それにせっかくたくさんあるレジスタを活用できず宝の持ち腐れ、結果として性能が上がらないだろう・・・ということで64bitのスタック渡しはやめた方が良い。

    > ソフトウエア割り込み・・・今回で言うcall printf
    call 命令は割込みじゃないよ。
    printf()のなかで、最終的に write() システムコールにたどり着き、そこがたぶん syscall 命令でOSを呼ぶのである。その syscall がソフトウェア割込み命令です。

    キャンセル

  • 2019/12/15 01:34

    ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る