Just Another Geek

25 Mar 2010

CVE-2010-0740: Record of death vulnerability in OpenSSL

A new vulnerability (CVE-2010-0740) was found in OpenSSL, affectionately called “Record of death” (in reference to the ping of death vulnerability back in 1996) was fixed by the patch below:

--- ssl/s3_pkt.c 24 Jan 2010 13:52:38 -0000
+++ ssl/s3_pkt.c 24 Mar 2010 00:00:00 -0000
@@ -291,9 +291,9 @@
  if (version != s->version)
-  /* Send back error using their
-   * version number :-) */
-  s->version=version;
+                if ((s->version & 0xFF00) == (version & 0xFF00))
+                 /* Send back error using their minor version number :-) */
+   s->version = (unsigned short)version;
   goto f_err;

Arno and myself had a look on this vuln, but at a glance, it’s hard to understand the consequences of theses two modifications:

  • Comparison of the server version and the packet version
  • Use of cast for the assignment.

The latter is the interesting part. s->version is declared as a int (32 bits signed value on x86) and version is a short (16 bits signed value on x86).

When doing the following assignment:

int i_version;
short s_version;

i_version = s_version;

What are the problems? On x86, i_version is big enough to store s_version so there is no truncation or overflow issues. However, theses two variables are signed and the C has the following rule:

Conversion of an operand value to a compatible type causes no change to the value or the representation.

In other words:

short s;
int i;

s = -1;
i = s; /* i must be equal to -1 */

To do this, the compiler has to perform a sign extension, which means that if the short value was negative, its integer value must stays negative.

Internally, the most significant bit (msb) of the short variable will be propagated in the integer variable for the “upper bits”. Examples:

  |  short | msb | integer      |                                                                                                                                                                                                                                                                                               
  | 0x0000 |   0 | 0x00000000   |                                                                                                                                                                                                                                                                                               
  | 0x7000 |   0 | 0x00070000   |                                                                                                                                                                                                                                                                                               
  | 0x8000 |   1 | 0xffff0000   |                                                                                                                                                                                                                                                                                               
  | 0xffff |   1 | 0xffffffff   |  

So if version >= 0x8000, s->version will have a value >= 0xffff0000 (a big negative value).

According to the advisory, this bug can cause a crash of an OpenSSL end-point due to a read attempt at NULL.
The OpenSSL code uses extensively indirect function pointers for callbacks so it is hard to follow the code path without spending some time, so I cannot confirm neither my hypothesis nor the impact of the bug.