sprintfを用いたフォーマット文字列攻撃

このQ&Aのポイント
  • sprintfを用いたフォーマット文字列攻撃に関する質問です。関数呼び出しにすると変な箇所を参照してしまう原因とそれによる悪影響について教えてください。
  • sprintfを使ったフォーマット文字列攻撃に関する質問です。関数呼び出しを行うと変な箇所が参照される原因と、その結果生じる悪影響について教えてください。
  • sprintfを利用したフォーマット文字列攻撃についての質問です。関数呼び出しをすると、変な箇所が参照される原因とその影響について教えてください。
回答を見る
  • ベストアンサー

sprintfを用いたフォーマット文字列攻撃

sprintfを用いたフォーマット文字列攻撃に関する質問です。 main文の中で --------------------- int main(void){ char str[50]; sprintf(str,"%s"); puts(str); } --------------------- を実行すると(null)で帰ってきます。 しかし関数呼び出しを行うと結果が変わってきます。 --------------------- void f(){ char str[50]; sprintf(str,"%s"); puts(str); } int main(void){ f(); } --------------------- これを実行すると" ・L "のような文字化けしたものに変わりました。 sprintf(str,"%s%s");と記述することによって" ・L (null) "と、本来呼び出される箇所のものが格納されています。 なぜ関数呼び出しにすると変なところを参照してしまうのか(第3引数があると勘違いしてしまうのか)教えてください。 また、これによりどのような悪影響があるのか教えてください。 自分はプログラマが意図しない箇所を参照するため攻撃者のプログラムアドレスを格納してしまう恐れがあると考えています。

質問者が選んだベストアンサー

  • ベストアンサー
  • wormhole
  • ベストアンサー率28% (1619/5654)
回答No.1

>なぜ関数呼び出しにすると変なところを参照してしまうのか(第3引数があると勘違いしてしまうのか)教えてください。 勘違いなどしていません。 sprintfのフォーマット指定"%s"で第3引数があり、それは文字列へのポインタと書いてあります。 同様にsprintfのフォーマット指定"%s%s"は第3引数,第4引数があり、それは文字列へのポインタということになります。 C,C++で可変長引数はどのようにして実装するのか調べてみてください。 >自分はプログラマが意図しない箇所を参照するため攻撃者のプログラムアドレスを格納してしまう恐れがあると考えています。 フォーマットをユーザーに入力させたりするなら、その可能性はあります。 ですが通常そのようなケースはないのではないでしょうか。

negimagi
質問者

補足

回答ありがとうございます。 char str[50]; sprintf(str,"%s"); puts(str); 上記のプログラムをmain文内で実行したところ、第3引数の%sは(null)になったのに、関数に記述すると第3引数に文字化けしたものが入るのか理由がわかりません。 同じ処理の流れだと思うのですが、なぜ%sに格納される内容が変わるのか教えていただけないでしょうか。 >フォーマットをユーザーに入力させたりするなら、その可能性はあります。 >ですが通常そのようなケースはないのではないでしょうか。 確かにそうですね。 これは現実的ではないですよね。 ありがとうございます。

その他の回答 (5)

  • Vivi0726
  • ベストアンサー率33% (1/3)
回答No.6

関数呼出しだから内容が変わったわけではありません。 strには何も書き込まれていないので、 コンパイルされた時点でchar str[50]で確保されたメモリに存在したデータが見えているだけしょう。 sprintf( str, "%s" );では、strに何も書き込まれていません。 sprintf( str, "%s", "ABC" ); とか sprintf( str, "%s%s", "ABC", "XYZ"); とすることでstrにABCやABCXYZの文字列が書き込まれます。

  • wormhole
  • ベストアンサー率28% (1619/5654)
回答No.5

>同じ処理の流れだと思うのですが、なぜ%sに格納される内容が変わるのか教えていただけないでしょうか。 未定義動作だしたまたまそうなっただけです。 前にも書きましたけど可変長引数の関数をどうやってつくるのか調べてみてください。

  • a_kwn
  • ベストアンサー率34% (8/23)
回答No.4

