[하루한줄] 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 라이브러리에 전달됩니다.
아래의 함수는 size를 MCLBYTES로 초기화해 초기값 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);
...
문제는 size가 MJUM16BYTES(0x4000)보다 클때 발생합니다. size가 MJUM16BYTES보다 크면 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);
만약 cbMin이 0x4000을 넘는다면 위 조건문들을 모두 통과하게 되고 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로 인한 정보 유출이 가능합니다.
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.