뭐하지
지금까지 분석 기법이랑 P-Code를 살펴봤고, 이제 뭘 해볼까 고민하다가 나는 CVE 하나를 파보기로 했다. 분석할 취약점의 조건은 다음과 같다.
- 특정 CVE가 들어있는 버전의 무언가를 쉽게 다운받을 수 있어야 한다.
- 그 프로그램을 직접 실행해볼 수 있어야 한다.
- 해당 프로그램의 소스코드를 볼 수 있어야 한다.
- (추가적) 빌드가 쉬우며, 내가 코드를 패치해서 다시 빌드해볼 수 있었으면 좋겠다.

그래서 목표로 한 취약점은 CVE-2014-0160, 하트블리드이다. 하트블리드는 2014년에 발견된 취약점으로, 대칭/비대칭 암호화 프로토콜이 구현된 오픈소스 라이브러리인 OpenSSL에서 발견된 취약점이다.

하트블리드 취약점이 어떻게 이뤄지는지에 대한 짤이다. 문제는 OpenSSL의 TLS(Transport Layer Security)에서 발생했다. TLS는 HTTPS처럼 인터넷 통신 과정에서 내용을 암호화하는 프로토콜으로, 한번 연결에 비용이 비싼 이유로 연결을 계속 유지하고자 한다. 이 과정에서 연결이 살아있는지를 확인하기 위해 주기적으로 살아있는지 요청을 클라이언트가 보내게 되는데, 이를 Heartbeat Request라고 한다. 서버는 이를 통해 연결이 살아있는지 확인한다.
이 Heartbeat 패킷은 Request인지 Responce인지를 담은 Type, 문자열 길이인 Length, 그리고 문자열 이렇게 세 가지를 담는다. 클라이언트가 Request, 5, HELLO를 묶은 패킷을 보내면 서버는 Responce, 5, HELLO 이렇게 Request로 받은 문자열을 그대로 돌려주게 된다.
여기서 문제가 발생하는데, 당시 취약 버전의 OpenSSL은 Heartbeat에 memcpy(버퍼, 요청 payload 시작 주소, 클라이언트가 적은 Length 값)을 사용했는데, 여기서 클라이언트가 보낸 문자열 길이를 검사하는 수식이 들어가지 않은 것이다. 이 과정에서 입력받은 문자열을 넘어서 유출되면 안되는 서버 내부 값들이 클라이언트가 서버에 악의적으로 Request, 5000, HELLO를 보내고, 서버측이 취약 버전의 OpenSSL을 쓰고 있다면 HELLO에 서버 메모리 HELLO 뒤 4995바이트를 붙여서 보내버린 것이다. 직접 예제를 돌려봤다.

취약 버전 OpenSSL을 github에서 clone한 뒤 OpenSSL 내에 있는 테스트용 더미 진단 서버를 웹서버 모드(-www)로 열었다. 웹서버 모드로 연 것은 curl 등으로도 접속할 수 있도록 하기 위함이다.

다음으로 python을 켜서 서버에 보낼 Request를 만들어줬다. 18 03 02 00 03은 TLS 헤더를 뜻하고, 01 40 00이 Heartbeat 본문이다. 총 3바이트고, 01은 Request, 40 00은 length로 0x4000, 즉 16384바이트를 보냈다고 이야기한다. 그리고 보다시피 뒤에 추가로 보낸 payload는 없다.

그리고 curl을 통해 내가 연 서버에 누군가 로그인을 수행해 이런 데이터를 보냈다고 하자. 이후 다른 누군가가 방금 본 패킷을 서버에 전송한다면?

공격자는 아무 문자열도 보내지 않았지만 서버로부터 16KB짜리 서버 메모리 내부를 그대로 받고, 그 중에 다른 사람이 보낸 데이터를 훔쳐 읽은 것을 볼 수 있다.
목표는 다음과 같다. 먼저 취약 버전 다운로드 및 빌드, PoC까지 확인했으며,
- 빌드한 바이너리를 Ghidra에 올려 눈으로 확인하기
- 정적 분석 탐지 스크립트 제작
- 취약 지점 패치 후 문제 해결해보기
- 패치 후 빌드한 뒤 Ghidra에 올려 탐지 스크립트에 탐지되지 않는지 확인하기
정도가 될 것 같다.