この質問は、残念ながら”コンパイラの実装依存”であるため、誰も回答できません。 まず、int sprintf(char *str, const char *format, ...) は、 format に渡されたフォーマット文字列から、可変個の引数を読みに行こうとします。 ですので、 sprintf(str, "%s"); と書けば引数を2つしか渡さなくても3番目の引数の値を読みに行き、 sprintf(str, "%s%s"); なら3番目と4番目の引数を読みに行くわけです。 (質問に”第3引数があると勘違いしてしまうのか”とありますが、そうではなく、 sprintf(str, "%s") は、必ず第3引数を読みにいくと考えてください。) この渡されなかった”3番目の引数がどのように読みだされるのか?” について、一般的なことは、誰にも回答できません。 大抵は、コンパイラや sprintf(str, "%s"); の前にあるコード群と そのコードの実行結果によって、様々に変わってしまうと思います。 ですので、質問にあるどちらの結果も”たまたまそうなった”に過ぎず、 ・少しコードを変えたら結果も変わった。 ・コンパイルオプションを変更したら結果が変わった。 ・他のコンパイラでコンパイルしたら結果が変わった。 だとしても、全く不思議ではないです。 もし、あなたがアセンブラを読めるなら、どのように引数が渡され、sprintf 側で どのように読みこまれたか調査することは可能でしょう。 でも、それもあなたが使用したコンパイラがたまたまそのように実装していたという 話に過ぎません。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

ま~未定義動作だからねぇ....

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

#1にもありますが「可変長引数 C言語 実装」で検索するとか、上級者向けの参考書を読むとかして、どんなカラクリになっているか、調べてください。 結果が違うのは「たまたま」です。 例えば、次のものは、また違った結果になるのではないでしょうか。 #include <stdio.h> int main(void){ char *a= "A"; char str[50]; char *b= "B"; sprintf(str,"%s"); puts(str); return 0; } また、どのコンパイラを使っているかわかりませんが、Release/Debugビルド、最適化オプション等でも変わる可能性があります

