[하루한줄] CVE-2021-2145: Oracle VirtualBox의 권한 상승 취약점

URL

GSOh No! Hunting for Vulnerabilities in VirtualBox Network Offloads

Target

  • Oracle VirtualBox

Explain

Oracle사의 가상화 프로그램 VirtualBox에서 세 취약점이 발견되었습니다. heap buffer overflow로 트리거되는 권한 상승 취약점 둘과 Out-of-Bounds로 인한 정보 유출 취약점입니다.

취약점들은 게스트 머신에서 외부 네트워크로 패킷을 보내는 데 사용되는 NAT의 반가상화 GSO 구현 코드에서 발견되었습니다.

GSO(Generic Segmentation Offload) : 패킷을 외부로 전송 시 분할하는 과정을 CPU가 아닌 NIC가 처리하는 오프로딩(연산 중 일부를 프로세서가 아닌 다른 장치에서 처리) 방법

CVE-2021-2145 – Oracle VirtualBox NAT Integer Underflow Privilege Escalation Vulnerability

NAT 코드는 GSO 프레임을 수신하면 전체 이더넷 패킷을 가져와 새 mbuf 메시지 버퍼를 할당하고 패킷을 mbuf에 복사합니다. mbuf는 TCP/IP 에뮬레이션용 라이브러리인 Slirp 라이브러리에 전달됩니다.

아래의 함수는 sizeMCLBYTES로 초기화해 초기값 0x800을 가지며, 이후에 if~else if 루틴에서 cbMin이 이보다 크면 MJUM9BYTES(0x2400), MJUM16BYTES(0x4000)으로 size를 설정해 할당 크기를 결정하고 cbMin 크기의 데이터를 복사합니다.

struct mbuf *slirp_ext_m_get(PNATState pData, size_t cbMin, void **ppvBuf, size_t *pcbBuf)
{
   struct mbuf *m;
   int size = MCLBYTES;    // 0x800 bytes
   LogFlowFunc(("ENTER: cbMin:%d, ppvBuf:%p, pcbBuf:%p\n", cbMin, ppvBuf, pcbBuf));
 
   if (cbMin < MCLBYTES)
       size = MCLBYTES;     // 0x800 bytes
   else if (cbMin < MJUM9BYTES)
       size = MJUM9BYTES;   // 0x2400 bytes
   else if (cbMin < MJUM16BYTES)
       size = MJUM16BYTES;  // 0x4000 bytes
   else
       AssertMsgFailed(("Unsupported size"));
 
   m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, size);
...

문제는 sizeMJUM16BYTES(0x4000)보다 클때 발생합니다. sizeMJUM16BYTES보다 크면 AssertMsgFailed 함수를 호출해 예외를 발생시킵니다. 그러나 AssertMsgFailed는 디버그 모드에서만 작동하는 Assert 문으로 릴리즈 모드에서는 작동하지 않습니다. 따라서 릴리즈 모드에서는 아래 코드만 실행됩니다.

if (cbMin < MCLBYTES)
    size = MCLBYTES;     // 0x800 bytes
else if (cbMin < MJUM9BYTES)
    size = MJUM9BYTES;   // 0x2400 bytes
else if (cbMin < MJUM16BYTES)
    size = MJUM16BYTES;  // 0x4000 bytes
  
m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, size);

만약 cbMin0x4000을 넘는다면 위 조건문들을 모두 통과하게 되고 size의 초기값인 0x800 만큼할당합니다. 따라서 0x800 크기로 할당된 힙에 0x4000 크기의 데이터가 복사되면서 heap buffer overflow가 트리거되며 권한 상승으로 이어질 수 있습니다.

CVE-2021-2310 - Oracle VirtualBox NAT Heap-based Buffer Overflow Privilege Escalation Vulnerability

게스트가 제공한 GSO 매개변수가 유효한지 확인하는 PDMNetGsoIsValid함수에서 Assert 함수가 존재합니다.

DECLINLINE(uint32_t) PDMNetGsoCalcSegmentCount(PCPDMNETWORKGSO pGso, size_t cbFrame)
{
   size_t cbPayload;
   Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), cbFrame));
   cbPayload = cbFrame - pGso->cbHdrsSeg;
   return (uint32_t)((cbPayload + pGso->cbMaxSeg - 1) / pGso->cbMaxSeg);
}

릴리즈 빌드에서 컴파일되지 않는 Assert로 인해 GSO 매개변수가 유효한지 확인하는 PDMNetGsoIsValid 함수가 호출되지 않고, 그 결과로 유효하지 않은 GSO 매개변수가 허용되며 이후 이 매개변수는 memcpy의 인자로 사용됩니다. 따라서 할당된 크기보다 더 많은 데이터를 복사하도록 매개변수를 전달해 heap buffer overflow를 통한 권한 상승이 가능합니다.

CVE-2021-2442 - Oracle VirtualBox NAT UDP Header Out-of-Bounds

VirtualBox는 TCP와 UDP에 대해 체크섬 오프로딩을 지원하는데, 체크섬 오프로딩 수행 과정에서 호출되는 RTNetUDPChecksum 함수에 취약점이 존재합니다.

RTDECL(uint16_t) RTNetUDPChecksum(uint32_t u32Sum, PCRTNETUDP pUdpHdr)
{
   bool fOdd;
   u32Sum = rtNetIPv4AddUDPChecksum(pUdpHdr, u32Sum);
   fOdd = false;
   u32Sum = rtNetIPv4AddDataChecksum(pUdpHdr + 1, RT_BE2H_U16(pUdpHdr->uh_ulen) - sizeof(*pUdpHdr), u32Sum, &fOdd);
   return rtNetIPv4FinalizeChecksum(u32Sum);
}

pUdpHdr→uh_ulen 의 범위 검사가 없어 sizeof(*pUdpHdr) 보다 작은 경우 연산 과정에서 integer underflow가 발생하고 이로 인해 OOB Access로 인한 정보 유출이 가능합니다.