
VirtualBox 7.2.20 - 높음 - 로컬 - 힙 오버플로 - HGCM SSM cParms 랩
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vbox-hgcm-ssm-cparms
분류: 힙 오버플로 도달 범위: 로컬 (호스트)
Oracle VM VirtualBox OSS 7.2.20 (f2792cf44) - Oracle
실행 중인 HGCM은 여전히 cParms > 32를 거부한다. 저장 상태 로드는 그렇지 않다. vmmdevR3HgcmLoadState는 VMMDEV_MAX_HGCM_PARMS 상한 없이 .sav에서 cParms를 읽은 뒤, uint32_t cbCmd로 명령 크기를 계산한다. cParms = 0x4000000일 때 이 곱은 sizeof(VBOXHGCMCMD)(~120)로 랩어라운드된다. 첫 번째 enmType 저장은 이미 블록을 넘어선 4바이트 쓰기다.
악성 저장 상태를 복원하는 호스트 사용자는 VM 프로세스 힙을 오버플로한다. 게스트는 Save를 통해 이를 심을 수 없다.
| ID | 아직 CVE 없음 |
| 분류 | 힙 오버플로 (VM 프로세스 OOB 쓰기, 호스트 커널 탈출 아님) |
| 도달 범위 | 로컬 (호스트) (UI:R 저장 상태 / 스냅샷 복원) |
| CWE | CWE-190, CWE-787 |
| CVSS | High: 7.8 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
| 제품 | VirtualBox OSS |
| 영향 버전 | 7.2.20 (f2792cf44f663989818507e36d3db5f978523c5e) |
| 인증 | 호스트 사용자가 저장된 VM, 스냅샷 또는 .sav를 복원 |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1 전용. 소스 하네스. VirtualBox 부팅 없음, .sav 파싱 없음. |
대기 중인 VMMDev HGCM CALL을 담은 저장 상태를 가진 VM 폴더를 운영자에게 건넨다. 운영자가 저장된 VM에서 Start를 클릭하거나, 스냅샷을 복원하거나, .sav를 채택하면 된다. 콘솔 복원은 SSMR3를 거쳐 vmmdevR3LoadExec로, 다시 vmmdevR3HgcmLoadState로 이어진다. 이것이 UI:R이다. 남는 것은 게스트 하이퍼콜이 아니라 파서다.
VMMDev는 기본 활성화다. 3D 없음. Guest Additions 없음. VRDP 없음. PUEL 아님.
| 단계 | 위치 | 부여되는 것 |
|---|---|---|
| 복원 | 호스트 UI / 저장된 VM에 대한 VBoxManage startvm | 호스트 사용자가 악성 .sav를 연다. 필요한 유일한 클릭이다. |
| SSM 단위 | VMMDev vmmdevR3HgcmLoadState (VMMDevHGCM.cpp:2043-2091) | VMMDEV_MAX_HGCM_PARMS(32) 상한 없이 SSMGetU32로 cParms에 읽어들인다. |
| 할당 랩 | vmmdevR3HgcmCmdAlloc (VMMDevHGCM.cpp:376-381) | uint32_t cbCmd = sizeof(VBOXHGCMCMD) + cParms * (40+24). cParms = 0x4000000은 ~120으로 랩어라운드된다. 그 크기의 RTMemAllocZ는 힙 예산 내에서 성공한다. |
| 첫 저장 | 로드 루프 paGuestParms[i].enmType (VMMDevHGCM.cpp:2085-2091) | paGuestParms는 pCmd + sizeof(VBOXHGCMCMD)이며, 랩 이후 이미 할당 끝이다. 첫 번째 타입 dword는 4바이트 OOB 쓰기다. 스트림에는 CALL 헤더와 파라미터 타입 하나만 있으면 된다. 6,700만 개의 레코드는 필요 없다. |
| 종류 | 출처 | 공격자가 얻는 것 |
|---|---|---|
| 정찰 | 없음 | 오버플로 바이트는 sav가 선택한다. 이는 유출이 아니다. 힙 내용은 파일로 반환되지 않는다. |
| 무결성 | 실험실 입증 | cParms=0x4000000은 uint32_t cbCmd를 120으로 래핑한다. 하나의 i=0 enmType 저장이 0xA5 카나리를 덮어쓴다. canary0=56424f58은 VBOX-HGCM-SSM-CPARMS-WITNESS의 VBOX 접두사이다. 대조군 cParms=2는 카나리를 그대로 둔다. |
| 무결성 | 추론됨 | 추가로 sav가 제어하는 parm 레코드(타입 dword와 32/64비트 또는 포인터-디스크립터 필드)는 VirtualBoxVM / VBoxHeadless의 약 120바이트 RTMemAllocZ에서 선형으로 기록한다. 이는 VM 프로세스 내의 쓰기 프리미티브로, QEMU 마이그레이션 memcorr과 같은 부류이다. 이 팩은 명령 포인터 제어를 입증하지 않는다. |
| 가용성 | 추론됨 | 덮어쓰기가 제어된 쓰기로 전환되지 않으면 VBoxVM이 충돌한다. 하네스는 카나리 덮어쓰기이며, VirtualBox 프로세스 충돌은 아니다. |
vmmdevR3HgcmCallAlloc은 여전히 cParms > 32를 거부한다(VMMDevHGCM.cpp:921-926). 게스트는 정당한 Save를 통해 래핑을 심을 수 없다.VMMDEV_MAX_HGCM_PARMS 32, VMMDEV_MAX_HGCM_DATA_SIZE 128MiB).offValue / cbValue memcpy는 HGCM 클라이언트가 동일한 sav에서 복원하는 경우에만 두 번째 호스트 힙 쓰기가 된다. 이 실험실은 그것이 아니다.완전한 호스트-사용자 / 호스트-커널에는 여전히 하드닝 잔여물이 필요하다. 이 덮어쓰기를 공개 IDispatch 우회와 결합하는 것은 폐기되었다.
나는 VirtualBox/virtualbox 태그 v7.2.20 peel f2792cf44를 고정했다. CVE-2013-5892가 이 부류였기 때문에 라이브 HGCM을 먼저 읽었다: VBOXHGCMCMD로의 인자 크기 래핑. 라이브 경로에는 여전히 상한이 있다:
ASSERT_GUEST_STMT_RETURN( cParms <= (cbHGCMCall - sizeof(VMMDevHGCMCall)) / cbHGCMParmStruct
&& cParms <= VMMDEV_MAX_HGCM_PARMS,
LogRelMax(50, ("VMMDev: request packet with invalid number of HGCM parameters: ...")),
VERR_INVALID_PARAMETER);
Load는 그렇지 않습니다. vmmdevR3HgcmLoadState는 sav에서 cParms를 가져와 동일한 할당자를 호출합니다:
const uint32_t cbCmd = sizeof(VBOXHGCMCMD) + cParms * (sizeof(VBOXHGCMGUESTPARM) + sizeof(VBOXHGCMSVCPARM))
+ (enmCmdType == VBOXHGCMCMDTYPE_CONNECT ? sizeof(HGCMServiceLocation) : 0);
AssertCompile 주석은 이 핀에서 CMD를 120, guestparm을 40, svcparm을 24로 지정합니다. cParms = 0x4000000은 64비트 곱을 0x100000000 + 120으로 만듭니다. uint32_t에 대한 저장은 120을 유지합니다. 그런 다음:
for (i = 0; i < cParms; ++i)
{
VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
rc = pHlp->pfnSSMGetU32(pSSM, &u32);
pGuestParm->enmType = (HGCMFunctionParameterType)u32;
paGuestParms는 pCmd + sizeof(VBOXHGCMCMD)에 위치합니다. 랩어라운드 이후 그 지점은 할당 영역의 끝입니다. 첫 번째 저장이 이미 OOB입니다.
이미 기록된 잘못된 접근들: 라이브 HGCM을 잔여물로 취급하는 것(여전히 32에서 상한이 걸림), 하네스에서 6,700만 번의 저장을 순회하는 것(첫 번째 dword가 증거임), 게스트가 Save를 통해 이를 심을 수 있다고 주장하는 것, VM 프로세스 힙 스매시를 호스트 커널 탈출로 과장하는 것, 공개 IDispatch를 체이닝하는 것.
극적 요소: 리버스 셸, .sav 파서, VirtualBox 부팅. 오라클은 랩어라운드와 VBOX-HGCM-SSM-CPARMS-WITNESS의 카나리 스매시입니다.
cd lab
./run.sh
또는 팩 루트에서:
python3 vbox-hgcm-ssm-cparms-Abraxas-Labs.py
루프백 127.0.0.1:19210에서 소스 하네스를 실행합니다. Compose 프로젝트 vbox-hgcm-ssm-cparms. gadget.c는 핀 크기, uint32_t cbCmd 공식, 라이브 32 상한, 그리고 하나의 로드 경로 enmType 저장을 복사합니다. VirtualBox를 부팅하지 않으며 .sav를 파싱하지도 않습니다. 루프백에 바인딩하세요.
SUCCESS vbox-hgcm-ssm-cparms wrap=yes oob=yes live-cap=32 canary-smashed=yes VBOX-HGCM-SSM-CPARMS-WITNESS
vmmdevR3HgcmLoadState에서 (그리고 v17 이전의 cLinAddrs 경로에서도) vmmdevR3HgcmCallAlloc이 이미 하는 것과 동일한 방식으로 SSM cParms를 VMMDEV_MAX_HGCM_PARMS로 제한하십시오. cbCmd를 uint64_t / size_t로 크기 지정하고 랩어라운드를 거부하십시오. 저장된 상태의 정수는 라이브 게스트 정수보다 더 신뢰할 수 없습니다.
f2792cf44f663989818507e36d3db5f978523c5e)VMMDevHGCM.cpp 라이브 상한 · 할당 랩어라운드 · SSM 로드VMMDEV_MAX_HGCM_PARMSAbraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - [email protected]