Repository: notscimmy/libelevate Branch: master Commit: 56c2292157f9 Files: 14 Total size: 45.7 KB Directory structure: gitextract_6xerqbfn/ ├── .gitignore ├── .gitmodules ├── README.md ├── libelevate/ │ ├── kerneloffsets.cpp │ ├── kerneloffsets.h │ ├── libelevate.cpp │ ├── libelevate.h │ ├── libelevate.vcxproj │ ├── libelevate.vcxproj.filters │ └── structs.h ├── libelevate.sln └── testlibelevate/ ├── main.cpp ├── testlibelevate.vcxproj └── testlibelevate.vcxproj.filters ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ **/Properties/launchSettings.json # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ ================================================ FILE: .gitmodules ================================================ [submodule "libcapcom"] path = libcapcom url = https://github.com/notscimmy/libcapcom.git ================================================ FILE: README.md ================================================ # libelevate - Bypass ObRegisterCallbacks via elevation ## ObRegisterCallbacks One of the goals of antivirus and anticheat software is to block malicious programs from reading and writing memory of other processes. Before more advanced enhancements to **PatchGuard**, anticheat and antivirus software were notorious for hooking the kernel, via an SSDT (System Service Dispatch Table) hook or straight up byte-patching, in order to monitor API calls from user-mode. **ObRegisterCallbacks** is Microsoft's official and supported method to intercept API calls that grant access rights of a process to another process. A very nice article that explains the internals of it can be found here: https://douggemhax.wordpress.com/2015/05/27/obregistercallbacks-and-countermeasures/. ## Reversing OpenProcess By design, a modern operating system implements **virtual memory** and **process isolation**, meaning that an arbitrary process X cannot reference memory in another arbitrary process Y. ```OpenProcess``` is a powerful WinAPI function that allows a user-mode process to gain access rights to other processes by asking for permission granted by the kernel. This access right is the entrypoint to enable many other powerful functions that manipulate other processes such as ```ReadProcessMemory```, ```WriteProcessMemory```, and ```VirtualAllocEx```. ```OpenProcess``` is an API that exported by **kernel32.dll**, which is part of the Windows subsystem that links against **ntdll.dll**, the library that contains all of the syscall stubs that will eventually call into the code in **ntoskrnl.exe** which does most of the work when it comes to implementing the higher level WinAPI functions. Let's talk about the flow of code that gets executed when someone links against the Window SDK and calls ```OpenProcess```. The first thing that happens is that ```NtOpenProcess```, the underlying **ntdll.dll** API, gets called. Then, a syscall instruction gets executed with the index that maps to the equivalent to ```NtOpenProcess``` on the syscall table. At this point, execution is transferred to the kernel, where the actual implementation lives. Finally, an internal and undocumented function inside **ntoskrnl.exe**, ```PsOpenProcess``` gets called, and the result of that function is returned. Here's a diagram that is basically a tl;dr. ![alt text](https://i.imgur.com/NqWD34r.png) ## Registering Callbacks ```ObRegisterCallbacks``` is a pretty well documented API on MSDN that clearly defines all the structures and parameters. Each callback you want to register is defined by the following structure and function prototypes: ```cpp OB_PREOP_CALLBACK_STATUS PobPreOperationCallback( PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation ); void PobPostOperationCallback( PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation ); typedef struct _OB_OPERATION_REGISTRATION { POBJECT_TYPE *ObjectType; OB_OPERATION Operations; POB_PRE_OPERATION_CALLBACK PreOperation; POB_POST_OPERATION_CALLBACK PostOperation; } OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION; ``` Example code for how to properly register callbacks can be found here: https://github.com/Microsoft/Windows-driver-samples/blob/master/general/obcallback/driver/callback.c ## Intercepting an OpenProcess Call So the next logical question at this point would be: where do these callbacks actually get called? Recall the diagram for the flow of execution for ```OpenProcess``` and all the steps that are taken to transition from user mode to kernel mode. It makes sense that somewhere along that path of code execution, the callback registered via ```ObRegisterCallbacks``` will be called, and it makes sense that this gets called in the kernel. Let's take a look at the disassembly starting from the implementation of ```NtOpenProcess``` inside **ntoskrnl.exe**. Disassembly from IDA shows us the following code: ```nasm PAGE:0000000140583180 NtOpenProcess proc near ; CODE XREF: PfpSourceGetPrefetchSupport+A5↓p PAGE:0000000140583180 ; DATA XREF: .pdata:0000000140423B20↑o PAGE:0000000140583180 PAGE:0000000140583180 var_18 = byte ptr -18h PAGE:0000000140583180 var_10 = byte ptr -10h PAGE:0000000140583180 PAGE:0000000140583180 sub rsp, 38h PAGE:0000000140583184 mov rax, gs:188h PAGE:000000014058318D mov r10b, [rax+232h] PAGE:0000000140583194 mov [rsp+38h+var_10], r10b PAGE:0000000140583199 mov [rsp+38h+var_18], r10b PAGE:000000014058319E call PsOpenProcess PAGE:00000001405831A3 add rsp, 38h PAGE:00000001405831A7 retn PAGE:00000001405831A7 NtOpenProcess endp ``` Cool, we don't see anything regarding ```ObRegisterCallbacks```, but we do see a call to ```PsOpenProcess```. Unfortunately, this function is extremely involved, so it doesn't make sense to paste an entire snippet here. Instead, let's look at a portion that may be related to ```ObRegisterCallbacks```. ```nasm PAGE:00000001405836E0 PsOpenProcess proc near ; CODE XREF: NtAlpcOpenSenderProcess+194↑p PAGE:00000001405836E0 ; NtOpenProcess+1E↑p PAGE:00000001405836E0 ; DATA XREF: ... PAGE:0000000140583950 loc_140583950: ; CODE XREF: PsOpenProcess+411↓j PAGE:0000000140583950 lea rax, [rsp+2D8h+var_248] PAGE:0000000140583958 mov qword ptr [rsp+2D8h+var_2A8], rax ; int PAGE:000000014058395D mov [rsp+2D8h+var_2B0], dil ; char PAGE:0000000140583962 mov rax, cs:PsProcessType PAGE:0000000140583969 mov [rsp+2D8h+var_2B8], rax ; __int64 PAGE:000000014058396E xor r9d, r9d PAGE:0000000140583971 lea r8, [rsp+2D8h+var_1E8] PAGE:0000000140583979 mov edx, [rsp+2D8h+var_294] PAGE:000000014058397D mov rdi, [rsp+2D8h+BugCheckParameter2] PAGE:0000000140583985 mov rcx, rdi ; BugCheckParameter2 PAGE:0000000140583988 call ObOpenObjectByPointer PAGE:000000014058398D mov ebx, eax PAGE:000000014058398F lea rcx, [rsp+2D8h+var_1E8] PAGE:0000000140583997 call SepDeleteAccessState PAGE:000000014058399C lea rcx, [rsp+2D8h+var_1C8] PAGE:00000001405839A4 call SeReleaseSubjectContext PAGE:00000001405839A9 mov eax, [rdi+2E0h] PAGE:00000001405839AF mov [rsp+2D8h+var_288], eax PAGE:00000001405839B3 mov edx, 746C6644h PAGE:00000001405839B8 mov rcx, rdi ; BugCheckParameter2 PAGE:00000001405839BB call ObfDereferenceObjectWithTag PAGE:00000001405839C0 test ebx, ebx PAGE:00000001405839C2 js short loc_1405839D8 ``` We see a function call to ```ObOpenObjectByPointer```. Symbols in the kernel are generally prefixed with an identifier that describes what type of data it manipulates. As seen here ```ObOpenObjectByPointer``` and ```ObRegisterCallbacks``` are both prefixed with ```Ob```, which would be the **Object Manager**. ```nasm PAGE:00000001405831B0 ObOpenObjectByPointer proc near ; CODE XREF: PspCreateObjectHandle+38↑p PAGE:00000001405831B0 ; NtQueryInformationProcess+35F0↑p ... PAGE:000000014058323F loc_14058323F: ; CODE XREF: ObOpenObjectByPointer+16E↓j PAGE:000000014058323F mov [rsp+258h+var_208], r14 PAGE:0000000140583244 xor r8d, r8d PAGE:0000000140583247 mov [rsp+258h+var_210], r13 PAGE:000000014058324C mov r9, rbx PAGE:000000014058324F mov [rsp+258h+var_218], r13d PAGE:0000000140583254 mov rdx, rsi PAGE:0000000140583257 mov [rsp+258h+var_220], r13 PAGE:000000014058325C mov byte ptr [rsp+258h+var_228], r15b PAGE:0000000140583261 lea ecx, [r8+1] PAGE:0000000140583265 mov [rsp+258h+var_230], ebp PAGE:0000000140583269 mov dword ptr [rsp+258h+var_238], r13d PAGE:000000014058326E call ObpCreateHandle PAGE:0000000140583273 mov edi, eax PAGE:0000000140583275 test eax, eax PAGE:0000000140583277 js loc_14058334F ``` Inside ```ObOpenObjectByPointer```, we see another call to an **Object Manager** related function, ```ObpCreateHandle```. ```nasm PAGE:00000001404DBEB0 ObpCreateHandle proc near ; CODE XREF: ObInsertObjectEx+109↑p PAGE:00000001404DBEB0 ; ObInsertObjectEx+397↑p ... PAGE:00000001404DC7C1 loc_1404DC7C1: ; CODE XREF: ObpCreateHandle+324↑j PAGE:00000001404DC84A xor eax, eax PAGE:00000001404DC84C mov [rbp+110h+var_88], rax PAGE:00000001404DC853 mov [rbp+110h+Dst], 1 PAGE:00000001404DC85A mov [rbp+110h+var_90], rbx PAGE:00000001404DC861 mov dword ptr [rbp+110h+var_C8+4], edi PAGE:00000001404DC864 mov dword ptr [rbp+110h+var_C8], edi PAGE:00000001404DC867 call ObpCallPreOperationCallbacks PAGE:00000001404DC86C mov ebx, eax PAGE:00000001404DC86E test eax, eax PAGE:00000001404DC870 js loc_1406796F5 ``` And finally, we find what we're looking for. ```ObpCallPreOperationCallbacks``` is the function that ultimately calls the ```POB_PRE_OPERATION_CALLBACK PreOperation``` function defined in the ```_OB_OPERATION_REGISTRATION``` structure initially passed into ```ObRegisterCallbacks```. In summary, this function loops over all of the ```_OB_OPERATION_REGISTRATION``` structures that are registered, and then sequentially calls each **PreOperation** function defined by a kernel mode driver. ## Bypassing ObRegisterCallbacks Douggem's article details a method of bypassing these callbacks by using DKOM (Direct Kernel Object Manipulation) in order to locate the callbacks in kernel memory, and then remove them so that ```ObpCallPreOperationCallbacks``` has nothing to call. This method, though effective, has a few drawbacks. 1. Using undocumented structures could be dangerous due to Windows updates 2. Forcibly removing callbacks means that the driver that registered them can execute sanity checks 3. PatchGuard may someday decide to check for tampering of these kernel structures **libelevate** takes a different approach. Instead of directly touching callbacks to prevent stripping of access rights, why not elevate those rights after the fact that they have been stripped? ### Locating a HANDLE in the kernel An exported, but undocumented function ```ExEnumHandleTable``` does exactly what we want: execute a driver-defined function for every handle table entry in the handle table. Let's take a look at the disassembly: ```nasm PAGE:000000014050E4C0 ExEnumHandleTable proc near ; CODE XREF: IoRevokeHandlesForProcess+11E↑p PAGE:000000014050E4C0 ; NtQueryInformationProcess+29DE↑p ... PAGE:000000014050E5AF loc_14050E5AF: ; CODE XREF: ExEnumHandleTable+7A↑j PAGE:000000014050E5AF ; DATA XREF: .pdata:000000014041EA38↑o ... PAGE:000000014050E5AF add r9, 4 PAGE:000000014050E5B3 mov rcx, rbp PAGE:000000014050E5B6 mov rdx, r9 PAGE:000000014050E5B9 call ExpLookupHandleTableEntry PAGE:000000014050E5BE mov rdi, rax PAGE:000000014050E5C1 jmp loc_14050E540 ``` Sweet, ```ExpLookupHandleTableEntry``` looks like a function that does what we actually want. This undocumented function takes in a **pointer to the object table**, found in the ```EPROCESS``` structure for a process, and the ```HANDLE``` value to look for. What this returns is a pointer to a ```_HANDLE_TABLE_ENTRY``` structure, which looks like this: ```C typedef struct _HANDLE_TABLE_ENTRY { union { PVOID Object; ULONG ObAttributes; PHANDLE_TABLE_ENTRY_INFO InfoTable; ULONG Value; }; union { ULONG GrantedAccess; struct { WORD GrantedAccessIndex; WORD CreatorBackTraceIndex; }; LONG NextFreeTableEntry; }; } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; ``` ### Leveraging libcapcom Implementing the handle elevation technique is quite simple given all the information above. The flow of execution is the following: 1. Open a handle with your desired access rights using ```OpenProcess``` 2. Antivirus/anticheat driver's callback strips your handle access rights 3. In the context of the kernel: * Find the ```HANDLE_TABLE_ENTRY``` that maps to the initial handle from step 1. * Set the ```GrantedAccess``` member to your desired access rights 4. Do whatever you want in user mode with your newly elevated handle ### Caveats 1. I have basically copy-pasted decompiled code for ```ExpLookupHandleTableEntry``` to be called in the context of the kernel. I can either do that, or implement a signature scan for the function, and call it directly. Either way, it is still Windows update dependent. 2. Antivirus/anticheat drivers can still strip handles by **manually iterating the handle table** and stripping unauthorized handles. If I am able to elevate handles, then they can strip them after ```ObRegisterCallbacks``` too. ## How to use this library 1. Build the project 2. Link against **libelevate.lib** 3. Include **libelevate.h** 4. Call ```OpenProcess``` 5. Call ```grant_access(HANDLE, ACCESS_MASK)``` with the returned ```HANDLE``` and your desired access rights An example of the list above can be found in the **testlibelevate** project. ![alt text](https://puu.sh/BxY5s/b1b4c4d15b.png) ================================================ FILE: libelevate/kerneloffsets.cpp ================================================ #include "kerneloffsets.h" windows_version kernel_offsets::version; uint64_t kernel_offsets::name; uint64_t kernel_offsets::pid; uint64_t kernel_offsets::base; uint64_t kernel_offsets::link; uint64_t kernel_offsets::protection; uint64_t kernel_offsets::flags2; uint64_t kernel_offsets::objecttable; uint64_t kernel_offsets::vadroot; windows_version get_windows_version() { std::wstring wskernel32 = L"\\kernel32.dll"; wchar_t *path = NULL; void *ver = NULL, *block; windows_version version; UINT n; BOOL r; DWORD versz, blocksz; VS_FIXEDFILEINFO *vinfo; path = (wchar_t*)malloc(sizeof(*path) * MAX_PATH); if (!path) abort(); n = GetSystemDirectoryW(path, MAX_PATH); if (n >= MAX_PATH || n == 0 || n > MAX_PATH - wskernel32.length()) abort(); memcpy(path + n, wskernel32.c_str(), wskernel32.length() * sizeof(wchar_t) + 2); versz = GetFileVersionInfoSizeW(path, NULL); if (versz == 0) abort(); ver = malloc(versz); if (!ver) abort(); r = GetFileVersionInfoW(path, 0, versz, ver); if (!r) abort(); r = VerQueryValueA(ver, "\\", &block, (PUINT)&blocksz); if (!r || blocksz < sizeof(VS_FIXEDFILEINFO)) abort(); vinfo = (VS_FIXEDFILEINFO *)block; if ((int)HIWORD(vinfo->dwProductVersionMS) == 10) version = WINDOWS10; else if ((int)HIWORD(vinfo->dwProductVersionMS) == 6) { switch ((int)LOWORD(vinfo->dwProductVersionMS)) { case 0: version = UNSUPPORTED; break; case 1: version = WINDOWS7; break; case 2: version = WINDOWS8; break; case 3: version = WINDOWS81; break; default: version = UNSUPPORTED; } } else version = UNSUPPORTED; free(path); free(ver); return version; } ================================================ FILE: libelevate/kerneloffsets.h ================================================ #pragma once #include #include enum windows_version { WINDOWS7, WINDOWS8, WINDOWS81, WINDOWS10, UNSUPPORTED }; extern windows_version get_windows_version(); extern "C" NTSTATUS RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation); class kernel_offsets { public: static windows_version version; // Eprocess Offsets static uint64_t name; static uint64_t pid; static uint64_t base; static uint64_t link; static uint64_t protection; static uint64_t flags2; static uint64_t objecttable; static uint64_t vadroot; static void init() { windows_version win_ver = get_windows_version(); version = win_ver; switch (win_ver) { case WINDOWS7: init_win7(); break; case WINDOWS8: init_win8(); break; case WINDOWS81: init_win81(); break; case WINDOWS10: init_win10(); break; } } private: static void init_win7() { name = 0x2D8; pid = 0x180; base = 0x270; link = 0x188; protection = 0x43C; flags2 = 0; objecttable = 0x200; vadroot = 0x448; } static void init_win8() { name = 0x438; pid = 0x2E0; base = 0x3B0; link = 0x2E8; protection = 0x648; flags2 = 0; objecttable = 0x408; vadroot = 0x590; } static void init_win81() { name = 0x438; pid = 0x2E0; base = 0x3B0; link = 0x2E8; protection = 0x67A; flags2 = 0x2F8; objecttable = 0x408; vadroot = 0x5D8; } static void init_win10() { name = 0x450; pid = 0x2E0; base = 0x3C0; link = 0x2E8; protection = 0x6B2; flags2 = 0x300; objecttable = 0x418; vadroot = 0x610; RTL_OSVERSIONINFOW osVersion; RtlGetVersion(&osVersion); if (osVersion.dwBuildNumber == 10586) { protection = 0x6B2; flags2 = 0x300; objecttable = 0x418; vadroot = 0x610; } else if (osVersion.dwBuildNumber == 14393) { protection = 0x6C2; flags2 = 0x300; objecttable = 0x418; vadroot = 0x620; } else if (osVersion.dwBuildNumber == 15063) { protection = 0x6CA; flags2 = 0x300; objecttable = 0x418; vadroot = 0x628; } else if (osVersion.dwBuildNumber == 16299) { protection = 0x6CA; flags2 = 0x828; objecttable = 0x418; vadroot = 0x628; } else if (osVersion.dwBuildNumber == 17134) { protection = 0x6CA; flags2 = 0x828; objecttable = 0x418; vadroot = 0x628; } } }; ================================================ FILE: libelevate/libelevate.cpp ================================================ #include "structs.h" #include "kerneloffsets.h" #include "libcapcom.h" using namespace native::structs; PHANDLE_TABLE_ENTRY ExpLookupHandleTableEntry(PHANDLE_TABLE pHandleTable, HANDLE handle) { unsigned __int64 v2; // rdx __int64 v3; // r8 signed __int64 v4; // rax __int64 v5; // rax v2 = (__int64)handle & 0xFFFFFFFFFFFFFFFCui64; if (v2 >= *(DWORD*)pHandleTable) return 0i64; v3 = *((uintptr_t*)pHandleTable + 1); v4 = *((uintptr_t *)pHandleTable + 1) & 3i64; if ((uint32_t)v4 == 1) { v5 = *(uintptr_t*)(v3 + 8 * (v2 >> 10) - 1); return (PHANDLE_TABLE_ENTRY)(v5 + 4 * (v2 & 0x3FF)); } if ((uint32_t)v4) { v5 = *(uintptr_t*)(*(uintptr_t *)(v3 + 8 * (v2 >> 19) - 2) + 8 * ((v2 >> 10) & 0x1FF)); return (PHANDLE_TABLE_ENTRY)(v5 + 4 * (v2 & 0x3FF)); } return (PHANDLE_TABLE_ENTRY)(v3 + 4 * v2); } bool grant_access(HANDLE handle, ACCESS_MASK access) { if (!init_exploit()) return false; kernel_offsets::init(); execute_in_kernel([&handle, &access](MmGetSystemRoutineAddress_t _MmGetSystemRoutineAddress) { UNICODE_STRING DbgPrintExName = { 0 }; RtlInitUnicodeString(&DbgPrintExName, L"DbgPrintEx"); UNICODE_STRING PsGetCurrentProcessName = { 0 }; RtlInitUnicodeString(&PsGetCurrentProcessName, L"PsGetCurrentProcess"); DbgPrintEx_t _DbgPrintEx = (DbgPrintEx_t)_MmGetSystemRoutineAddress(&DbgPrintExName); PsGetCurrentProcess_t _PsGetCurrentProcess = (PsGetCurrentProcess_t)_MmGetSystemRoutineAddress(&PsGetCurrentProcessName); void* pEProcess = _PsGetCurrentProcess(); PHANDLE_TABLE pHandleTable = *(PHANDLE_TABLE*)((unsigned char*)pEProcess + kernel_offsets::objecttable); PHANDLE_TABLE_ENTRY pEntry = ExpLookupHandleTableEntry(pHandleTable, handle); ACCESS_MASK oldAccess = pEntry->GrantedAccess; pEntry->GrantedAccess = access; _DbgPrintEx(77, 0, "Old: 0x%llx -> New: 0x%llx", oldAccess, pEntry->GrantedAccess); }); return cleanup_exploit(); } ================================================ FILE: libelevate/libelevate.h ================================================ #pragma once #include /* Grants the given `handle` the access rights defined by `access` @param handle a handle that is created by a call to OpenProcess or OpenThread @param access a 32-bit mask that defines the access rights that a handle grants. More information can be found on MSDN: https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-security-and-access-rights @return true if the exploit was loaded, executed, and unloaded properly */ extern bool grant_access(HANDLE handle, ACCESS_MASK access); ================================================ FILE: libelevate/libelevate.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 15.0 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E} libelevate 10.0.17134.0 Application true v141 MultiByte Application false v141 true MultiByte Application true v141 MultiByte StaticLibrary false v141 true MultiByte $(SolutionDir)libcapcom\libcapcom;$(IncludePath) $(SolutionDir)x64\Release;$(LibraryPath) Level3 Disabled true true Level3 Disabled true true Level3 MaxSpeed true true true true true true Level3 MaxSpeed true true true true stdcpplatest MultiThreaded true true libcapcom.lib ================================================ FILE: libelevate/libelevate.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Header Files Header Files Header Files ================================================ FILE: libelevate/structs.h ================================================ #pragma once #define WIN32_NO_STATUS #include #include #undef WIN32_NO_STATUS #include namespace native::structs { typedef struct _HANDLE_TABLE_ENTRY { union { PVOID Object; ULONG ObAttributes; }; union { union { ACCESS_MASK GrantedAccess; struct { USHORT GrantedAccessIndex; USHORT CreatorBackTraceIndex; }; }; LONG NextFreeTableEntry; }; } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; typedef struct _HANDLE_TABLE { char padding[100]; // we don't care about this actual structure } HANDLE_TABLE, *PHANDLE_TABLE; typedef BOOLEAN(*EX_ENUMERATE_HANDLE_ROUTINE)(IN PHANDLE_TABLE_ENTRY, IN HANDLE, IN PVOID); typedef CLIENT_ID* PCLIENT_ID; typedef ULONG(*DbgPrintEx_t)(_In_ ULONG, _In_ ULONG, _In_ PCSTR, ...); typedef PVOID(*PsGetCurrentProcess_t)(); typedef VOID(*RtlInitUnicodeString_t)(PUNICODE_STRING, PCWSTR); typedef PVOID(NTAPI* MmGetSystemRoutineAddress_t)(PUNICODE_STRING); } ================================================ FILE: libelevate.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28010.2016 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libelevate", "libelevate\libelevate.vcxproj", "{5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}" ProjectSection(ProjectDependencies) = postProject {AE5BAFC9-E4BA-4786-96E4-A564B113B206} = {AE5BAFC9-E4BA-4786-96E4-A564B113B206} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcapcom", "..\libcapcom\libcapcom\libcapcom.vcxproj", "{AE5BAFC9-E4BA-4786-96E4-A564B113B206}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testlibelevate", "testlibelevate\testlibelevate.vcxproj", "{D5D362C4-C5E5-4567-91C1-9563A83423E4}" ProjectSection(ProjectDependencies) = postProject {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E} = {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E} EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Debug|x64.ActiveCfg = Debug|x64 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Debug|x64.Build.0 = Debug|x64 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Debug|x86.ActiveCfg = Debug|Win32 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Debug|x86.Build.0 = Debug|Win32 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Release|x64.ActiveCfg = Release|x64 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Release|x64.Build.0 = Release|x64 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Release|x86.ActiveCfg = Release|Win32 {5BBDF4C8-27BC-488C-8D19-B3C345CA1F0E}.Release|x86.Build.0 = Release|Win32 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x64.ActiveCfg = Debug|x64 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x64.Build.0 = Debug|x64 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x86.ActiveCfg = Debug|Win32 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x86.Build.0 = Debug|Win32 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x64.ActiveCfg = Release|x64 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x64.Build.0 = Release|x64 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x86.ActiveCfg = Release|Win32 {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x86.Build.0 = Release|Win32 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Debug|x64.ActiveCfg = Debug|x64 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Debug|x64.Build.0 = Debug|x64 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Debug|x86.ActiveCfg = Debug|Win32 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Debug|x86.Build.0 = Debug|Win32 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Release|x64.ActiveCfg = Release|x64 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Release|x64.Build.0 = Release|x64 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Release|x86.ActiveCfg = Release|Win32 {D5D362C4-C5E5-4567-91C1-9563A83423E4}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A8666E26-937D-424D-B5B3-63D9FCD96291} EndGlobalSection EndGlobal ================================================ FILE: testlibelevate/main.cpp ================================================ #include #include #include #include #include #include "libelevate.h" std::map GetProcessList() { std::map processList; PROCESSENTRY32 pe32; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE || hSnapshot == 0) return processList; pe32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hSnapshot, &pe32)) { CloseHandle(hSnapshot); return processList; } do { processList[pe32.szExeFile] = pe32.th32ProcessID; } while (Process32Next(hSnapshot, &pe32)); CloseHandle(hSnapshot); return processList; } int main() { DWORD notepadPID = GetProcessList()["notepad.exe"]; HANDLE notepadHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, notepadPID); printf("Elevating handle 0x%llx for PID %i from %s to %s\n", notepadHandle, notepadPID, "PROCESS_QUERY_LIMITED INFORMATION", "PROCESS_ALL_ACCESS"); grant_access(notepadHandle, PROCESS_ALL_ACCESS); system("pause"); return 0; } ================================================ FILE: testlibelevate/testlibelevate.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 15.0 {D5D362C4-C5E5-4567-91C1-9563A83423E4} testlibelevate 10.0.17134.0 Application true v141 MultiByte Application false v141 true MultiByte Application true v141 MultiByte Application false v141 true MultiByte $(SolutionDir)libelevate\;$(IncludePath) $(SolutionDir)x64\Release;$(LibraryPath) Level3 MaxSpeed true true true true MultiThreaded true true libelevate.lib;Version.lib;ntdll.lib;%(AdditionalDependencies) RequireAdministrator Level3 Disabled true true Level3 Disabled true true Level3 MaxSpeed true true true true true true ================================================ FILE: testlibelevate/testlibelevate.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files