読者です 読者をやめる 読者になる 読者になる

labunix's blog

labunixのラボUnix

「CVE-2014-0160.」の脆弱性の対処について(Debian Wheezy)

■Debianの修正情報が014/03/07に出ている。
 「なるほど、よく分からん。」という印象でしたので、調べてみた。

 Debian Security Advisory
 https://www.debian.org/security/2014/dsa-2896

■Wheezyである私の環境も対象の「1.0.1e-2+deb7u4」を使用している。
 「1.0.1e-2+deb7u5」が修正されたバージョン。

$ sudo strings  /usr/bin/openssl | grep libssl
libssl.so.1.0.0
$ sudo ldd  /usr/bin/openssl | grep "libssl.*1.0.0"
	libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007f5d544dc000)

$ openssl version
OpenSSL 1.0.1e 11 Feb 2013
$ dpkg-query -p openssl | grep "Version\|depends"
Version: 1.0.1e-2+deb7u4

$ sudo apt-get update
$ sudo apt-cache show openssl | grep -i "Version\|depends"
Version: 1.0.1e-2+deb7u5
Depends: libc6 (>= 2.7), libssl1.0.0 (>= 1.0.1), zlib1g (>= 1:1.1.4)
Version: 1.0.1e-2+deb7u4
Depends: libc6 (>= 2.7), libssl1.0.0 (>= 1.0.1), zlib1g (>= 1:1.1.4)

■対象外のコンパイルオプションとは「-DOPENSSL_NO_HEARTBEATS」。
 「HeartBeart」を無効にするようなコンパイルオプションは無いので、
 私の環境は対象外には該当しない。

$ sudo openssl version -a | grep compiler | sed s/" \-"/"\n&"/g | column
compiler: gcc			 -DL_ENDIAN			 -Wl,-z,relro			 -DSHA256_ASM
 -fPIC				 -DTERMIO			 -Wa,--noexecstack		 -DSHA512_ASM
 -DOPENSSL_PIC			 -g				 -Wall				 -DMD5_ASM
 -DZLIB				 -O2				 -DMD32_REG_T=int		 -DAES_ASM
 -DOPENSSL_THREADS		 -fstack-protector		 -DOPENSSL_IA32_SSE2		 -DVPAES_ASM
 -D_REENTRANT			 --param=ssp-buffer-size=4	 -DOPENSSL_BN_ASM_MONT		 -DBSAES_ASM
 -DDSO_DLFCN			 -Wformat			 -DOPENSSL_BN_ASM_MONT5		 -DWHIRLPOOL_ASM
 -DHAVE_DLFCN_H			 -Werror=format-security	 -DOPENSSL_BN_ASM_GF2m		 -DGHASH_ASM
 -m64				 -D_FORTIFY_SOURCE=2		 -DSHA1_ASM

■OpenSSLの「heartbeat」 という機能の実装から「Heartbleed」と呼ばれている。
 RFCにある「heartbeat_request」と「heartbeat_response」がそれっぽい。

 RFC6520
 http://www.rfc-editor.org/rfc/rfc6520.txt

 OpenSSL の脆弱性に関する注意喚起
 https://www.jpcert.or.jp/at/2014/at140013.html

$ w3m -dump http://www.rfc-editor.org/rfc/rfc6520.txt | grep "heartbeat_re[a-z]*("
      heartbeat_request(1),
      heartbeat_response(2),

■「ssl/d1_both.c」「t1_lib.c」のソースが修正されている。

 existential type crisis : Diagnosis of the OpenSSL Heartbleed Bug
 http://blog.existentialize.com/diagnosis-of-the-openssl-heartbleed-bug.html

$ sudo apt-get source --download-only openssl 
$ tar ztvf openssl_1.0.1e.orig.tar.gz  | grep d1_both.c
-rw-rw-r-- openssl/openssl  44243 2013-02-12 00:26 openssl-1.0.1e/ssl/d1_both.c
$ tar zxvf openssl_1.0.1e.orig.tar.gz openssl-1.0.1e/ssl/d1_both.c
openssl-1.0.1e/ssl/d1_both.c
$ grep -A 8 "HeartBeat" openssl-1.0.1e/ssl/d1_both.c 
	/* Create HeartBeat message, we just use a sequence number
	 * as payload to distuingish different messages and add
	 * some random stuff.
	 *  - Message Type, 1 byte
	 *  - Payload Length, 2 bytes (unsigned int)
	 *  - Payload, the sequence number (2 bytes uint)
	 *  - Payload, random bytes (16 bytes uint)
	 *  - Padding
	 */

■「HeartBeat message」のメモリアドレスの位置が予測可能な実装をしていて、
 影響範囲がTLSとDTLSということかな。。。

