1月5日(水)1、3コマ目
今日、やったこと
バッファオーバーフロー
今日のホワイトボード
バッファオーバーフローとは
バッファオーバーランとも呼ばれる。
確保済みメモリサイズを超えたサイズのデータをコピーして、未確保部分を上書きすること。
| 図 バッファオーバーフロー |
実は、コンピュータシステムの重大インシデントの要因ナンバーワン(と思われる)がバッファオーバーフロー。C言語特有の「メモリ操作が簡単にできる」+「メモリ保護がない」から簡単に発生する。いやらしいのは発生するパターンが限定されるケースが多いこと。(再現性が高くない)
メモリは
以下の3つのエリアに分けて使われる。
| 図 メモリ上のエリア |
名前と利用目的をおさえておいてください。
サンプルプログラムをデバッガで確認
デバッガでサンプルプログラム実行時のメモリの状態を確認する。
メモリ番地および中身の確認にはxコマンドを使う。
| 図 xコマンドで変数auth_flagのメモリエリアをチェック |
サンプルプログラムについて
(一応)認証を行うプログラム。
コマンドライン引数にて、認証に使うパスワードを指定して実行。
なお、認証OKなパスワードは「SamplePassword」。
しかしながら、パスワード「aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa」(aを30個)を指定すると、認証OKとなる。
原因はバッファオーバーフローによるメモリ破壊。実際にメモリがどうなっているかをデバッガを使って確認してみる。
デバッガで確認(正常系の場合)
引数に認証が成功する「SamplePassword」を指定して実行。
バッファオーバーフローが発生するstrcpy()の前後にブレークポイントを設定してデバッガ上で実行してみる。
〇strcpy()前
2つのローカル変数auth_flag、password_bufferを確認すると、以下のようになっている。
| 図 [正常系]strcpy()前 |
〇strcpy()後
password_bufferには「SamplePassword」がコピーされている。
auth_flagは0のまま。
| 図 [正常系]strcpy()後 |
結局、check()関数はauth_flagの値0を返すため、main()にて認証失敗と出力される。
デバッガで確認(異常系の場合)
引数になぜか認証成功になるAを30個で実行。
〇strcpy()後
メモリを確認するとauth_flagのエリアに0x41が書き込まれている。これは、strcpy()でpassword_bufferに引数A30個をコピーしたため、password_bufferのエリアを超えてauth_flagのエリアまで書き込んだために発生。
| 図 [異常系]strcpy()の後 |
check()関数はauth_flagの値をそのまま返すため、0以外の値が返る。
main()関数ではcheck()の戻り値が0ではないため、認証成功と出力される。
結局
主な原因はstrcpy()でコピー先のサイズを考えずに書き込んだことだが、他にも原因がある。
| 図 不具合の要因 |
要素1 strcpy()
strcpy()は
コピー先のサイズに関係なく、コピー元の先頭から'\0'までをコピーする
ため、password_bufferのエリアを超えて、auth_flagのエリアまで上書きをしてしまった。
要素2 check()の戻り値
変数auth_flagの値を返しているが、auth_flagは
- 初期値0で宣言
- strcmp()で”SamplePassword”と一致したら、1を代入
の2か所で代入を行っている。
strcpy()でバッファオーバーフローして上書きされても正しい値を代入することがない。
要素3 c言語のif()
カッコ内が
- 0なら条件不成立
- 0以外なら条件成立
です。
| 図 C言語とJavaのif() |
c言語にはtrue、falseがないため、このような仕様になっている。
よって、check()関数の戻り値が0以外なら認証OKになってしまう。
以上、バッファオーバーフローだけではなく、3つの要素が重なって不具合を発生させている。
次回は
バッファーオーバーフローでもっといろいろと上書きしてみる。