本稿のアップデート版が執筆者のサイトで掲載されています。併せてご参照ください。
はじめに
この連載では、最初に問題のあるコードを示します。前回に引き続き今回も、まずはコードだけを見て、どこに問題があるのか考えてみてください。
コードの後には、コードに含まれる脆弱性を見つけるためのヒントや、コードが行おうとしていることを理解するために役立つ背景知識などを説明します。コードを見ただけではどこに問題があるのか分からない、といった場合は、これらの説明を手がかりに考えてみてください。
どこに問題があるのか分かったら、次にどのように修正すべきかを考えましょう。修正方法は一通りとは限りません。むしろ、複数の修正方法が考えられることが多いと思います。
最後に、実際にどのような修正が行われたか説明します。自分が考えた修正案と比較してみてください。
オープンソースの画像処理ソフトウェア「ImageMagick」
今回はオープンソースの画像処理ソフトウェアであるImageMagickを取り上げます。ImageMagickは、GIF、JPEG、PNG、PDF、TIFFなど100種類以上の画像ファイルフォーマットに対応するCで書かれたプログラムです。ユーザーは、コマンドラインから呼び出すことでImageMagickが提供するさまざまな機能を利用できます。APIが公開されており、C++、Java、Lisp、.NET、Perl、PHP、Python、Rubyなどさまざまな言語で書かれたプログラムからその機能を呼び出すことも可能です。画像フォーマットの変換やサイズの変更など単純な作業から、画像ファイルの合成、動画の作成、独自の数学的処理を画像ファイルに適用するなどより高度な機能も提供しているのが特徴です。
画像処理プログラムには過去、さまざまな脆弱性が発見されてきました。ImageMagickもご多分に漏れず、CVEを検索するだけでも過去に37件の脆弱性が発見されていることが分かります。ちなみに、脆弱性の種類を分類すると表1のようになります。
脆弱性の分類 | 件数 |
バッファオーバーフロー | 13件(35%) |
整数オーバーフロー | 7件(19%) |
サービス運用妨害 | 6件(16%) |
コマンドインジェクション | 4件(10%) |
書式指定出力 | 3件(8%) |
ファイル入出力関連 | 2件(5%) |
符号エラー | 1件(2%) |
権限昇格 | 1件(2%) |
合計 | 37件 |
上記の表に挙げられた脆弱性の一つ一つを詳細に解析したわけではありませんが「バッファオーバーフロー」の原因を詳細に探れば、過去の連載でも紹介した整数オーバーフローが問題の根本的原因であるケースも見られるのではないかと推測されます。
さて、以下のコードはImageMagick バージョン6.3.4のmagick/blob.cで定義されているReadBlobString()からの抜粋です。
ImageMagickの脆弱性:その1
MagickExport char *ReadBlobString(Image *image, char *string) { register const unsigned char *p; register long i; ssize_t count; unsigned char buffer[1]; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); for (i=0; i < (long) MaxTextExtent; i++) { p=ReadBlobStream(image,1,buffer,&count); if (count != 1) { if (i == 0) return((char *) NULL); break; } string[i]=(char) (*p); if ((string[i] == '\n') || (string[i] == '\r')) break; } string[i]='\0'; return(string); }
ReadBlobString()関数は、BLOBファイルから文字列を読み取る関数です。改行文字もしくはEOFに達すると読み取りを終了します。第1引数には画像ファイルへのポインタを、第2引数にはこれから読み取る文字(バッファ)が置かれたアドレスを指定します。それではこのコードのどこに問題があるのか考えてみましょう。
脆弱性の解説
変数stringは文字型配列で、その長さはMaxTextExtentであることが分かります。ループのインデックスの値がMaxTextExtentと等しいとき、string[i] = '\0'は用意されたバッファを1バイトはみ出してNULL文字を書き込んでしまう問題があることに気づけたでしょうか。このように1バイトはみ出してバッファに書き込んでしまう脆弱性は、off-by-one errorと呼ばれることもあります。
この脆弱性が攻撃されると、プログラムを実行しているユーザーの権限で任意のコードを実行されてしまう危険があります。
ImageMagick バージョン6.3.5ではこの問題を次のように修正しました。
- for (i=0; i < (long) MaxTextExtent; i++) { + for (i=0; i < (MaxTextExtent-1L); i++) {
1バイトはみ出した書き込みを行わないために、ループ変数の範囲を-1Lしていることが分かります。