$ w3m -dump "http://www.rfc-editor.org/rfc/rfc6520.txt" | grep payload_length
      uint16 payload_length;
      opaque payload[HeartbeatMessage.payload_length];
   payload_length:  The length of the payload.
      payload_length is 2.  Therefore, the padding_length is
      TLSPlaintext.length - payload_length - 3 for TLS and
      DTLSPlaintext.length - payload_length - 3 for DTLS.  The
   If the payload_length of a received HeartbeatMessage is too large,

■以下を参考に、DTLSは再送タイマーを使ってパケットロスに対処するTLSの拡張と解釈出来る。

 RFC 4347 (DTLS) について調べてみた
 http://mdgw.hateblo.jp/entry/20080217/1203273902

■「payload」の配列境界のチェックを行うようにしたのが今回のパッチということかな。。。

$ tar ztvf openssl_1.0.1e-2+deb7u5.debian.tar.gz | grep CVE | awk '{print $NF}'
debian/patches/CVE-2013-6449.patch
debian/patches/CVE-2013-4353.patch
debian/patches/CVE-2014-0160.patch
debian/patches/CVE-2013-6450.patch

$ tar zxvf openssl_1.0.1e-2+deb7u5.debian.tar.gz debian/patches/CVE-2014-0160.patch
debian/patches/CVE-2014-0160.patch

$ grep -i "^+++\|RFC\|HEARTBEAT" debian/patches/CVE-2014-0160.patch
Subject: [PATCH] Add heartbeat extension bounds check.
A missing bounds check in the handling of the TLS heartbeat extension
+++ b/ssl/d1_both.c
@@ -1459,26 +1459,36 @@ dtls1_process_heartbeat(SSL *s)
 		s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
+		return 0; /* silently discard per RFC 6520 sec. 4 */
+		unsigned int write_length = 1 /* heartbeat type */ +
+					    2 /* heartbeat length */ +
@@ -1489,11 +1499,11 @@ dtls1_process_heartbeat(SSL *s)
-		r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
+		r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);
 			s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
+++ b/ssl/t1_lib.c
@@ -2588,16 +2588,20 @@ tls1_process_heartbeat(SSL *s)
 		s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
+		return 0; /* silently discard per RFC 6520 sec. 4 */

$ grep -A 12 "Random" debian/patches/CVE-2014-0160.patch 
 		/* Random padding */
 		RAND_pseudo_bytes(bp, padding);
 
-		r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
+		r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);
 
 		if (r >= 0 && s->msg_callback)
 			s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
-				buffer, 3 + payload + padding,
+				buffer, write_length,
 				s, s->msg_callback_arg);
 
 		OPENSSL_free(buffer);

■テストサイトがあって、該当する脆弱性を利用して「YELLOW SUBMARINE」を
 書き込めるかどうかを確認出来る。

 あるプロセスに限定しているとはいえ、
 予測可能であることを確認したのであれば、
 例え書き込まなくてもメモリ上のSSL証明書の秘密鍵が
 読まれると困ること位は容易に想像出来る。
 また、システムのログにも見当たらない。(操作ログは出る)

 http://filippo.io/Heartbleed/

