VMware Workstation PVSCSI LFH Escape (VMware-vmx on Windows 11)

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

버그 해부: fixed-size realloc + 분산된 OOB 쓰기

  • PVSCSI_FillSGI는 게스트의 scatter/gather 엔트리를 내부 배열로 복사한다. 초기에는 512 엔트리 정적 버퍼(0x2000)로 시작한다. 512를 초과하면 0x4000 바이트로 realloc하고, 기능적 버그 때문에 매 반복마다 realloc이 발생한다.
  • 재할당 크기는 커지지 않음: 0x4000 / 0x10-바이트 엔트리 = 1024 사용 가능 엔트리. 게스트가 >1024 엔트리를 공급하면, 각 새 엔트리는 방금 할당된 0x4000 청크보다 16바이트 뒤에 기록되어 인접 청크 헤더 또는 객체를 손상시킨다.
  • 오버플로우 내용: VMware는 {u64 addr; u64 len}을 저장하는데; 게스트는 {u64 addr; u32 len; u32 flags}를 제공한다. 32비트 len제로 확장(zero-extended) 되므로, 모든 16바이트 OOB 요소의 마지막 dword는 항상 0x00000000 이다.

LFH 제약 & 결정론적 “Ping-Pong” 배치

  • 0x4000 할당은 Windows 11 LFH에 배치된다 (버킷당 16개 청크, 0x10-바이트 메타데이터와 keyed checksum). 이후에 헤더 checksum이 맞지 않는 청크를 건드리면 프로세스가 종료되므로, 손상된 헤더는 절대 재사용되어서는 안 된다.
  • LFH는 랜덤한 free 청크를 반환하지만 가장 최근에 해제된 청크를 포함하는 버킷을 우선한다. 두 개의 무료 슬롯만 강제하려면:
  1. 할당기를 정렬하기 위해 모든 무료 0x4000 청크를 할당; 32 SVGA shaders를 스프레이하여 B1B2 버킷을 채운다.
  2. B1에서 하나의 고정된 shader(Hole0)만 남기고 나머지를 해제해 B1을 활성 상태로 유지; B1에 15 URBs를 할당한다.
  3. B2에서 하나의 shader(PONG)를 해제한 다음 즉시 Hole0을 해제한다. LFH는 두 개의 사용 가능한 슬롯 PING (B1)PONG (B2) 사이에서 번갈아 할당한다.
  • 1025번째 반복은 PONG 뒤의 헤더를 손상시키며(이후 절대 접근하지 않음); 1026번째 반복은 PING 뒤의 URB 첫 16바이트를 건드려(메타데이터 우회로 안전) 적용된다. 레이아웃을 안정적으로 반복하기 위해 자리 표시자 shaders로 PING/PONG을 회수(reclaim)한다.

Reap Oracle: 연속된 홀 표시

  • UHCI URB는 FIFO 큐에 존재하며 완전히 reaped 되면 해제된다. 제약된 16바이트 덮어쓰기 때문에 actual_len은 항상 0이 되어 마커를 제공한다.
  • URB를 순서대로 reap; 0으로 된 actual_len이 보이면 즉시 해제된 슬롯을 식별 가능한 shader로 채운다. 이를 반복하면 이후 인접성 기반 프리미티브를 위해 Hole0–Hole3을 알려진 순서의 네 개 연속 청크로 매핑할 수 있다.

제한된 쓰기를 임의 덮어쓰기로 전환하기 (coalescing 남용)

PVSCSI는 인접 엔트리를 AddrA + LenA == AddrB 조건으로 병합(coalesce)하고, 이후 엔트리를 위로 compact 한다.

  • 두 단계 오버플로우: PING(홀수 인덱스)에서 시작해 coalescing을 건너뛰도록 일찍 종료시킨 뒤; PONG(짝수 인덱스)에서 다시 트리거하여 갭을 채우고 fake S/G 엔트리를 포함한 스프레이된 shader로 계속 쓰기를 진행한다.
  • Vacuum + payload: [1023..2047] 엔트리를 {addr=0,len=0}로 설정하면 coalescing이 이들을 하나로 압축해 논리적 홀을 만든다. 그 이후 shader에 배치된 페이로드 엔트리들은 위로 이동되어 이전 메모리로 들어가 피해 URB 내부에 위치하게 된다.
  • 인접성 체크 우회: LenA=0으로 설정하면 조건이 AddrA==AddrB가 된다. 다음과 같은 쌍을 구성하라:
{addr = X, len = 0}
{addr = X, len = Y}

