これらの脆弱性は、Yves Younan によって発見されました。
Pidgin は汎用のチャット クライアントで、世界中の数百万のシステムで使用されています。Pidgin チャット クライアントを使用することで、複数のチャット ネットワークで同時に通信することが可能になります。今回 Talos は、Pidgin での MXit プロトコル処理方法に複数の脆弱性があることを確認しました。これらの脆弱性は以下の 4 つのカテゴリに分類されます。
- 情報漏えい
- サービス拒否(DoS)
- ディレクトリ トラバーサル
- バッファ オーバーフロー
確認された脆弱性は以下のとおりです(CVE 番号順に掲載)。
CVE-2016-2365 – Pidgin MXIT Markup Command Denial of Service Vulnerability(Pidgin MXIT のマークアップ コマンドにおけるサービス拒否(DoS)に関する脆弱性)
CVE-2016-2366 – Pidgin MXIT Table Command Denial of Service Vulnerability(Pidgin MXIT のテーブル コマンドにおけるサービス拒否(DoS)に関する脆弱性)
CVE-2016-2367 – Pidgin MXIT Avatar Length Memory Disclosure Vulnerability(Pidgin MXIT のアバターの長さに起因するメモリ開示に関する脆弱性)
CVE-2016-2368 – Pidgin MXIT g_snprintf Multiple Buffer Overflow Vulnerability(Pidgin MXIT の g_snprintf に起因する複数のバッファ オーバーフローに関する脆弱性)
CVE-2016-2369 – Pidgin MXIT CP SOCK REC TERM Denial of Service Vulnerability(Pidgin MXIT の CP SOCK REC TERM に起因するサービス拒否(DoS)に関する脆弱性)
CVE-2016-2370 – Pidgin MXIT Custom Resource Denial of Service Vulnerability(Pidgin MXIT のカスタム リソースにおけるサービス拒否(DoS)に関する脆弱性)
CVE-2016-2371 – Pidgin MXIT Extended Profiles Code Execution Vulnerability(Pidgin MXIT の拡張プロファイルににおけるコード実行に関する脆弱性)
CVE-2016-2372 – Pidgin MXIT File Transfer Length Memory Disclosure Vulnerability(Pidgin MXIT のファイル転送の長さに起因するメモリ開示に関する脆弱性)
CVE-2016-2373 – Pidgin MXIT Contact Mood Denial of Service Vulnerability(Pidgin MXIT のコンタクト ムードにおけるサービス拒否(DoS)に関する脆弱性)
CVE-2016-2374 – Pidgin MXIT MultiMX Message Code Execution Vulnerability(Pidgin MXIT の MultiMX メッセージにおけるコード実行に関する脆弱性)
CVE-2016-2375 – Pidgin MXIT Suggested Contacts Memory Disclosure Vulnerability(Pidgin MXIT の連絡先候補におけるメモリ開示に関する脆弱性)
CVE-2016-2376 – Pidgin MXIT read stage Ox3 Code Execution Vulnerability(Pidgin MXIT の read stage Ox3 に起因するコード実行に関する脆弱性)
CVE-2016-2377 – Pidgin MXIT HTTP Content-Length Buffer Overflow Vulnerability(Pidgin MXIT の HTTP Content-Length に起因するバッファ オーバーフローに関する脆弱性)
CVE-2016-2378 – Pidgin MXIT get_utf8_string Code Execution Vulnerability(Pidgin MXIT の get_utf8_string に起因するコード実行に関する脆弱性)
CVE-2016-2380 – Pidgin MXIT mxit_convert_markup_tx Information Leak Vulnerability(Pidgin MXIT の mxit_convert_markup_tx に起因する情報漏えいに関する脆弱性)
CVE-2016-4323 – Pidgin MXIT Splash Image Arbitrary File Overwrite Vulnerability(Pidgin MXIT のスプラッシュ イメージに起因する任意のファイル上書きに関する脆弱性)
情報漏えいに関する脆弱性
Talos は、Pidgin チャット クライアントの一部である MXit プロトコルの実装に、情報漏えいに関する 4 件の脆弱性があることを発見しました。この脆弱性は以下のとおりです。
- CVE-2016-2367
- CVE-2016-2372
- CVE-2016-2375
- CVE-2016-2380
CVE-2016-2375
この脆弱性が原因で、サーバから送信される、巧妙に細工された MXIT データにより、範囲外の読み込みが行われる恐れがあります。mxit/protocol.c ファイルの 2,020 行目にある mxit_parse_cmd_suggestcontacts 関数で、属性の数が、着信パケットから count 変数へと読み込まれます。
2020 count = atoi( records[0]->fields[3]->data );
この値は、その後の 2,030 行目でループの範囲として使用され、そのループ インデックスは 2,034 ~ 2,036 行で配列インデックスとして使用されます。
2030 for ( j = 0; j < count; j++ ) {
2034 fname = records[0]->fields[4 + j]->data; /* field name */
2035 if ( records[i]->fcount > ( 2 + j ) )
2036 fvalue = records[i]->fields[2 + j]->data; /* field value */
これらの場所で設定されたポインタは、その後のデータの読み込みに使用されるため、範囲外の読み込みが行われる恐れがあります。データは 2,056 ~ 2,059 行の例が示すように、結果フィールドにコピーされます。
2056 else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) {
2057 /* nickname */
2058 g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) );
2059 }
情報の多くはサーバに送り返されることはありません。そのため、範囲外の読み込みが行われても、メモリ ページへのアクセスが不可能であれば、大抵の場合はクラッシュの原因となるだけです。ただし、追加ボタンが押された場合は、mxit/profile.c で定義されているコールバックが呼び出されます。これにより、ニックネームがサーバに返されると、サーバに返される情報が漏えいする恐れがあります。それは、このニックネームにはメモリからリークされた情報が含まれているためです。
CVE-2016-2380
Pidgin での MXIT プロトコルの処理方法が原因で、情報漏えいが発生します。巧妙に細工された MXIT データがサーバに送信されると、範囲外の読み込みが行われる恐れがあります。ユーザは特定の文字列を入力するように促され、その文字列が誤って変更されることにより、範囲外の読み込みが行われる恐れがあります。
Pidgin によってメッセージがサーバに送信される場合、そのメッセージは libpurple(HTML ベース)のマークアップから MXIT マークアップへの変換が必要になります。この変換を行うために、markup.c ファイルで定義されている mxit_convert_markup_tx 関数が呼び出されます。
この関数は、データを古い文字列メッセージから新しい mx 文字列へとコピーし、その過程で変換を行います。
しかし、1,146 ~ 1,154 行では、マークアップの変換とフォント色の変更が行われていますが、残りの文字列の長さはチェックされていません。
1146 else if ( purple_str_has_prefix( &message[i], "<font color=" ) ) {
1147 /* font colour */
1148 tag = g_new0( struct tag, 1 );
1149 tag->type = MXIT_TAG_COLOR;
1150 tagstack = g_list_append( tagstack, tag );
1151 memset( color, 0x00, sizeof( color ) );
1152 memcpy( color, &message[i + 13], 7 );
1153 g_string_append( mx, color );
1154 }
メッセージの現在の位置である 1,146 行目では、当該文字列が <font color= で始まっているかどうかが比較されます。始まっている場合は、「=」の後にある 1 要素後から 7 バイトがコピーされ、「#」タグはスキップされることになります。ただし、「<font color=」が文字列の終わりにある場合は、メッセージの範囲外の読み込みが発生します。「=」の先にある 1 バイトがスキップされることで、NULL 終端文字列がスキップされ、この文字列から後の 7 バイトが、サーバに送信される文字列である mx にコピーされます。
DoS の問題に加えて、CVE-2016-2367 と CVE-2016-2372 も情報漏えいの原因になる恐れがあります。CVE-2016-2367 は、メモリ内の機密情報がアバターの後ろのデータに漏えいする恐れがあります。このデータはアバターのコピー時に転送されるため、機密情報の漏えいの原因になる恐れがあります。CVE-2016-2372 は、メモリ内の機密情報を、受け取ったファイルの末尾に追加することにより、この機密情報を漏えいする恐れがあります。
Denial of Service(DoS)の脆弱性
Talos は、Pidgin チャット クライアントの一部である MXit プロトコルの実装に、DoS に関する 6 件の脆弱性があることを発見しました。
- CVE-2016-2365
- CVE-2016-2366
- CVE-2016-2367
- CVE-2016-2369
- CVE-2016-2370
- CVE-2016-2372
CVE-2016-2365
この脆弱性が原因で、サーバ経由で送信される、巧妙に細工された MXIT データにより、NULL ポインタの逆参照が行われる恐れがあります。この原因は、受け取ったコマンドを正常に実行するために必要なすべてのフィールドが提供されているかどうかのチェックが不十分であることです。メッセージでコマンドが受信されると、以下のように、「キー=値」形式の値を探すために mxit_parse_command() 関数が呼び出されて、これらのペアをハッシュテーブルに挿入します。
580 hash = command_tokenize(start); /* break into <key,value> pairs */
次に、処理対象のコマンドの種類がチェックされ、該当する関数が呼び出されます。
以下の 2 つの関数
command_imagestrip()
command_table()
は、このキー/値ペアに依存するため、定義されていない場合は NULL ポインタの逆参照の原因となります。最初の関数である command_imagestrip() は、mxit/formcmds.c の 383 行目に定義されています。393 ~ 399 行では、以下のように、この関数がキー nm、v、および dat の値を検索します。
392 /* image strip name */
393 name = g_hash_table_lookup(hash, "nm");
394
395 /* validator */
396 validator = g_hash_table_lookup(hash, "v");
397
398 /* image data */
399 tmp = g_hash_table_lookup(hash, "dat");
400 行目のチェックで、tmp が NULL ではないことは確認されますが、名前とバリデータに対する同様のチェックは行われません。これにより、以下のように、419 行目と 420 行目でこれらのチェックが行われると、NULL ポインタへの逆参照の原因となります。
419 escname = g_strdup(purple_escape_filename(name));
420 escvalidator = g_strdup(purple_escape_filename(validator));
432 ~ 439 行のキー fw、fh、layer にも同様のエラーがあります。
432 tmp = g_hash_table_lookup(hash, "fw");
433 width = atoi(tmp);
434
435 tmp = g_hash_table_lookup(hash, "fh");
436 height = atoi(tmp);
437
438 tmp = g_hash_table_lookup(hash, "layer");
439 layer = atoi(tmp);
mxit/formcmds.c の 530 ~ 543 行に定義されている command_table() 関数でも、同様のエラーが発生します。
530 tmp = g_hash_table_lookup(hash, "col");
531 nr_columns = atoi(tmp);
532
533 /* number of rows */
534 tmp = g_hash_table_lookup(hash, "row");
535 nr_rows = atoi(tmp);
536
537 /* mode */
538 tmp = g_hash_table_lookup(hash, "mode");
539 mode = atoi(tmp);
540
541 /* table data */
542 tmp = g_hash_table_lookup(hash, "d");
543 coldata = g_strsplit(tmp, "~", 0);
これらのキー/値ペアのいずれかが欠落していると、クラッシュが発生します。ネットワーク トラフィックを傍受する、悪意のあるサーバや攻撃者は、無効なデータを送信してこの脆弱性をトリガーし、クラッシュを発生させることができます。
CVE-2016-2366
この脆弱性では、サーバ経由で送信される、巧妙に細工された MXIT データにより、範囲外の読み込みが行われる恐れがあります。mxit/formcmds.c の 531 行目と 535 行目の command_table 関数は、テーブルの行と列の数をサーバから受信します。
531 nr_columns = atoi(tmp);
535 nr_rows = atoi(tmp);
これらの 2 つの値は、549 行目で配列にアクセスするために、547 行目と 548 行目のループで使用されます。
547 for (i = 0; i < nr_rows; i++) {
548 for (j = 0; j < nr_columns; j++) {
549 purple_debug_info(MXIT_PLUGIN_ID, " Row %i Column %i = %s\n", i, j, coldata[i*nr_columns + j]);
550 }
551 }
配列から読み込まれるデータはサーバに渡されないので、情報の漏えいが発生することはありませんが、メモリへのアクセス先がアクセス不可能なメモリ ページであった場合、クラッシュが発生します。ネットワーク トラフィックを傍受する、悪意のあるサーバや攻撃者は、無効なデータを送信してこの脆弱性を誘発し、クラッシュを発生させることができます。
CVE-2016-2367
この脆弱性が原因で、サーバ経由で送信される、巧妙に細工された MXIT データにより、範囲外の読み込みが行われる恐れがあります。MXIT サーバ経由でアバターを受信する際、このサーバからは CP_CHUNK_GET_AVATAR コマンドが送信されます。この処理は、mxit/protocol.c の 2,208 ~ 2,234 行目において、mxit_parse_cmd_media 関数によって行われます。2,215 行目は、以下のようになっています。
2215 mxit_chunk_parse_get_avatar( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk );
ここでは mxit_chunk_parse_get_avatar() 関数が呼び出されます。この関数は、mxit/chunk.c の 683 行目で、データからチャンクのサイズを読み込みます。
683 pos += get_int32( &chunkdata[pos], &(avatar->length) );
指定されたチャンクの長さがこのバッファより長い場合は、範囲外の読み込みが行われることになり、メモリ内の結果データが、受信したアバターの後に書き込まれます。脆弱性がトリガーされた時点のプログラムのメモリ レイアウトによっては、ページがアクセス不可能であることが原因でクラッシュが発生するか、または機密情報がメモリからファイルへと漏えいします。ユーザがこのアバターを別の場所にコピーしたり、別のユーザに送信したりすると、このデータが漏えいすることになります。
ネットワーク トラフィックを傍受する、悪意のあるサーバ、攻撃者、または悪意のあるユーザは、範囲外の読み込みに関する脆弱性をトリガーする、無効なサイズのファイルを送信することができます。これは Denial of Service(DoS)の原因となったり、アバターが別のユーザや同じユーザに送信されると、情報の漏えいが発生する原因になったりする恐れがあります。
CVE-2016-2369
この脆弱性が原因で、サーバ経由で送信される、巧妙に細工された MXIT データにより、NULL ポインタの逆参照が行われる恐れがあります。データが MXIT サーバから受信されると、関連するバイトのストリームを MXIT パケットへと解析するために、mxit/protocol.c の mxit_parse_packet() 関数が呼び出されます。
パケットが受信されると、そのデータを反映して、新しいレコードがパケット内に作成されます。
2672 rec = NULL;
2673 field = NULL;
2674 memset( &packet, 0x00, sizeof( struct rx_packet ) );
2675 rec = add_record( &packet );
add_record 関数によって、以下の処理が行われます。
2634 static struct record* add_record( struct rx_packet* p )
2635 {
2636 struct record* rec;
2637 rec = g_new0( struct record, 1 );
2638 p->records = g_realloc( p->records,
2639 sizeof( struct record* ) * ( p->rcount + 1 ) );
2640 p->records[p->rcount] = rec;
2641 p->rcount++;
2642 return rec;
2643 }
これにより、パケット内にレコードが作成され、rcount 変数が 1 つ増加します。
2,679 ~ 2,744 行では、パケットがさらに分析され、使用されているセパレータが 0x0,0x1 か 0x2 かに応じて、レコードとフィールドに分解されます。
その場合、特に興味深いのが次のコードです。
2679 while ( ( i < session->rx_i ) && ( !pbreak ) ) {
2680 switch ( session->rx_dbuf[i] ) {
2681 case CP_SOCK_REC_TERM :
2682 /* new record */
2683 if ( packet.rcount == 1 ) {
2684 /* packet command */
2885
2686 packet.cmd = atoi( packet.records[0]->fields[0]->data );
2687 }
値 CP_SOCK_REC_TERM は、レコードの末尾に到達したこと、パケットを送信するコマンドを取得することを示しています。しかし、パケットが NULL バイトから始まっている場合、そのレコードのフィールド変数が初期化されず、2,686 行目で逆参照が試行されるとクラッシュが発生します。サーバ経由で送信される、巧妙に細工された MXIT データが、Denial of Service(DoS)に関する脆弱性の原因となる恐れがあります。悪意のあるサーバは、脆弱性をトリガーする NULL バイトで始まるパケットを送信できます。
CVE-2016-2370
この脆弱性が原因で、サーバ経由で送信される、巧妙に細工された MXIT データにより、範囲外の読み込みが行われる恐れがあります。mxit/chunk.c ファイルで定義されている、mxit_chunk_parse_cr() 関数は、新しいスプラッシュ イメージなど、カスタムのリソースを解析するために使用されます。このようなタイプのリソースは、マルチメディア パケットの一部として送信されます。
573 行目では、チャンクの長さが解析対象のチャンクから読み込まれますが、範囲のチェックは行われません。このチャンクには、577 行目で設定される 1 つまたは複数のリソース チャンクが含まれる場合があります。このリソース チャンクのサイズはチャンクの先頭に含まれており、以下のように、587 行目と 604 行目ではサイズの読み込みが行われますが、ここでも範囲のチェックは行われません。
573 pos += get_int32( &chunkdata[pos], &chunklen );
574
575 /* parse the resource chunks */
576 while ( chunklen > 0 ) {
577 gchar* chunk = &chunkdata[pos];
578
579 /* start of chunk data */
580 pos += MXIT_CHUNK_HEADER_SIZE;
581
582 switch ( chunk_type( chunk ) ) {
583 case CP_CHUNK_SPLASH : /* splash image */
584 {
585 struct splash_chunk* splash = g_new0( struct splash_chunk, 1 );
586
587 mxit_chunk_parse_splash( &chunkdata[pos], chunk_length( chunk ), splash );
588
589 cr->resources = g_list_append( cr->resources, splash );
590 break;
591 }
592 case CP_CHUNK_CLICK : /* splash click */
593 {
594 struct splash_click_chunk* click = g_new0( struct splash_click_chunk, 1 );
595
596 cr->resources = g_list_append( cr->resources, click );
597 break;
598 }
599 default:
600 purple_debug_info( MXIT_PLUGIN_ID, "Unsupported custom resource chunk received (%i)\n", chunk_type( chunk) );
601 }
602
603 /* skip over data to next resource chunk */
604 pos += chunk_length( chunk );
605 chunklen -= ( MXIT_CHUNK_HEADER_SIZE + chunk_length( chunk ) );
この長さは、582 行目と 587 行目でチャンク内のデータにアクセスするために使用されるため、範囲外の読み込みが行われることになります。このデータがサーバに再送信されることはないので、情報漏えいの脆弱性の原因となることはありませんが、範囲外のメモリへのアクセスは、アクセス対象のロケーションが割り当て済みのメモリ領域ではない場合、Denial of Service(DoS)の原因となります。
CVE-2016-2372
この脆弱性が原因で、サーバ経由で送信される、巧妙に細工された MXIT データにより、範囲外の読み込みが行われる恐れがあります。MXIT サーバ経由で転送されたファイルを受信する際、サーバからは CP_CHUNK_GET コマンドが送信されます。この処理は、mxit/protocol.c の 2,195 ~ 2,206 行で、mxit_parse_cmd_media 関数によって行われます。
2195 case CP_CHUNK_GET : /* get file response */
2196 {
2197 struct getfile_chunk chunk;
2198
2199 /* decode the chunked data */
2200 memset( &chunk, 0, sizeof( struct getfile_chunk ) );
2201 mxit_chunk_parse_get( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk );
2202
2203 /* process the getfile */
2204 mxit_xfer_rx_file( session, chunk.fileid, chunk.data, chunk.length );
2205 }
2206 break;
2,201 行目では、mxit_chunk_parse_get 関数が呼び出され、この関数がファイルのサイズ(およびファイルにある他の情報の一部)を、getfile_chunk 構造へと読み込みます。この関数は mxit/chunk.c で定義されており、長さは 509 行目で以下のように読み込まれます。
509 pos += get_int32( &chunkdata[pos], &(getfile->length) );
この関数が解析され、情報が返されると、2,204 行目で mxit_xfer_rx_file 関数が呼び出されます。
この関数は mxit/filexfer.c ファイルで定義されています。このファイルの 445 行目で、この関数は受信したバッファをファイルへと読み込みます。
445 if ( fwrite( data, datalen, 1, xfer->dest_fp ) > 0 ) {
指定されたチャンクの長さがこのバッファよりもx長い場合は、範囲外の読み込みが行われることになり、メモリ内の結果データが、新しく受信したファイルの後に書き込まれます。脆弱性がトリガーされた時点のプログラムのメモリ レイアウトによっては、ページがアクセス不可能であることが原因でクラッシュが発生するか、または機密情報がメモリからファイルへと漏えいします。ファイルは無害であると思われるため、被害者は他の人にこのファイルを送ったり、攻撃者に再送信したりしてしまうこともあります。これにより、データの漏えいが発生します。
ディレクトリ トラバーサルの脆弱性
Talos は、Pidgin チャット クライアントの一部である MXit プロトコルの実装に、ディレクトリ トラバーサルに関する 1 件の脆弱性があることを発見しました。この脆弱性は次のとおりです。
- CVE-2016-4323
CVE-2016-4323
Pidgin での MXIT プロトコルの処理において、ディレクトリ トラバーサルが発生します。サーバから送信される、巧妙に細工された MXIT データにより、ファイルの上書きが行われる恐れがあります。この脆弱性は、ネットワーク トラフィックへのアクセス権を持つ悪意のあるサーバや人物が、スプラッシュ イメージに対して無効なファイル名を提供することでトリガーされます。
Pidgin は、MXIT サーバへの接続が行われた際に、このサーバが表示するスプラッシュ イメージを提供することを許可します。また、このサーバは、サーバから送り返されるコマンドを使用して、新しいイメージを提供することで、イメージを更新できます。
サーバが multimedia コマンドを使用して新しいイメージを提供すると、以下のように、mxit/protocol.c の 2,170 行目で splash_update 関数が呼び出されます。
2170 splash_update( session, chunk.id, splash->data, splash->datalen, clickable );
564 行目の mxit_chunk_parse_cr 関数では、サーバから受信したデータから chunk.id 変数が読み込まれます。
564 pos += get_utf8_string( &chunkdata[pos], cr->id, sizeof( cr->id ) );
splash_update 関数は、以下のように、mxit/splashscreen.c の 115 ~ 136 行目で定義されています。
115 void splash_update(struct MXitSession* session, const char* splashId, const char* data, int datalen, gboolean clickable)
116 {
117 char* dir;
118 char* filename;
119
120 /* Remove the current splash-screen */
121 splash_remove(session);
122
123 /* Save the new splash image */
124 dir = g_strdup_printf("%s" G_DIR_SEPARATOR_S "mxit", purple_user_dir());
125 purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR);
126 /* ensure directory exists */
127 filename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s.png", dir, purple_escape_filename(splashId));
128 if (purple_util_write_data_to_file_absolute(filename, data, datalen)) {
129 /* Store new splash-screen ID to settings */
130 purple_account_set_string(session->acc, MXIT_CONFIG_SPLASHID, splashId);
131
132 purple_account_set_bool(session->acc, MXIT_CONFIG_SPLASHCLICK, clickable );
133 }
134 g_free(dir);
135 g_free(filename);
136 }
127 行目では、ディレクトリ トラバーサルが発生することを防ぐために、splashId が適切にエスケープされています。しかし、130 行目の MXIT_CONFIG_SPLASHID 変数には、エスケープされていない変数が保存されます。この関数の 121 行目で呼び出される splash_remove 関数は、以下のように、MXIT_CONFIG_SPLASHID を使用して削除するファイルを検出します(84 ~ 104 行)。
84 void splash_remove(struct MXitSession* session)
85 {
86 const char* splashId = NULL;
87 char* filename;
88
89 /* Get current splash ID */
90 splashId = splash_current(session);
91
92 if (splashId != NULL) {
93 purple_debug_info(MXIT_PLUGIN_ID, "Removing splashId: '%s'\n", splashId);
94
95 /* Delete stored splash image */
96 filename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "mxit" G_DIR_SEPARATOR_S "%s.png", purple_user_dir(), splashId);
97 g_unlink(filename);
98 g_free(filename);
99
100 /* Clear current splash ID from settings */
101 purple_account_set_string(session->acc, MXIT_CONFIG_SPLASHID, "");
102 purple_account_set_bool(session->acc, MXIT_CONFIG_SPLASHCLICK, FALSE);
103 }
104 }
しかし、splash_update の場合とは異なり、ここではファイル名のエスケープが行われていないため、攻撃者はシステム上の任意の png ファイルを削除することが可能です。
バッファ オーバーフローに関する脆弱性
Talos は、Pidgin チャット クライアントの一部である MXit プロトコルの実装に、バッファ オーバーフローに関する 5 件の脆弱性があることを発見しました。この脆弱性は以下のとおりです。
- CVE-2016-2368
- CVE-2016-2371
- CVE-2016-2376
- CVE-2016-2377
- CVE-2016-2378
CVE-2016-2368
この脆弱性が原因で、サーバ経由で送信される、巧妙に細工された MXIT データにより、バッファ オーバーフローが発生する恐れがあります。Pidgin 用の MXIT プラグインでは約 27 ヵ所で g_snprintf 関数が使用されており、そこではこの関数の戻り値が受け取られます。g_snprintf は、実際に書き込まれたバイト数ではなく、バッファが十分な大きさであったとした場合に書き込まれるはずのバイト数を返します。この説明については、以下を参照してください。
https://developer.gnome.org/glib/stable/glib-String-Utility-Functions.html#g-snprintf: [英語]
MXIT プラグインは、g_snprintf からの戻り値をインデックスまたはオフセットとして使用します。この戻り値は複数の場所で処理されますが、この戻り値が範囲内にあるかどうかの確認は行われません。これは、バッファ オーバーフローの原因となる恐れがあります。この問題の例は、467 ~ 479 行目の mxit_queue_packet() で確認できます。
467 hlen = g_snprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc ), CP_REC_TERM ); /* client mxitid */
468
469 if ( session->http ) {
470 /* http connection only */
471 hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "s=");
472 if ( session->http_sesid > 0 ) {
473 hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_sesid, CP_FLD_TERM ); /* http session id */
474 }
475 session->http_seqno++;
476 hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_seqno, CP_REC_TERM ); /* http request sequence id */
477 }
478
479 hlen += g_snprintf( header + hlen, sizeof( header ) - hlen, "cm=%i%c", cmd, CP_REC_TERM ); /* packet command */
467 行目で長いユーザ アカウントが返されると、471、473、476、または 479 行目でバッファ オーバーフローが発生する恐れがあります。g_snprintf のすべての戻り値を確認することが推奨されますが、アドバイザリでは、中でも 7 つの関数が行う 12 の呼び出しが問題であるとしています。これらの呼び出しでは信頼されないロケーションからのデータが文字列にコピーされる可能があるためです。悪意のあるサーバまたは悪意のある行動を取る可能性があるユーザは、データがサーバによって検証されない場合、誤って使用されることになるデータを g_snprintf によって送信できるため、バッファ オーバーフローが発生する恐れがあります。
CVE-2016-2371
この脆弱性が原因で、サーバ経由で送信される、巧妙に細工された MXIT データにより、バッファ オーバーフローが発生する恐れがあります。サーバから拡張プロファイル パケットが受信されると、mxit_parse_cmd_extprofile() 関数が呼び出されます。1,837 行目では、サーバから送信された属性の数が、count 変数へと読み込まれます。
1837 count = atoi( records[0]->fields[1]->data );
この値は、その後の 1,839 行目でループの範囲として使用され、1,843 行目で配列へのインデックスを計算するために使用されます。この値は、1,845 ~ 1,847 行で、アレイ内の値にアクセスするために使用されます。
1839 for ( i = 0; i < count; i++ ) {
1840 char* fname;
1841 char* fvalue;
1842 char* fstatus;
1843 int f = ( i * 3 ) + 2;
1844
1845 fname = records[0]->fields[f]->data; /* field name */
1846 fvalue = records[0]->fields[f + 1]->data; /* field value */
1847 fstatus = records[0]->fields[f + 2]->data;/* field status */
このインデックスは、1,859 ~ 1,860 行で、配列への書き込みにも使用されます。これにより、範囲外の書き込みが発生する恐れがあります。
1859 fvalue[10] = '\0';
1860 records[0]->fields[f + 1]->len = 10;
悪意のあるサーバや、ネットワーク トラフィックを傍受する攻撃者、または悪意のある行動を取る可能性があるユーザは、サーバがデータを検証しない場合、無効な数のレコードを送信することができるため、データへの範囲外の書き込みが発生する恐れがあります。
CVE-2016-2376
サーバから送信される、巧妙に細工された MXIT データにより、バッファ オーバーフローが発生する恐れがあります。mxit/protocol.c ファイル内の mxit_cb_rx 関数は、データが MXIT サーバから送信されると、Pidgin によって必ず呼び出されるコールバック関数です。データが受け取られると、着信パケットのサイズも 2,825 行目で受け取られます。2,826 行目では、このデータが、CP_MAX_PACKET として定義された最終的な MXIT パケットの最大サイズを超えていないかチェックされます。
2825 session->rx_res = atoi( &session->rx_lbuf[3] );
2826 if ( session->rx_res > CP_MAX_PACKET ) {
purple_connection_error( session->con, _( "A connection error occurred to MXit.(read stage 0x03)" ) );
2827 }
これは、データが読み込まれるバッファのサイズでもあります。しかし、サイズが CP_MAX_PACKET を超えていても、エラーは記録されますが、実行はそのまま継続されます。加えて、このサイズが負の場合(rx_res が整数のため、この状況はあり得ます)、エラーは記録されず、この場合も実行は継続されます。その後、このサイズは 2,846 行目の読み込み操作で使用されます。
2846 len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res );
ネットワーク トラフィックを傍受する、悪意のあるサーバや攻撃者は、パケットに対する無効なサイズを送信して、バッファ オーバーフローを発生させることが可能です。
CVE-2016-2377
サーバによって送信される、巧妙に細工された MXIT データにより、1 バイトの範囲外の書き込みが行われる恐れがあります。HTTP サーバから、HTTP 要求に対する応答を受信すると、mxit/http.c で定義されている mxit_cb_http_read() コールバック関数が呼び出されます。この関数は HTTP ヘッダーを解析し、本文を通常の MXIT パケットとして、処理のために送信します。この HTTP ヘッダーの解析では、以下のように、178 ~ 185 行で CONTENT_LENGTH がこのヘッダーから読み込まれます。
178 ch += strlen( HTTP_CONTENT_LEN );
179 tmp = strchr( ch, '\r' );
180 if ( !tmp ) {
181 purple_debug_error( MXIT_PLUGIN_ID, "Received bad HTTP reply
packet (ignoring packet)\n" );
182 goto done;
183 }
184 tmp = g_strndup( ch, tmp - ch );
185 bodylen = atoi( tmp );
bodylen は符号付き整数として定義されているため、HTTP ヘッダーから読み込まれた入力は負になる可能性があります。以下のように、189 ~ 192 行でサイズの確認が行われます。
189 if ( buflen + bodylen >= CP_MAX_PACKET ) {
190 /* this packet is way to big */
191 goto done;
192 }
しかし、bodylen が負の値に設定されている場合、このチェックは合格となります。
206 行目では、bodylen が、符号なし整数である session->rx_i 変数にコピーされるので、負の値が正の大きな値へとキャストされる恐れがあります。
206 session->rx_i = bodylen;
この値は、以下のように、mxit/procotol.c の 2,669 行目の mxit_parse_packet 関数でパケットが処理される際に、ループを制御するために使用されます。
2669 while ( i < session->rx_i ) {
このインデックス i は、後から 2,713、2,720、および 2,729 行目など複数の場所で、rx_dbuf バッファへの書き込みを行うために使用されます。そのため、攻撃者は rx_dbuf でバッファ オーバーフローを発生させることができます。
悪意のあるサーバは、HTTP 要求への応答で負の content-length を送信することで、脆弱性をトリガーすることができます。
CVE-2016-2378
Pidgin での MXIT プロトコルの処理に、バッファ オーバーフローに関する脆弱性が存在します。サーバ経由で送信される、巧妙に細工されたデータによりバッファ オーバーフローが発生した結果、メモリが破損する恐れがあります。libpurple/protocols/mxit/chunk.c の 231 行目で定義されている get_utf8_string 関数は、引数として最大長の文字列を使用します。通常、この引数は、書き込まれる str の文字列のサイズとして渡されます。
この引数は、238 行目で文字列の長さを読み込み、240 行目で、この長さが文字列の最大長を超えていないかチェックされます。文字列の最大長を超えている場合は、maxstrlen と同じ長さに設定されます。
238 pos += get_int16( &chunkdata[pos], &len );
239
240 if ( len > maxstrlen ) {
243 skip = len - maxstrlen;
244 len = maxstrlen;
245 }
しかし、len は、符号なし整数である nthos から読み込まれる符号付きの short であり、len は符号付きであるために、符号付き整数としてキャストされます。len の値が大きな正の値である場合、この値は負の値へとキャストされるので、240 行目で行われるサイズのチェックが回避されます。
その結果、248 行目の get_data の呼び出しがバッファ オーバーフローにつながります。
248 pos += get_data( &chunkdata[pos], str, len );
get_data 関数は最終的に memcpy を呼び出しますが、memcpy は符号なしのサイズ パラメータを想定しており、負の値を大きな正の値として解釈します。
悪意のあるサーバまたは悪意のある行動を取る可能性があるユーザは、データがサーバによって検証されない場合、負の長さの値を送信して、この脆弱性をトリガーできます。
まとめ
素早く、かつ効果的にコミュニケーションを取ることができることから、このインスタント メッセージング ソフトウェアは幅広く使われるようになっています。その結果攻撃者は、インスタント メッセージング アプリケーションが持つ脆弱性を常に探し、悪用を試みるようになりました。これは、脆弱性を利用することで多数の潜在的な被害者にアクセスできるためです。こうした継続的な攻撃に対抗して攻撃対象領域を減らすためには、ソフトウェアの修正が不可欠です。ユーザの多くは定期的にパッチを適用していないため、攻撃者はこの状況を利用してシステムへと簡単にアクセスすることができます。Pidgin のインスタント メッセージング クライアントは多数のシステムにインストールされており、Talos が特定した脆弱性に対応するには、更新済みの最新バージョン(2.11.0)をインストールすることが必要不可欠です。 最新の Pidgin ソフトウェアリリースはここから入手できます。
軽減策
お客様の保護のため、Talos はこうした脆弱性を悪用しようとする行為を検出するルールをリリースいたしました。今後、脆弱性に関する新たな情報が追加されるまでの間は、ルールが追加されたり、現行のルールが変更されたりする場合がありますのでご注意ください。最新のルールの詳細については、Firepower Management Center、FireSIGHT Management Center、または Snort.org を参照してください。
Snort ルール:38344、38345、38545 ~ 38551、38578、38867、38870、39150、39151
本稿は 2016年6月21日に Talos Group のブログに投稿された「Vulnerability Spotlight: Pidgin Vulnerabilities」の抄訳です。