([]uint8) {
 00000000  02 00 79 68 65 61 72 74  62 6c 65 65 64 2e 66 69  |..yheartbleed.fi|
 00000010  6c 69 70 70 6f 2e 69 6f  59 45 4c 4c 4f 57 20 53  |lippo.ioYELLOW S|
 00000020  55 42 4d 41 52 49 4e 45  39 c2 b9 0e 5e c3 b6 62  |UBMARINE9...^..b|
 00000030  bc 6d fa a6 59 6b 56 9a  f2 5c 4b b9 a1 7b 55 77  |.m..YkV..\K..{Uw|
 00000040  8b e4 bf 24 11 17 d0 58  ac 08 22 ee db 5a 56 68  |...$...X.."..ZVh|
 00000050  bd a9 07 d6 67 88 7f d9  4a c1 06 d3 87 9b c5 23  |....g...J......#|
 00000060  90 d3 e7 b0 38 ce ac b4  05 a1 09 d5 20 52 4b 54  |....8....... RKT|
 00000070  59 6a 17 4d 24 34 64 30  6a db 4f d9 08 df 39 01  |Yj.M$4d0j.O...9.|
 00000080  50 a5 18 8e d9 4c 7e 4f  79 6a 86 3b              |P....L~Oyj.;|
}
$ sudo find /var/log -type f -exec grep -i "heartbeart\|libssl" {} \; | awk -F\; '{print $(NF-1)}'
 COMMAND=/usr/bin/find /var/log -type f -exec grep -i "heartbeart\|libssl" {} 

■上記の理由から、SSLサーバ証明書の再発行と再設定を行った方が良いので、
 「要検証の上~」の但し書きがあるのだと思う。
 その他、影響範囲の説明は下記で。

 CVE-2014-0160 OpenSSL Heartbleed 脆弱性まとめ
 http://d.hatena.ne.jp/nekoruri/20140408

 OpenSSL 1.0.1に含まれる脆弱性への対応のお願い
 https://jp.globalsign.com/information/important/detail.php?no=1396947073

 CVE-2014-0160 の OpenSSL の脆弱性対応
 http://blog.n-z.jp/blog/2014-04-08-cve-2014-0160.html

■目安として、以下のパッチで再起動チェックを行うサービスに関係のあるプロセスは
 チェックしておく。

$ tar zxvf openssl_1.0.1e-2+deb7u5.debian.tar.gz debian/libssl1.0.0.postinst
debian/libssl1.0.0.postinst

$ grep check debian/libssl1.0.0.postinst | grep -v "dpkg\|for "
	    check="sendmail openssh-server"
	    check="$check apache2-common ssh-nonfree exim4"
	    check="$check apache-ssl libapache-mod-ssl openvpn spamassassin"
	    check="$check courier-imap-ssl courier-mta-ssl courier-pop-ssl"
	    check="$check postfix cyrus21-imapd cyrus21-pop3d"
	    check="$check postgresql-7.4 postgresql-8.0 postgresql-8.1"
	    check="$check postgresql-8.2"
	    check="$check racoon dovecot-common bind9"
	    check="$check ntp openntpd clamcour nagios-nrpe-server"
	    check="$check clamav-freshclam clamav-daemon"
	    check="$check fetchmail ftpd-ssl slapd"
	    check="$check proftpd proftpd-ldap proftpd-mysql proftpd-pgsql"
	    check="$check partimage-server conserver-server tor"
	    check="$check stunnel4"
	    check=$(echo $check | sed 's/apache2-common/apache2/g')
	    check=$(echo $check | sed 's/libapache-mod-ssl/apache/g')
	    check=$(echo $check | sed 's/proftpd-.*/proftpd/g')
	    check=$(echo $check | sed 's/dovecot-common/dovecot/g')
	    check=$(echo $check | sed 's/openssh-server/ssh/g')

■openssh-serverが影響無しとされているのは、鍵の生成にlibsslを使っていないからだと思う。

$ sudo ldd /usr/bin/ssh-keygen 
	linux-vdso.so.1 =>  (0x00007ffff05ff000)
	libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007fc6cd58c000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc6cd388000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007fc6cd171000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc6ccde6000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fc6ccbcf000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fc6cdbd3000)

■念の為、checkrestartが使えるようにしてアップデート。
 以下に見る通り、ssh/postfix/python2.7の再起動が必要とある。

$ sudo apt-get install -y debian-goodies
$ sudo checkrestart
Found 0 processes using old versions of upgraded files

$ sudo apt-get upgrade -y
$ sudo checkrestart
Found 7 processes using old versions of upgraded files
(7 distinct programs)
(4 distinct packages)

Of these, 2 seem to contain init scripts which can be used to restart them:
The following packages seem to have init scripts that could be used
to restart them:
openssh-server:
	3629	/usr/sbin/sshd
postfix:
	4390	/usr/lib/postfix/qmgr
	6645	/usr/lib/postfix/pickup
	4369	/usr/lib/postfix/master

These are the init scripts:
service ssh restart
service postfix restart

These processes do not seem to have an associated init script to restart them:
python2.7-minimal:
	3606	/usr/bin/python2.7
openssh-client:
	4564	/usr/bin/ssh-agent

■どうしてもシステムを再起動出来ないならば。。。

$ sudo /etc/init.d/ssh restart
$ sudo /etc/init.d/postfix restart
$ sudo checkrestart
Found 2 processes using old versions of upgraded files
(2 distinct programs)
(1 distinct packages)
These processes do not seem to have an associated init script to restart them:
python2.7-minimal:
	3606	/usr/bin/python2.7

$ sudo ps aux | grep python | sed s/".*[0-9]\:[0-9]* "//g
/usr/bin/python /usr/bin/fail2ban-server -b -s /var/run/fail2ban/fail2ban.sock
grep python

$ sudo /etc/init.d/fail2ban restart
$ sudo checkrestart
Found 1 processes using old versions of upgraded files
(1 distinct program)
(0 distinct packages)
No packages seem to need to be restarted.
(please read checkrestart(1))

■出来れば、システムの再起動をお勧めします。

$ sudo shutdown -r now && exit