SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

実例で学ぶ脆弱性対策コーディング

glibライブラリに潜む脆弱性をつぶすパッチ

実例で学ぶ脆弱性対策コーディング 第1回

  • X ポスト
  • このエントリーをはてなブックマークに追加

gbase64.cの問題点

 g_base64_encode()では、整数オーバーフローが発生する可能性があります。base64変換した結果を収めるメモリの大きさを計算する部分を見てみましょう。

/* len は base64 エンコードする元データの長さ */
out = g_malloc(len * 4 / 3 + 4);

 base64エンコードするとその大きさはおよそ4/3倍になります。素直に変換後の長さを計算しており、分かりやすいコードなのですが、入力データの長さlenはとても大きな値になっているかもしれません。

 (len * 4 / 3 + 4)を計算する過程は以下のようになるでしょう。

ステップ1: a = (len * 4)
ステップ2: b = a / 3
ステップ3: c = b + 4

 lenがとても大きな値の場合、ステップ1の計算結果が整数型で表現できる大きさを超えてしまう可能性があります。その場合、あふれた桁の部分は無視され、計算結果は思っていたよりもごく小さな値となってしまいます(C言語仕様ではmodwrapセマンティクスと呼ばれています)。

実際に試してみよう

 計算結果がラップアラウンドしてしまう様子を確認してみましょう。

[int-a.c]
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[]){
  if (2 <= argc){
    errno = 0;
    long i =  strtol(argv[1], (char **)NULL, 10);
    if (errno == 0){
      printf("%ld * 4 == %ld\n", i, i * 4);
      printf("%ld * 4 /3 == %ld\n", i, (i * 4)/3 );
    }
  }
}

 long型が32ビットで表現される環境で動かしてみるとこうなりました。

[実行結果]
$ ./int-a 1073741825
1073741825 * 4 == 4
1073741825 * 4 / 3 == 1
$ 

 1073741825のビット表現は以下のようになります。

MSB                                       LSB
   0100 0000 0000 0000 0000 0000 0000 0001

 31ビット位置と1ビット位置の2か所に1、それ以外は0です。4倍するということは結局左向きに2ビット分シフトするのだと考えれば、結果が4になることが理解しやすいでしょう。例え数学的な計算結果が整数型に収まる値であっても、途中で結果があふれてしまっていては、正しい値を求めることはできないのです。

どのような影響があるか

 この計算結果はg_malloc()の引数に使われています。つまり、確保するメモリ領域が意図していたよりもずっと小さくなる、という結果を招きます。そこにデータを書き込んでいくことで、ヒープオーバフローが発生します。この問題にはCVE-2008-4316という識別番号がつけられていますが、その説明にはこうあります。

 (CVE-2008-4316 の Description より)

 Multiple integer overflows in glib/gbase64.c in GLib before 2.20

 allow context-dependent attackers to execute arbitrary code via a

 long string that is converted either (1) from or (2) to a base64

 representation.

 (日本語訳)

 GLib の 2.20 より前のバージョンの glib/gbase64.c には

 複数の整数オーバフローがある。実行時の状況にもよるが、

 長い文字列に関する

 (1) base64 からの変換、あるいは

 (2) base64 への変換

 によって、任意のコードを実行させることが可能になる。

 base64変換を使うアプリケーションといえば、メールアプリやwebブラウザなどが思い浮かびます。攻撃コードを仕込んだスパムメールの閲覧、あるいは細工したwebページのアクセスによって、被害者のPC上で(ユーザの権限で)攻撃コードが実行される可能性があるということです。

glibではどのように修正したか

 glibでは以下のように、除算を先に行うように修正することで、整数オーバーフローの可能性を排除しました。

/* g_malloc() の引数式が G_MAXSIZE 以上にならないことを確認している */
if (len >= ((G_MAXSIZE - 1) / 4 - 1) * 3)
    g_error(......);  // len が大き過ぎる場合はエラーにする
out = g_malloc((len / 3 + 1) * 4 + 1);

 また、g_malloc()の直前のif文により、最終的な計算結果が所定の範囲に収まっているかどうかのチェックも加えています。

len >= ((G_MAXSIZE - 1) / 4 - 1) * 3

 この条件式を変形してみると

((len * 4/3) + 4) + 1 >= G_MAXSIZE

 となります。つまりg_malloc()の引数に与えた式がG_MAXSIZEより小さい値でなければ、エラー処理を行うようにしたのです。ある式の計算結果が想定した範囲に収まっていることを確認する時、計算過程でラップアラウンドが発生することを避けるためには、このように条件式を変形して扱うことが必要になります。

計算式の中でオーバーフローを起こさないように注意しよう

 「数学的な」計算式では、そのなかの部分式の値の大きさについて考慮することはあまりないかもしれません。しかし、プログラムに実装する計算過程では、各部分式についてその値が適切な範囲に収まることまで確認すべきです。

 また、メモリを動的に確保するmalloc()では、確保するメモリサイズを指定する引数の値を正しく計算するために細心の注意を払うことが必要です。malloc()の引数に複雑な計算式を使っていたり、引数の値の範囲チェックを行なっていない場合には何らかの問題があると思ってまず間違いありません。

 皆さんが今までに書いたコードでmalloc()を使っているものがあれば、もう一度見直してみてください。そして、攻撃される可能性がないかどうか、きっちり確認することをおすすめします。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
実例で学ぶ脆弱性対策コーディング連載記事一覧

もっと読む

この記事の著者

戸田 洋三(JPCERT コーディネーションセンター)(トダ ヨウゾウ(JPCERT コーディネーションセンター))

リードアナリストJPCERTコーディネーションセンター東京工業大学情報理工学研究科修士課程修了。学生時代は、型理論および証明からのプログラム抽出を研究。その後、千葉大学総合情報処理センターのスタッフとして、学内ネットワークの運営、地域ネットワーク、IPマルチキャストの実験ネットワークであるJP-MB...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5150 2010/05/20 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング