課題です

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
勉強なう

課題です

#1

投稿記事 by 勉強なう » 12年前

以下のプログラムはxinetdから呼び出され、パスワードを知っているユーザのみがメッセージ投稿・表示を行えることを意図して作られています。ただ、このプログラムにはいくつか脆弱性が存在します。発見した脆弱性と攻撃方法、またその修正方法について解説してください。

コード:

   1 #include <stdio.h>
   2 #include <string.h>
   3 #include <stdlib.h>
   4 #include <unistd.h>
   5 #include <crypt.h>
   6 #define SALT "$1$54lt$"
   7 #define HASH SALT"4OOZtZMXwyHi.bwq2QS6U0"
   8 char *get_line()
   9 {
  10   char buf[256];
  11   gets(buf);
  12   return strdup(buf);
  13 }
  14 int main()
  15 {
  16   char *name=NULL, *passwd=NULL, *msg=NULL, buf[256];
  17   int trial_num, pass = 0;
  18   setbuf(stdout, NULL);
  19   for(trial_num = 0; trial_num < 10;trial_num++){
  20     printf("USER: ");
  21     name = get_line();
  22     printf("PASS: ");
  23     passwd = get_line();
  24     if(strcmp(HASH, crypt(passwd, SALT)) == 0){
  25       pass = 1;
  26       break;
  27     }
  28     snprintf(buf, sizeof(buf), "NG(%s)\n", name);
  29     printf(buf);
  30   }
  31   if(pass){
  32     printf("MSG: "); msg = get_line();
  33     snprintf(buf, sizeof(buf), "echo [%s]%s >> /tmp/memo.log", name, msg);
  34     system(buf);
  35     system("cat /tmp/memo.log");
  36   }
  37   return 0;
  38  }
このコードの脆弱性として自分が見つけたものは、11行目のバッファーオーバーフローと24行目の初期化されてない変数です。
質問
①上であげた2つの脆弱性はあっているのか?
②また上であげたものがあっているならどのような攻撃方法をされるのか・
③上の2つ以外に脆弱性はありますか?

かずま

Re: 課題です

#2

投稿記事 by かずま » 12年前

勉強なう さんが書きました: ③上の2つ以外に脆弱性はありますか?
snprintf の返却値が sizeof(buf)以上の場合をチェックして
いないので、name や msg が指す文字列が 255バイト以下でも
buf には部分文字列しか入らないことがあります。
例えば、/tmp/me というファイルができるかもしれません。

他のユーザが使ったら、すでに /tmp/memo.log があって
書き込みできないでしょう。

strdup が NULL を返す場合もないとは言えません。

勉強なう

Re: 課題です

#3

投稿記事 by 勉強なう » 12年前

その脆弱性によって攻撃されてしまうのでしょうか?
②はわからないでしょうか?

アバター
へにっくす
記事: 634
登録日時: 13年前
住所: 東京都

Re: 課題です

#4

投稿記事 by へにっくす » 12年前

勉強なう さんが書きました:その脆弱性によって攻撃されてしまうのでしょうか?
②はわからないでしょうか?
貴方の挙げた2つについて②をいいますと、
それぞれUSER、PASS、MSGが表示された時、256バイト以上入力することが攻撃にあたりますね。

とりあえず私も。。
strdupは、文字列の複製なので、必要無くなったらfreeで解放する必要があります。
_strdup、_wcsdup、_mbsdup - MSDN
それをしていないので、メモリが圧迫されシステムが不安定になるかもしれません。
(要するに何も攻撃されなくとも、通常の運用で落ちる可能性がある)
written by へにっくす

かずま

Re: 課題です

#5

投稿記事 by かずま » 12年前

勉強なう さんが書きました: その脆弱性によって攻撃されてしまうのでしょうか?
このプログラムが正常に終了しないことも脆弱性の一つでしょう。
攻撃とはどういうことを言っていますか?
システムを落とすこと?
読めないファイルを読みだすこと?

仮にこのプログラムが root の権限で動いていたとしましょう。
このプログラムのパスワードを知っているユーザが MSG: に対して
; shutdown -h now; echo abc
と入力したら
system("echo [user]; shutdown -h now; echo abc >>/tmp/memo.log");
でシステムが落ちます。

ユーザ入力をそのまま system() で実行するのは危険です。

ユーザ入力をそのまま printf() の書式に使うのも脆弱性の一つです。
USER: に対して %s%s%s と入力したら、
printf("NG(%s%s%s)\n"); を実行して、おかしなことになります。
勉強なう さんが書きました: ②はわからないでしょうか?
gets のバッファオーバーフローなら、スタック上にある get_line() の
リターンアドレスを壊してどこに飛んでいくかわからない。
バッファオーバフローで書き込んだところに飛ばせば、
そこに書かれた攻撃者のプログラムを実行できます。

