本周四,OpenSSL基金会发布警告称,一个已存在10 16 年的漏洞可能导致黑客利用通过OpenSSL加密的流量发动“中间人”攻击。

伯乐在线补充:新浪科技编辑原写的是 10 年,这是笔误,这个Bug已存在 16 年。

@刘江总编:这次bug是由日本程序员Masashi Kikuchi发现的,他在公司技术博客中翔实说明了技术细节:http://t.cn/RvxtpaQ 令人担忧的是,这个bug已经存在16年之久。从细节中还能看出OpenSSL的开发和审核者对协议本身似乎并不十分熟悉。

The biggest reason why the bug hasn’t been found for over 16 years is that code reviews were insufficient, especially from experts who had experiences with TLS/SSL implementation. If the reviewers had enough experiences, they should have been verified OpenSSL code in the same way they do their own code. They could have detected the problem.

信息安全专家目前仍试图解决OpenSSL加密协议中的“心脏流血”漏洞。根据AVG Virus Labs的数据,目前仍有1.2万个热门域名存在这一漏洞。而根据OpenSSL基金会此次发布的消息,黑客可能利用新漏洞去拦截加密流量,对其进行解密,随后阅读流量中的内容。

OpenSSL的用户被建议安装新的补丁,并升级至OpenSSL软件的最新版本。这一漏洞的发现者是软件公司Lepidum的日本研究员Masashi Kikuchi。Lepidum的网站上显示:“当服务器和客户端都存在漏洞时,攻击者可以窃听及伪造你的通信。”

与能够导致服务器直接受到攻击的“心脏流血”漏洞不同,这一新漏洞要求黑客位于两台通信的计算机之间。例如,使用机场公开WiFi的用户可能成为被攻击目标。

这一漏洞自1998年OpenSSL首次发布以来就一直存在。而“心脏流血”漏洞是在2011年新年OpenSSL进行升级时引入的。

这一漏洞在长达十几年的时间内一直没有被发现,这再次表明了OpenSSL管理的缺陷。OpenSSL是开源的,这意味着任何人都可以对其进行评估及更新。由于这一原因,OpenSSL被认为比某家公司开发的私有代码更安全、更可信。

但实际上,OpenSSL在欧洲只有1名全职开发者,以及3名“核心”的志愿程序员,其运营依靠每年2000美元的捐赠。不过,OpenSSL仍被用于加密全球大部分网站的服务器,并被亚马逊和思科等大公司广泛使用。

在“心脏流血”漏洞被发现之后,包括亚马逊、思科、戴尔、Facebook、富士通、谷歌、IBM、英特尔、微软、NetApp、Rackspace、高通和VMware在内的公司都承诺在未来3年内每年投入10万美元,用于Core Infrastructure Initiative项目。这一新的开源项目由Linux基金会牵头,用于支撑OpenSSL等关键的开源基础设施。

 

 

原文

==================================================================

How I discovered CCS Injection Vulnerability (CVE-2014-0224)

05 Jun 2014

Hello. My name is Masashi Kikuchi. Here is my story how I find the CCS Injection Vulnerability. (CVE-2014-0224)

What is the bug?

The problem is that OpenSSL accepts ChangeCipherSpec (CCS) inappropriately during a handshake. This bug has existed since the very first release of OpenSSL.

tls-message-flow1

In a correct handshake, the client and the server exchange messages in the order as depicted in this figure. (See RFC5246 The Transport Layer Security (TLS) Protocol Version 1.2 §7.3)。

ChangeCipherSpec MUST be sent at these positions in the handshake. OpenSSL sends CCS in exact timing itself. However, it accepts CCS at other timings when receiving. Attackers can exploit this behavior so that they can decrypt and/or modify data in the communication channel.

How difficult is it to find the bug?

