VirtualBox Slirp NAT 패킷 힙 익스플로잇
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
요약
- VirtualBox는 패킷 버퍼(mbufs)가 인라인 메타데이터와 함수 포인터 콜백(
pfFini,pfDtor)을 가진 커스텀 zone allocator에 존재하는, 크게 수정된 Slirp 포크를 포함한다. - 게스트는 신뢰된
m->m_len을 공격자가 제어하는 IP 헤더 길이로 덮어쓸 수 있으며, 이는 이후의 모든 경계 검사들을 무력화하고 infoleak 및 overwrite primitives를 유발한다. - checksum이
0이고ip_len이 과도한 UDP 패킷을 악용하면, 게스트는 mbuf 꼬리와 인접 청크의 메타데이터를 exfiltrate하여 힙 및 zone 주소를 알아낼 수 있다. - 조작된 IP 옵션을 제공하면
ip_stripoptions()가 제자리에서memcpy()로 너무 많은 데이터를 복사하도록 강제되어, 공격자가 다음 mbuf의struct item헤더를 덮어쓰고 그zone필드를 완전히 제어되는 데이터로 가리키게 할 수 있다. - 손상된 mbuf를 free하면 공격자가 제공한 인수를 가진
zone->pfFini()가 호출된다; 이를memcpy@plt로 가리키게 하면 arbitrary copy/write primitive가 되어 non-PIE VirtualBox 바이너리 내부의 GOT 엔트리나 다른 제어 데이터로 향하도록 조종할 수 있다.
Packet allocator anatomy
VirtualBox는 각 인터페이스마다 zone_clust라는 zone에서 들어오는 모든 Ethernet 프레임을 할당한다. 각 0x800-byte 데이터 청크는 인라인 헤더가 앞에 붙는다:
struct item {
uint32_t magic; // 0xdead0001
void *zone; // uma_zone_t pointer with callbacks
uint32_t ref_count;
LIST_ENTRY(item) list; // freelist / used list links
};
mbuf가 해제될 때 호출 스택 m_freem -> ... -> slirp_uma_free()는 인라인 헤더를 신뢰합니다:
uma_zfree_arg()는item = (struct item *)mem - 1을 재계산하고item->zone을 검증해야 하지만Assert()는 릴리스 빌드에서 컴파일되지 않습니다.slirp_uma_free()는zone = item->zone을 로드하고 조건 없이zone->pfFini(zone->pData, data_ptr, zone->size)를 실행한 다음zone->pfDtor(...)를 호출합니다.
따라서 mbuf 헤더에 대한 모든 write-what-where는 free() 중에 제어 가능한 간접 호출로 이어집니다.
Infoleak via m->m_len override
VirtualBox는 ip_input() 상단에 다음을 추가했습니다:
if (m->m_len != RT_N2H_U16(ip->ip_len))
m->m_len = RT_N2H_U16(ip->ip_len);
Because the assignment happens before verifying the IP header, a guest can advertise any length up to 0xffff. The rest of the stack (ICMP, UDP, fragmentation handlers, etc.) assumes m->m_len is trustworthy and uses it to decide how many bytes to copy off the mbuf.
체크섬이 0인 UDP 패킷(즉 “no checksum”)을 사용하라. NAT fast-path는 payload 무결성을 검사하지 않고 m->m_len 바이트를 전달하므로 ip_len을 부풀리면 Slirp가 실제 버퍼를 넘어 읽고 heap residues를 게스트나 NAT 밖의 협력하는 외부 헬퍼로 반환하게 된다. 청크 크기가 2048 바이트이므로 leak은 다음을 포함할 수 있다:
- 다음 mbuf의 inline
struct item— freelist 순서와 실제zone포인터를 드러낸다. magic필드 같은 heap cookies — 이후 corruptions를 수행할 때 유효해 보이는 헤더를 만들도록 도움.
IP options로 인접 청크 헤더 덮어쓰기
같은 잘못된 길이는 패킷을 ip_stripoptions()를 통하게 하여 overwrite 기본 동작으로 바꿀 수 있다(이는 IP header에 options가 있고 payload가 UDP/TCP일 때 트리거된다). 헬퍼는 m->m_len으로부터 복사 길이를 계산하고 memcpy()를 호출해 전송 헤더를 제거된 옵션 위로 이동시킨다:
- 긴
ip_len을 제공하여 계산된 이동 길이가 현재 mbuf를 넘어가도록 한다. - IP options를 적은 수 포함시켜 Slirp가 stripping 경로로 진입하게 한다.
memcpy()가 실행되면 다음 mbuf에서 읽어 현재 mbuf의 payload와 inline header에 덮어써magic,zone,ref_count등을 손상시킨다.
할당자는 동일 인터페이스의 패킷을 freelist 상에서 인접하게 유지하므로, 이 오버플로우는 적절한 heap grooming 이후에 다음 청크를 결정론적으로 덮친다.
uma_zone_t 위조로 pfFini 탈취하기
인접한 struct item을 손상시킬 수 있게 되면, 익스플로잇은 다음과 같이 진행된다:
- 유출된 heap 주소를 사용해 게스트가 완전히 제어하는 mbuf 안에 가짜
uma_zone구조를 구축한다. 다음을 채운다:pfFini에memcpy()의 PLT 엔트리pData에 원하는 목적지 포인터(예: GOT 엔트리, vtable 슬롯, 함수 포인터 배열)size에 복사할 바이트 수- 선택:
pfDtor를 2단계 호출로 설정(예: 새로 쓴 함수 포인터를 호출하도록)
- 대상 mbuf의
zone필드를 가짜 구조체 포인터로 덮어쓰고,list포인터를 조정해 freelist 장부가 충돌을 피할 수 있을 만큼 일관되게 유지되게 한다. - mbuf를 free한다. 그러면
slirp_uma_free()는 mbuf에 게스트 제어 데이터가 남아있는 동안memcpy(dest=pData, src=item_data, n=size)를 실행해 arbitrary write를 발생시킨다.
Linux VirtualBox 바이너리는 non-PIE이므로 memcpy와 system의 PLT 주소가 고정되어 있어 직접 사용할 수 있다. 게스트는 또 가로채진 호출이 실행될 때 참조가 유지되는 다른 mbuf 안에 /bin/sh 같은 문자열을 저장할 수 있다.
Fragmentation을 통한 Heap grooming
Slirp의 인터페이스별 zone은 깊이가 3072 청크이고 처음에는 연속 배열로 할당되어 freelist가 높은 주소에서 낮은 주소로 순회된다. 결정론적 인접성은 다음으로 달성할 수 있다:
- NAT에 일정 크기의
IP_MFfragment를 다수 흘려보내어 reassembly 코드가 예측 가능한 mbuf 시퀀스를 할당하게 한다. - 타임아웃되는 fragment를 보내 특정 청크를 재활용하여 frees가 LIFO 순서로 freelist로 돌아가게 한다.
- freelist walk에 대한 지식을 사용해 미래의 피해자 mbuf를 IP options overflow를 실을 mbuf 바로 뒤에 배치한다.
이러한 grooming은 오버플로우가 목표 struct item을 정확히 타격하도록 하고 가짜 uma_zone이 leak primitive의 범위 내에 남아있게 보장한다.
From arbitrary write to host code execution
memcpy-on-free primitive를 이용하면:
- 공격자가 제어하는
/bin/sh문자열과 명령 버퍼를 안정적인 mbuf로 복사한다. - primitive를 사용해 GOT 엔트리나 간접 호출 지점(예: NAT 장치 상태 내부의 함수 포인터)을
system()의 PLT 엔트리로 덮어쓴다. - 덮어쓴 호출을 트리거한다. VirtualBox가 NAT 장치를 호스트 프로세스 내부에서 실행하므로 페이로드는 VirtualBox를 실행하는 사용자의 권한으로 실행되어 guest-to-host escape를 허용한다.
대체 페이로드로는 힙 메모리에 소형 ROP 체인을 심고 그 주소를 자주 호출되는 콜백에 복사하거나, pfFini/pfDtor 자체를 연쇄된 gadgets로 재지정해 반복적인 쓰기를 수행하는 방법이 있다.
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