最初の get_line() の gets() で EOF だと、passwd は 未初期化の buf
のコピーを指しますが、こちらは被害は少ないでしょう。

勉強なう

Re: 課題です

#6

投稿記事 by 勉強なう » 12年前

貴方の挙げた2つについて②をいいますと、
それぞれUSER、PASS、MSGが表示された時、256バイト以上入力することが攻撃にあたりますね。

>>24行目の方も256バイト以上の入力でいいんでしょうか?

勉強なう

Re: 課題です

#7

投稿記事 by 勉強なう » 12年前

かずま さんが書きました:
勉強なう さんが書きました: その脆弱性によって攻撃されてしまうのでしょうか?
このプログラムが正常に終了しないことも脆弱性の一つでしょう。
攻撃とはどういうことを言っていますか?
システムを落とすこと?
読めないファイルを読みだすこと?

仮にこのプログラムが root の権限で動いていたとしましょう。
このプログラムのパスワードを知っているユーザが MSG: に対して
; shutdown -h now; echo abc
と入力したら
system("echo [user]; shutdown -h now; echo abc >>/tmp/memo.log");
でシステムが落ちます。

ユーザ入力をそのまま system() で実行するのは危険です。

ユーザ入力をそのまま printf() の書式に使うのも脆弱性の一つです。
USER: に対して %s%s%s と入力したら、
printf("NG(%s%s%s)\n"); を実行して、おかしなことになります。
勉強なう さんが書きました: ②はわからないでしょうか?
gets のバッファオーバーフローなら、スタック上にある get_line() の
リターンアドレスを壊してどこに飛んでいくかわからない。
バッファオーバフローで書き込んだところに飛ばせば、
そこに書かれた攻撃者のプログラムを実行できます。

最初の get_line() の gets() で EOF だと、passwd は 未初期化の buf
のコピーを指しますが、こちらは被害は少ないでしょう。

③のことで
すごい知識ですね!! 感激しました。
自分はそんなスキルないので全部理解できないので、
もう少しまとめてもらえないでしょうか?
「どこの脆弱性をどのようにコードを書き換えればいいのか?」
面倒なこと言ってすいません。

アバター
へにっくす
記事: 634
登録日時: 13年前
住所: 東京都

Re: 課題です

#8

投稿記事 by へにっくす » 12年前

勉強なう さんが書きました:貴方の挙げた2つについて②をいいますと、
それぞれUSER、PASS、MSGが表示された時、256バイト以上入力することが攻撃にあたりますね。

>>24行目の方も256バイト以上の入力でいいんでしょうか?
初期化していないとはどの変数ですかね。

コード:

24  if(strcmp(HASH, crypt(passwd, SALT)) == 0){
HASH、SALTは定数ですしpasswdはその前のget_line()でセットしています。
なので24行目は「変数が初期化していない」とは言えないと思います。
(かずまさんがpasswdについて述べられていますが、結局はget_line関数の中の話ですので)
修正方法はまず自分で考えてみてください(質問内容には書かれていないので)。
たとえばget_line関数で使用しているgetsはVC++ならgets_sに置き換えるとかね(もちろんこれだけではだめですよ?)。
written by へにっくす

勉強なう

Re: 課題です

#9

投稿記事 by 勉強なう » 12年前

へにっくす さんが書きました:
勉強なう さんが書きました:貴方の挙げた2つについて②をいいますと、
それぞれUSER、PASS、MSGが表示された時、256バイト以上入力することが攻撃にあたりますね。

>>24行目の方も256バイト以上の入力でいいんでしょうか?
初期化していないとはどの変数ですかね。

コード:

24  if(strcmp(HASH, crypt(passwd, SALT)) == 0){
HASH、SALTは定数ですしpasswdはその前のget_line()でセットしています。
なので24行目は「変数が初期化していない」とは言えないと思います。
(かずまさんがpasswdについて述べられていますが、結局はget_line関数の中の話ですので)
修正方法はまず自分で考えてみてください(質問内容には書かれていないので)。
たとえばget_line関数で使用しているgetsはVC++ならgets_sに置き換えるとかね(もちろんこれだけではだめですよ?)。

HASHとSALTは変数じゃないんでしょうか?

get_lineについて調べましたが、自分ではわかりそうにないので、できればどこをどのように修正するか教えてもらえないでしょうか?

アバター
へにっくす
記事: 634
登録日時: 13年前
住所: 東京都

Re: 課題です

#10

投稿記事 by へにっくす » 12年前

勉強なう さんが書きました:HASHとSALTは変数じゃないんでしょうか?
6行目と7行目で#defineで定義してますよね?
こういうのは変数でなく「定数」です。
C言語のdefineについて
コンパイルするときに#defineで定義したものは置換しますので、実際にはこう書いているのと同じです。

コード:

24  if(strcmp("$1$54lt$""4OOZtZMXwyHi.bwq2QS6U0", crypt(passwd, "$1$54lt$")) == 0){
勉強なう さんが書きました:get_lineについて調べましたが、自分ではわかりそうにないので、できればどこをどのように修正するか教えてもらえないでしょうか?
こんな感じになるかな(あくまでも一例です)

コード:

char *get_line()
{
  char buf[256]={0}; // 初期化を忘れずに(初期化されてないと質問内容にあったのでここぐらいは答えてほしかったなあ^^;)
  gets_s(buf, sizeof(buf) - 1); // これで256文字以上入力すると、CRTが無効な引数を検出する。
  return strdup(buf); // 入力していない場合は空文字列の複製が返る
}
あとはCRTが無効な引数を検出した場合に呼び出す関数を設定します。
_set_invalid_parameter_handler - MSDN
written by へにっくす

勉強なう

Re: 課題です

#11

投稿記事 by 勉強なう » 12年前

へにっくす さんが書きました:
勉強なう さんが書きました:HASHとSALTは変数じゃないんでしょうか?
6行目と7行目で#defineで定義してますよね?
こういうのは変数でなく「定数」です。
C言語のdefineについて
コンパイルするときに#defineで定義したものは置換しますので、実際にはこう書いているのと同じです。

コード:

24  if(strcmp("$1$54lt$""4OOZtZMXwyHi.bwq2QS6U0", crypt(passwd, "$1$54lt$")) == 0){
勉強なう さんが書きました:get_lineについて調べましたが、自分ではわかりそうにないので、できればどこをどのように修正するか教えてもらえないでしょうか?
こんな感じになるかな(あくまでも一例です)

コード:

char *get_line()
{
  char buf[256]={0}; // 初期化を忘れずに(初期化されてないと質問内容にあったのでここぐらいは答えてほしかったなあ^^;)
  gets_s(buf, sizeof(buf) - 1); // これで256文字以上入力すると、CRTが無効な引数を検出する。
  return strdup(buf); // 入力していない場合は空文字列の複製が返る
}
あとはCRTが無効な引数を検出した場合に呼び出す関数を設定します。
_set_invalid_parameter_handler - MSDN


ありがとうございました。
11行目のバッファオーバーフローを防ぐためにはそのような方法もあるのですね。
①fgets関数などでもふせげるでしょうか?
②ということはこのコードの脆弱性は11行目のバッファオーバーフローの脆弱性だけということですか?
 ほかにもあればその部分と修正方法、どのように攻撃されてしまうのか教えてくださいorz

アバター
へにっくす
記事: 634
登録日時: 13年前
住所: 東京都

Re: 課題です

#12

投稿記事 by へにっくす » 12年前

勉強なう さんが書きました:①fgets関数などでもふせげるでしょうか?
②ということはこのコードの脆弱性は11行目のバッファオーバーフローの脆弱性だけということですか?
 ほかにもあればその部分と修正方法、どのように攻撃されてしまうのか教えてくださいorz
①fgetsでは代わりになりません。

コード:

fgets(buf, sizeof(buf) - 1,stdin);
この場合は、256文字以上入力した場合エラーにはならず、255文字とりだして戻ります。切り捨てされた文字は次の入力に行きますので意図した動きにはなりませんよ。
こちらで実験してみるとUSERで256文字以上入力すると、256文字からの入力はPASSに行ってしまいます。
②えーとかずまさんの指摘みてますかね? (^^;) あとはかずまさんにお任せします。
written by へにっくす

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: 課題です

#13

投稿記事 by softya(ソフト屋) » 12年前

セキュリティ・キャンプ2013のソフトウェアセキュリティクラスの課題と判明しましたので、一時的に明確になるまで回答を停止してもらってよいでしょうか。
「課題( ̄◇ ̄;) • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=13168#p105360

とりあえず、課題締め切りの2013年6月10日(月)17:00(必着)まで保留とさせて頂きます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

勉強なう

Re: 課題です

#14

投稿記事 by 勉強なう » 12年前

ありがとうございました

勉強なう

Re: 課題です

#15

投稿記事 by 勉強なう » 12年前

ありがとうございました

閉鎖

“C言語何でも質問掲示板” へ戻る