関連するQ&A

  • 文字列をうまく返してくれない

    数値を文字列として呼び出し元に渡し、呼び出し元で文字列を数値に変えようとしたのですがatoi関数(strtolを使うと最初の文字のみ帰ってくるため2桁以上の数値に対応できない)を使うとうまく行きませんでした。 どのように変更したらatoiが使える文字列になりますか? #include <stdio.h> #include <stdlib.h> static struct{   char *name; }kuda[5]={   { "もも" , "りんご" , "みかん" , "バナナ" , "パイナップル" } } char *re_3( void ){   int a = 3;   char str_h[100];   char *str;   sprintf_s( str_h , 100 , "%d" , a );   *str = *str_h; //原因はおそらくここ   return str; } void main( void ){   printf( "%s" , kuda[ atoi( re_3() ) ].name ); }

  • mainから渡した文字列を関数内で書き換え

    非常に基礎的な質問で申し訳ないのですが mainから渡した文字列を関数内で書き換えることができません。 int型の整数やchar型一文字はできるのですが。。。 例えば以下のようなソースでmainのABCをDEFに書き換えたいとき どのようにすればいいのでしょうか。 (関数の戻り値で変更という方法以外で) 以下のソースでは値は書き換わりませんでした。 void func(char *str2) { str2 = "DEF"; } int main() { char str1[20] = "ABC" printf("%s", str1); //ABC func(str1); printf("%s", str1); //DEFになるようにしたい }

  • sprintf の使い方について

    C 言語の超初心者につき,おろかな質問をお許しください。 sprintf 関数について char str[40]; sprintf(str, "hogehoge %d", hoge_int); という文法はよく目にするのですが char *str; sprintf(str, "hogehoge %d", hoge_int); という文法はあまり見たことがありません。 両方ともちゃんと動くのですが,後者は文法的に正しいのでしょうか? char str[40] とした場合でも char *str とした場合でも, str は文字列の最初の文字のアドレスを表すんだったと思うんですが。 違いが分からず,混乱しています。 初心者につき,間抜けな質問をしているかもしれませんが,ご教授くださると幸いです。

  • C言語のint型の配列が分かりません

    #include<stdio.h> int main(void) { int str[ ]={0,1,2} printf("%s\n", str); return 0; } というプログラムをC言語でつくってみましたが動きません.(012と表示されて欲しかったのですが) int str[ ]={1,2,3}の部分をchar str[ ]={'0','1','2'}とすれば動きます. そこで質問なのですが, printf("~%s~", (配列名));  はchar型の配列にしか適応できないのですか? ※追記 puts関数の定義は int puts (const char *str); であるそうなので char型の仮引数にはchar型のアドレスを渡さなければいけません. ではprintf関数の定義は一体どんなものなのですか?

  • 次のようにセットされている文字列を出力するプログラムを作成したのですが

    次のようにセットされている文字列を出力するプログラムを作成したのですが、引数を渡すことができません。まだ、初心者でどこがいけないかが分かりません。ご教授をお願いいたします。 int _tmain(int argc, _TCHAR* argv[]) { int a=0; char name_main[50]; set_name(name_main); printf( "セットされている名前は%sです", name_main ); return 0; } void set_name(char *name) { int n=0; char *str=NULL; char str2[50] = "yamada taro"; str = (char*) malloc( sizeof(char) * 50); memcpy( str, str2, 50 );

  • 文字列のコピー

    C言語で、文字列をコピーする関数で、処理が止まってしまいます。 以下が実行したプログラムです。 #include<stdio.h> #include<string.h> void copy(char *str2 ,char *str3){ strcpy(str2,str3); } void input(char* str){ scanf("%s",str); } int main (void){ char *a; char *b="TEST"; printf("%s\n",b); input(a); copy(b,a); printf("%s",b); return(0);} copy(b,a);で処理が止まってしまいます。入力した文字列を表示できるように、御指摘お願いします。

  • ftoa の作り方

    atofの逆の変換ftoaの作り方を教えてください。あるいは参考になるページを教えてください。 #include <stdio.h> void ftoa(char *string, double f, int figure) { sprintf(string, "%.*f", figure, f); } main() { char str[10]; ftoa(str, 0.3532, 4); puts(str); } 関数ftoaの部分はネット上で見つけたもので、このように書けば4行ですむらしいのですがどうも実行結果は  0.123400 というふうに4桁に指定しているのに6桁ででてきてしまいます(私の使っているコンピュータでは)。main関数は私が描いたのですが、まずいところがあったら教えてください。ちなみに私はsprintfの使い方がわかっていません。

  • 文字列を関数に渡すぷろぐらむなのですがおかしいです。

    <ソース> #include<stdio.h> #include<stdlib.h> void str(char a[]); int main() { char st[10]="abcde"; str(st); str("ABCabc123"); return 0; } void str(char a[]) { int i; printf("%s\n",a); i=0; while(a[i]){ a[i]=toupper(a[i]); putchar(a[i]); i++; } putchar('\n'); } 分からないところがあるので質問します。 toupperは、大文字にするんですよね。 putcharは、基本的にchar型でしたっけ? putsとgetsは、int型でしたっけ? 後、プログラムが暴走してます。 どこがおかしいんでしょう?

  • 文字列のコピーについて

    下記<コード1>と<コード2>はどちらも同じに思えますが、実際には<コード2>は”不正な処理”で終了してしまいます。 いろいろ考えたのですが、どうしても違いが理解できません。 理由がお分かりの方お願いいたします。 <コード1> char *str_copy(char *d , const char *s){ char *t=d; while(*d++=*s++) ; return (t); } int main(void) { char s1[128]="ABCD"; char s2[128]="EFGH"; str_copy(s2,s1); <コード2> char *str_copy(char *d , const char *s){ char *t=d; while(*d++=*s++) //不正な処理 ; return (t); } int main(void) { char s1[128]="ABCD"; char *ptr ="EFGH"; str_copy(ptr,s1);

  • 文字列の扱いについて教えてください

    #include<cstdio> #include<cstring> #define _CRT_SECURE_NO_DEPRECATE 1 #define MAXBUFF 256 void s_swap(char* str_a, char* str_b) { char str_dummy[MAXBUFF]; strcpy_s(str_dummy,strlen(str_dummy),str_a); ★ strcpy_s(str_a,strlen(str_a),str_b); ★ strcpy_s(str_b,strlen(str_b),str_dummy); } void main(void) { char* str_a = "ABC"; char* str_b = "DEF"; int a; printf("呼出前:str_a=%s, str_b=%s\n", str_a, str_b); s_swap(str_a,str_b); printf("呼出後:str_a=%s, str_b=%s\n", str_a, str_b); } str_aとstr_bの中身を入れ替える処理で、エラーや警告はでないのですが ★のところで実行失敗します。 昔から文字列の処理は苦手でどのように攻略したらよいのか 解説していただけないでしょうか。

専門家に質問してみよう