coalescing은 이를 {addr=X,len=Y}로 합친다. 짝수 인덱스의 제로-사이즈 요소는 제약된 오버플로우에서 나오고; 홀수 인덱스 값은 shader에 존재한다. 결과: 강제된 제로 dword에도 불구하고 임의의 16바이트 패턴을 만들 수 있다.

Hybrid URB infoleak via coalescing side-effects

  • 연속된 청크를 배치: [Hole0 (free/PING), URB1 (target), URB2 (valid, actual_len=0), URB3 (leak target)].
  • URB1을 연속된 fake 엔트리(크기 0xFFFFFFFF)로 채우고 URB2는 최소한으로 건드린다. Coalescing은 이들을 하나로 합치고; 합계 0xFFFFFFFF * 0x401는 URB1의 actual_len 오프셋 상위 dword를 0x400으로 설정한다.
  • 압축(compaction)은 뒤따르는 데이터를 위로 복사하여 URB2의 헤더를 URB1로 끌어온다. 이제 URB1은 유효한 헤더(파이프/리스트 포인터), actual_len=0x400, 그리고 이미 URB2 버퍼 끝에 위치한 데이터 포인터를 갖게 된다.
  • URB1을 reap하면 URB3 바로 앞에서 시작하는 0x400 바이트를 복사하여 URB3의 헤더/자기참조를 포함한 OOB read를 일으키고, 이는 절대 힙 주소를 드러내어 이후 위조 구조물에 대한 ASLR을 무력화한다.

Post-leak primitives (no re-triggering the bug)

  • Hole0을 차지하는 shader 내부에 URB 구조체를 위조한 다음 coalescing의 “move up“을 이용해 URB1을 위조 데이터로 교체한다.
  • URB를 지속화: URB1.next = Hole0으로 설정하고 refcount를 증가시킨다; URB1을 reap하면 Hole0-기반의 fake URB가 FIFO 헤드에 놓인다. 이후 프리미티브는 Hole0의 재할당만으로 이루어진다.
  • Arbitrary read: 원하는 data_ptractual_len을 가진 fake URB를 만들고 reap하여 호스트 메모리를 게스트로 복사한다.
  • Arbitrary write (32-bit): pipe가 제어된 메모리를 가리키는 fake URB를 만들고 UHCI의 TDBuffer writeback을 악용해 임의 주소에 선택한 dword를 저장한다.
  • Arbitrary call: USB pipe 콜백을 덮어쓴다; 호스트는 RCX+0x90에 제어된 데이터와 함께 호출한다. WinExec를 동적으로 해결(게스트 측에서 Kernel32 읽기)하고 vmware-vmx 내부의 CFG-유효 가젯으로 피벗하여 RCX+0x100에서 인자를 로드한 뒤 WinExec("calc.exe")로 전달한다.

LFH timing side-channel to learn the initial bucket offset

  • 결정론적 Ping-Pong은 LFH의 free-청크 오프셋(16 슬롯 중 어느 슬롯이 먼저 히트될지)을 알아야 한다. VMware 백도어 명령어(inl %%dx, %%eax)와 동기화된 VMware Tools 명령 vmx.capability.unified_loop0x4000-바이트 문자열을 사용하면, 호출당 두 개의 0x4000 할당을 강제한다.
  • gettimeofday로 8번의 호출(16 할당)을 타이밍 측정하면, LFH가 새 버킷을 생성할 때 일관된 스파이크가 하나 나타난다. 한 번 더 할당을 추가해 반복하면: 스파이크가 같은 인덱스에 머물면 오프셋은 홀수, 이동하면 짝수; 그렇지 않으면 노이즈 때문에 재시작.
  • 주의: unified_loop는 고유 문자열을 해제 불가능한 리스트에 저장하여 O(n) 조회 오버헤드와 노이즈 증가를 초래하므로, 사이드채널은 빠르게 수렴해야 한다.

References

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기