
하트블리드 버그 `CVE-2014-0160`는 OpenSSL 라이브러리의 심각한 구현 결함으로, 공격자가 피해자 서버의 메모리에서 데이터를 훔칠 수 있게 합니다. 도난당한 데이터의 내용은 서버 메모리에 무엇이 있는지에 따라 달라집니다. 여기에는 개인 키, TLS 세션 키, 사용자 이름, 비밀번호, 신용카드 정보 등이 포함될 수 있습니다. 이 취약점은 SSL/TLS가 연결을 유지하는 데 사용하는 하트비트(Heartbeat) 프로토콜의 구현에 존재합니다.
Heartbleed 버그 CVE-2014-0160는 OpenSSL 라이브러리의 심각한 구현 결함으로, 공격자가 피해자 서버의 메모리에서 데이터를 훔칠 수 있게 합니다. 훔친 데이터의 내용은 서버 메모리에 무엇이 있는지에 따라 달라집니다. 여기에는 개인 키, TLS 세션 키, 사용자 이름, 비밀번호, 신용카드 정보 등이 포함될 수 있습니다. 이 취약점은 SSL/TLS가 연결을 유지하는 데 사용하는 Heartbeat 프로토콜의 구현에 있습니다.
영향을 받는 OpenSSL 버전 범위는 1.0.1부터 1.0.1f까지입니다. Ubuntu VM의 버전은 1.0.1입니다.
Heartbleed 공격은 Heartbeat 요청을 기반으로 합니다. 이 요청은 서버에 일부 데이터를 보내고, 서버는 데이터를 응답 패킷에 복사하므로 모든 데이터가 그대로 에코되어 돌아옵니다. 정상적인 경우 요청에 3바이트의 데이터 "ABC"가 포함되어 있다고 가정해 봅시다. 그러면 길이 필드의 값은 3입니다. 서버는 데이터를 메모리에 배치하고 데이터의 시작 부분에서 3바이트를 응답 패킷에 복사합니다. 공격 시나리오에서 요청에는 3바이트의 데이터가 포함될 수 있지만 길이 필드에는 1003이 명시될 수 있습니다. 서버가 응답 패킷을 구성할 때 데이터 시작 부분(즉, "ABC")에서 복사하지만 3바이트 대신 1003바이트를 복사합니다. 이 추가 1000바이트는 분명히 요청 패킷에서 오는 것이 아닙니다. 그것들은 서버의 개인 메모리에서 오며, 다른 사용자의 정보, 비밀 키, 비밀번호 등을 포함할 수 있습니다.
다음으로, 요청의 길이 필드를 변경합니다. 먼저, 위 그림에서 Heartbeat 응답 패킷이 어떻게 구성되는지 이해해 봅시다. Heartbeat 요청 패킷이 도착하면 서버는 패킷을 파싱하여 페이로드와 Payload_length 값(위에서 강조 표시됨)을 얻습니다. 여기서 페이로드는 3바이트 문자열 "ABC"뿐이고 Payload_length 값은 정확히 3입니다. 서버 프로그램은 이 길이 값을 요청 패킷에서 맹목적으로 가져옵니다. 그런 다음 "ABC"를 저장하는 메모리를 가리키고 Payload_length 바이트를 응답 페이로드에 복사하여 응답 패킷을 구성합니다. 이렇게 하면 응답 패킷에는 3바이트 문자열 "ABC"가 포함됩니다.
다음으로, 아래 그림과 같이 Heartbleed 공격을 실행합니다. 동일한 페이로드(3바이트)를 유지하되 Payload_length 필드를 1003으로 설정합니다. 서버는 응답 패킷을 구성할 때 다시 맹목적으로 이 Payload_length 값을 사용합니다. 이번에는 서버 프로그램이 문자열 "ABC"를 가리키고 메모리에서 1003바이트를 응답 패킷의 페이로드로 복사합니다. 문자열 "ABC" 외에도 추가 1000바이트가 응답 패킷에 복사되며, 이는 비밀 활동, 로깅 정보, 비밀번호 등 메모리의 어떤 것이든 될 수 있습니다.
공격 코드는 Payload_length 값을 변경할 수 있게 합니다. 기본적으로 이 값은 상당히 큰 값(0x4000)으로 설정되지만, 줄일 수 있습니다.
Heartbleed 취약점을 수정하는 가장 쉬운 방법은 OpenSSL 라이브러리를 최신 버전으로 업데이트하는 것입니다. 그러나 여기서 목표는 소스 코드를 통해 취약점을 패치하는 것입니다.
Heartbeat 요청/응답 패킷의 형식
struct {
HeartbeatMessageType type; // 1 byte: request or the response
uint16 payload_length; // 2 byte: the length of the payload
opaque payload[HeartbeatMessage.payload_length];
opaque padding[padding_length];
} HeartbeatMessage;
패킷의 첫 번째 필드(1바이트)는 유형 정보이고, 두 번째 필드(2바이트)는 페이로드 길이이며, 그다음에 실제 페이로드와 패딩이 옵니다. 페이로드의 크기는 페이로드 길이 필드의 값과 같아야 하지만, 공격 시나리오에서는 페이로드 길이를 다른 값으로 설정할 수 있습니다. 다음 코드 스니펫은 서버가 요청 패킷의 데이터를 응답 패킷으로 복사하는 방법을 보여줍니다.
Heartbeat 요청 패킷 처리 및 응답 패킷 생성
/* Allocate memory for the response, size is 1 byte
* message type, plus 2 bytes payload length, plus
* payload, plus padding
*/
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
// Read from type field first
hbtype = *p++; /* After this instruction, the pointer
* p will point to the payload_length field */
// Read from the payload_length field from the request packet
n2s(p, payload); /* Function n2s(p, payload) reads 16 bits
* from pointer p and store the value
* in the INT variable "payload". */
pl = p; // pl points to the beginning of the payload content
if (hbtype == TLS1_HB_REQUEST)
{
unsigned char *buffer, *bp;
int r;
/* Allocate memory for the response, size is 1 byte
* 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);
// copy payload
memcpy(bp, pl, payload); /* pl is the pointer which
* points to the beginning
* of the payload content */
bp += payload;
// Random padding
RAND_pseudo_bytes(bp, padding);
// this function will copy the 3+payload+padding bytes
// from the buffer and put them into the heartbeat response
// packet to send back to the request client side.
OPENSSL_free(buffer);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
}
취약점은 바로 여기에 있습니다
// copy payload
memcpy(bp, pl, payload);
pl이 유효한지 확인하는 검사가 없습니다. 따라서 메모리 위반이 발생할 수 있습니다.
패치:
memcpy() 실행 전 경계 검사VM에는 패치가 적용되었지만 이 저장소에는 표시되지 않습니다.
관심을 가져주셔서 감사합니다. 이 프로젝트는 재미있고 통찰력 있는 경험이었습니다!