This is an old revision of the document!
We are going to setup a minimal vulnerable environment to experiment with the exploit. We need a vulnerable OpenSSL version and a webserver. We also need to configure a basic website that will just serve a static page.
tar xvf ~/Downloads/openssl-1.0.1f.tar.gz tar xvf ~/Downloads/nginx-1.6.0.tar.gz
If Perl 5.18.X is installed on your machine, you'll have to apply a patch to the OpenSSL sources in order to compile. Use the first command to find Perl's version, and skip to the next group of commands if it's older than 5.18.X.
perl -v This is perl 5, version 18, subversion 2 (v5.18.2) built for x86_64-linux-gnu-thread-multi (with 41 registered patches, see perl -V for more detail) ...
Download this patch: openssl-perl-5.18.x.patch.tar.gz
cd openssl-1.0.1f.tar.gz tar xvf ~/Downloads/openssl-perl-5.18.x.patch.tar.gz patch -p1 < openssl-perl-5.18.x.patch
Use default options for any exception that patch
will encounter.
Continue from here if your Perl version is older than 5.18.X.
cd nginx-1.6.0.tar.gz mkdir ~/vuln ./configure --prefix=$HOME/vuln --with-openssl=../openssl-1.0.1f --with-http_ssl_module --without-http_rewrite_module make make install
You should be able to run the Nginx binary after this step:
~nginx-1.6.0.tar.gz$ ~/vuln/sbin/nginx -V nginx version: nginx/1.6.0 built by gcc 4.8.2 (Ubuntu 4.8.2-19ubuntu1) TLS SNI support enabled configure arguments: --prefix=/home/vladum/vuln --with-openssl=../openssl-1.0.1f --with-http_ssl_module --without-http_rewrite_module
Prepare a self-signed certificate:
sudo mkdir -p /etc/nginx/ssl sudo openssl genrsa -des3 -out /etc/nginx/ssl/server.key 1024
Enter any passphrase.
sudo openssl req -new -key /etc/nginx/ssl/server.key -out /etc/nginx/ssl/server.csr sudo cp /etc/nginx/ssl/server.key /etc/nginx/ssl/server.key.org sudo openssl rsa -in /etc/nginx/ssl/server.key.org -out /etc/nginx/ssl/server.key sudo openssl x509 -req -days 365 -in /etc/nginx/ssl/server.csr -signkey /etc/nginx/ssl/server.key -out /etc/nginx/ssl/server.crt
Replace ~/vuln/conf/nginx.conf
with the following configuration:
worker_processes 1; events { worker_connections 1024; } http { server { listen 127.0.0.1:11443; server_name localhost; root /usr/share/nginx/www; index index.html; ssl on; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; } }
Nginx configuration and a static HTML page:
sudo mkdir -p /usr/share/nginx/www sudo chown vladum: /usr/share/nginx/www echo “Hello” > /usr/share/nginx/www/index.html
You should see the page live at https://127.0.0.1:11443. Ignore the certificate warning.
General information about this vulnerability can be obtained from this website.
The TLS Heartbeat protocol extension (see RFC 6520 specifies a keep-alive functionality between a TLS client and server that uses 2 messages: a request and the response. The RFC mandates the following:
A HeartbeatRequest message can arrive almost at any time during the lifetime of a connection. Whenever a HeartbeatRequest message is received, it SHOULD be answered with a corresponding HeartbeatResponse message. [...] When a HeartbeatRequest message is received and sending a HeartbeatResponse is not prohibited as described elsewhere in this document, the receiver MUST send a corresponding HeartbeatResponse message carrying an exact copy of the payload of the received HeartbeatRequest.
Both Heartbeat messages have the following format:
struct { HeartbeatMessageType type; uint16 payload_length; opaque payload[HeartbeatMessage.payload_length]; opaque padding[padding_length]; } HeartbeatMessage;
The vulnerable OpenSSL allocates memory for the response (using OPENSSL_malloc
) using the received payload length and copies the same amount of data from the received payload buffer. No bound checks are performed. The relevant source code looks like this:
/* Allocate memory for the response, size is 1 bytes * message type, plus 2 bytes payload length, plus * payload, plus padding */ buffer = OPENSSL_malloc(1 + 2 + payload + padding); bp = buffer; /* Enter response type, length and copy payload */ *bp++ = TLS1_HB_RESPONSE; s2n(payload, bp); memcpy(bp, pl, payload);
If the attacker sends a payload
, but a bogus, big, payload_length
, the vulnerable routine will copy past the end of the buffer and leak memory contents. Since the payload_length
field is represented on 2 bytes, 64KB can be leaked.
The Heartbeat RFC
16 03 02 00 31 # TLS Header 01 00 00 2d # Handshake header 03 02 # ClientHello field: version number (TLS 1.1) 50 0b af bb b7 5a b8 3e f0 ab 9a e3 f3 9c 63 15 \ 33 41 37 ac fd 6c 18 1a 24 60 dc 49 67 c2 fd 96 # ClientHello field: random 00 # ClientHello field: session id 00 04 # ClientHello field: cipher suite length 00 33 c0 11 # ClientHello field: cipher suite(s) 01 # ClientHello field: compression support, length 00 # ClientHello field: compression support, no compression (0) 00 00 # ClientHello field: extension length (0)