[하루한줄] SLP heap overflow를 통한 VMware ESXi pre-auth RCE 취약점
URL
CVE-2020-3992 & CVE-2021-21974: PRE-AUTH REMOTE CODE EXECUTION IN VMWARE ESXI
Target
- VMware ESXi
- 7.0 before ESXi70U1c-17325551
- 6.7 before ESXi670-202102401-SG
- 6.5 before ESXi650-202102101-SG
Explain
VMware ESXi에서 pre-authentication remote code execution 취약점이 발견되었습니다. 해당 취약점은 Service Location Protocol(SLP) 서비스에서 발생합니다.
Service Location Protocol(SLP)는 VMware ESXi 기본 설치 시 TCP와 UDP의 427 포트를 리스닝하는 네트워크 서비스입니다. VMware는 OpenSLP 1.0.1을 기반으로 기능을 추가하여 서비스를 구현하였습니다. 이 서비스는 인증 없이 네트워크 input을 구문 분석하고 root 권한으로 실행하므로 ESXi SLP 서비스의 취약점을 통해 pre-auth remote code execution이 가능합니다.
VMware의 SLP 코드의 일부분은 다음과 같습니다.
__int64 __fastcall SLPParseSrvUrl(int srvurllen, const char *srvurl, _QWORD *a3)
{
// ...
obuf = calloc(1uLL, srvurllen + 53LL);
if ( !obuf )
return 12LL;
v6 = strstr(srvurl, ":/"); // <-- out-of-bounds string search
if ( !v6 )
{
free(obuf);
return 22LL;
}
memcpy((char *)obuf + 41, srvurl, v6 - srvurl); // <-- heap overflow
// ...
}
srvurl
은 네트워크 input을 통해 받는 값이지만 함수에서 strstr()
을 사용하기 전에 srvurl
의 끝에 NULL 바이트를 추가하지 않습니다. 따라서 범위 밖의 문자열을 검사하여 memcpy
부분에서 heap overflow가 발생합니다.
SLP를 통해 Remote Code Execution을 하기 위해 SLP에서 송수신에 사용되는 struct SLPBuffer
의 구조는 다음과 같습니다.
typedef struct _SLPBuffer
{
SLPListItem listitem;
size_t allocated;
unsigned char* start;
unsigned char* curpos;
unsigned char* end;
// buffer data is appended
}*SLPBuffer;
typedef struct _SLPDSocket
{
SLPListItem listitem;
int fd;
time_t age;
int state;
// ...
SLPBuffer recvbuf; /* Incoming socket stuff */
SLPBuffer sendbuf;
// ...
}SLPDSocket;
VMware의 SLP 코드를 통해 Remote Code execution을 트리거하는 과정은 다음과 같습니다.
connection->state
를STREAM_WRITE_FIRST
로 덮어씁니다. 메모리를 leak 하기 위해sendbuf->curpos
를sendbuf->start
로 리셋시켜야 합니다.sendbuf->start
의 일부분을 2개의 널 바이트로 덮어씁니다. 연결 후 수신을 기다리면,sendbuf
의 주소를 포함한 메모리를 leak 할 수 있습니다.mmap()
으로 할당된recvbuf
를 leak 하기 위해 새로운 연결을 하고sendbuf->curpos
를 덮어씁니다. mmap 된 주소를 통해 libc base 주소를 얻을 수 있습니다.free_hook
의 주소를 설정하기 위해 새로운 연결에서recvbuf->curpos
를 덮어씁니다. 연결 후 전송이 시작되면free_hook
을 덮어쓸 수 있습니다.- 접속을 종료하면
free_hook
이 호출되어 ROP 체인이 시작됩니다.
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.