————————————
최근 핫 이슈 !!!!
“OpenSSL – HeartBleed Bug”
테스트는 많이 하셨을테니, 취약점 관점에서 좀 살펴볼까요????
————————————
Heartbleed Bug는 OpenSSL 암호화 관련 라이브러리의 취약점입니다. SSL/TLS 암호화를 사용하여 안전한 인터넷 세상을 만들고자 했던 OpenSSL이 지금은 인터넷의 위협적인 존재가 되고 있다. 이 위협의 대상은 SSL/TLS 를 기반으로 통신하는 웹, 이메일, 메신저, VPN 등이 될 수 있으며, 시스템의 중요 정보가 외부로 유출될 수 있다.
취약점 정보
- CVE Number : CVE-2014-0160
- Publish Date : 2014. 04. 07
- Affected Version
OpenSSL 1.0.2-beta1 vulnerable
OpenSSL 1.0.1 through 1.0.1f (inclusive) are vulnerable
- Affected Software
Cygwin, TeraTerm(HTTP Proxy), Phusion Passenger, ruby RVM, ruby rbenv / ruby –build, OpenVPN
- Affected Service
HTTPS, SMTP, POP3/IMAP, FTPS, LDAPS, Mysql/PostgreSQL 연결 암호화
이번 취약점은 SSL/TLS 프로토콜 자체 결함이 아닌 OpenSSL 라이브러리 상에서 TLS/DTSL(transport layer security protocols) HeartBeat Extension을 구현하는 과정에서 발생한다. HeartBeat Extension을 처리할때 데이터에 대한 올바른 경계체크를 하지 않아 64kb 정도의 시스템 메모리에 존재하는 데이터가 유출 될 수 있다. 메모리에 존재하는 데이터는 상황에 따라 다르겠지만, 개인키나 웹 서비스의 경우 사용자의 로그인 패킷(ID/PW) 및 웹 어플리케이션 관련 정보가 포함될 수 있다. 또한, 이러한 공격은 서버에 공격 관련 로그가 남지 않아 공격 징후를 판단하는데 쉽지 않다.
공격 형태
정상적인 HeartBeat 처리는 서버가 HeartBeat Request 메시지를 받으면, 받은 메시지의 페이로드 데이터를 그대로 복사한 HeartBeat Response 메시지를 클라이언트에 재전달한다.
코드 분석
>> 취약한 코드 ( openssl 1.0.1e )
ssl/d1_both.c and ssl/t1_lib.c
int dtls1_process_heartbeat(SSL *s)
{
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
위 코드는 DTLS 관련 heartbeat 처리 함수이다. (실제 공개된 공격코드에서는 dtls 버전 대신 tls 버전을 사용하지만, 코드는 동일) 이 코드는 사용자로 부터 받은 HeartBeat 패킷을 처리하는 함수이다.
첫번째 줄의 p라는 포인터 변수는 data는 SSLv3 레코드의 data를 가리키고 있다.
typedef struct ssl3_record_st
{
int type; /* type of record */
unsigned int length; /* How many bytes available */
unsigned int off; /* read/write offset into ‘buf’ */
unsigned char *data; /* pointer to the record data */
unsigned char *input; /* where the decode bytes are */
unsigned char *comp; /* only used with decompression – malloc()ed */
unsigned long epoch; /* epoch number, needed by DTLS1 */
unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */
} SSL3_RECORD;
전체 코드 중 hbtype이 TLS1_HB_REQUEST이면, 아래와 같은 코드를 수행하게 된다.
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
먼저 사용자가 보낸 데이터를 활용하여 메모리를 할당한다. 1 (응답 플래그) + 2(메시지 타입) + payload(사용자가 보내준 페이로드길이) + padding(16 바이트)
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
할당된 메모리의 첫 부분에 TLS1_HB_RESPONSE 이라는 응답이라는 플래그를 넣고,
memcpy(bp, pl, payload);
bp += payload;
/* Random padding */
RAND_pseudo_bytes(bp, padding);
r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
받은 데이터에서 payload 길이만큼 메모리 복사(memcpy) /* pl = p; */
만약 공격자가 payload 길이만 던지고 실제 payload 데이터를 넣지 않았다면??… 이러한 상황이면 공격자는 정해진 메모리 범위 바깥에 위치한 데이터까지 복사하게 된다.
이렇게 만들어진 응답 패킷은 다시 공격자에게 전송되어 진다.
>> 패치된 코드 ( openssl 1.0.1g )
http://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=96db902
패치된 버전에서는 아래와 같이 우선적으로 길이를 검사하도록 코드가 추가되었다. 패치된 코드는 페이로드가 없는 올바르지 않은 메시지나 페이로드 길이 필드의 값이 실제 메시지 크기를 초과할 경우 메시지를 폐기한다.
또한, 요청 메시지를 확인한 후에도, 페이로드 길이 필드값을 기반으로 계산된 값이 SSL3_RT_MAX_PLAIN_LENGTH 보다 클 경우 폐기 처리한다.