The biggest reason why the bug hasn’t been found for over 16 years is that code reviews were insufficient, especially from experts who had experiences with TLS/SSL implementation. If the reviewers had enough experiences, they should have been verified OpenSSL code in the same way they do their own code. They could have detected the problem.

Fuzzing may have worked. However, as the history (see below) shows, knowledge of TLS/SSL implementation seems vital.

CVE-2004-0079

The first chance to find CCS Injection was in 2004, when Codenomicon found CVE-2004-0079. The following verification was added as a fix against this.

Fix null-pointer assignment in do_change_cipher_spec() revealed

This ensures that CCS is accepted only when new_cipher is set. Nevertheless, since new_cipher is already set in process of ServerHello, it cannot prevent CCS Injection.

CVE-2009-1386

The next chance was CVE-2009-1386.

DTLS: SegFault if ChangeCipherSpec is received before ClientHello

This was a problem that receiving CCS too early in DTLS handshake causes a segmentation fault. Again CCS timing verification hasn’t been fixed.

DTLS fragment retransmission bug

OpenSSL ticket #1950 also fixes the behavior over CCS.

DTLS fragment retransmission bug

This fix adds a check against unfortunate packet reorder on the wire. This solves the timing problem in DTLS handshake. As analyzed in the ticket, an uncomputed master secret is used for encryption. The value is said to be read from uninitialized memory and the random value causes a failure. But the truth is: the value was an empty byte sequence, i.e. not a random value. If this observation has been noted, possibility of the attack could have been alerted.

How difficult is it to implement CCS correctly?

It is easy to correctly implement CCS. Just send and receive messages in the order drawn in the protocol flow above. There is a little pitfall here, however, CCS uses a different type of record than other handshake messages. RFC explains the reason as follows:

  Note:          To help avoid pipeline stalls, ChangeCipherSpec is
                 an independent SSL Protocol content type, and is not
                 actually an SSL handshake message.
draft-ietf-tls-ssl-version3-00 §5.5

IMHO, this sentence is the very cause of the vulnerability. According to it, the reason that CCS is assigned an independent record type is to avoid a stall. This is the point where the most complex synchronization is required in TLS/SSL handshake. First, you need to wait until the handshake proceeds to the proper phase. Then, you need to check whether the handshake receives CCS before Finish.

More precisely, when accepting CCS, you must verify the following three conditions (*):

  • the handshake proceeds to the proper phase, i.e., just before to receive Finished,
  • no fragments from the handshake remains,
  • and, the next message is Finished.

Being more careful, you should also check

  • no Alert fragment remains (they can be rejected in the first place),
  • and, no Heartbeat fragment remains

as described in Alert attack.

I think the RFC can be clarified as:

Note: To help avoid pipeline stalls, ChangeCipherSpec is an independent SSL Protocol content type, and is not actually an SSL handshake message.

  • to avoid ChangeCipherSpec being transmitted in mix with other handshake fragments in one record,
  • and to avoid pipeline stalls, ChangeCipherSpec should be sent from both the server and the client.

How did I discover the bug?

When Heartbleed arose, everyone talked about how to prevent similar bugs. Unit tests, code analyzers, fork and rewrite cleanly, improve API, not to reinvent malloc, use other language than C, etc. etc.

One of the favored candidate for such a language was ATS. I thought how it would be to write TLS/SSL in Coq, which I am more familiar with. Proving the protocol safety is a huge job, while it would not contribute to implementation safety. I try to create something that can show the correctness of the implementation at a glance.

My goals were:

  • parsers and printers are correctly coupled,
  • printers are implemented trivially correct,
  • and, state machine behaviors are represented in predicates.

I set the above conditions (*) by start thinking from transitions on CCS, as I know it is the most complicated part in the state machine. (Not overly complicated as though it’s the worst.)

Next I check whether existing implementations correctly verify these conditions. Most implementations except OpenSSL verify them, more or less. OpenSSL seems not doing at all. Later I confirmed that OpenSSL is actually exploitable.

 

tls-message-flow