PowerShell Execution Policy Bypass
The PowerShell execution policy is a safety feature, not a security boundary — Microsoft says so explicitly in about_Execution_Policies. It gates whether configuration files (profiles) load and whether scripts must be digitally signed, but it does nothing to stop a user from running arbitrary PowerShell code: any command that works interactively works regardless of policy. That design intent is exactly why bypassing it is trivial and why defenders should never treat Restricted/AllSigned as a preventive control. Enforcement of the policies only occurs on Windows; on non-Windows PowerShell 6+ the policy is Unrestricted and can’t be changed.
Policy levels and scopes
Per about_Execution_Policies, the levels are Restricted (default on Windows clients; no scripts), AllSigned (scripts must be signed by a trusted publisher), RemoteSigned (default on Windows Server; local scripts run, downloaded scripts must be signed), Unrestricted, and Bypass (nothing blocked, no warnings). Scopes determine where the setting lives:
MachinePolicy/UserPolicy— set via Group Policy (win over all others)LocalMachine— registryHKLM, default forSet-ExecutionPolicy, needs adminCurrentUser— registryHKCUProcess— held only in$Env:PSExecutionPolicyPreference, dies with the session
Check effective policy and per-scope values:
Get-ExecutionPolicy
Get-ExecutionPolicy -ListBypass techniques
One-shot flag — -ExecutionPolicy Bypass (abbreviated -ex bypass or -ep bypass) applies only to the launched process:
powershell -ex bypass -File .\script.ps1Note the flag is not needed when executing code directly with -Command (-c) — the policy only governs script files, not interactive commands. That asymmetry is the entire reason bypass is always available.
Process-scope switch — same effect from inside a running shell, no admin required:
Set-ExecutionPolicy Bypass -Scope Process -ForceMachine-scope change (requires admin, persistent):
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachineChild-shell inheritance — set the environment variable the Process scope reads so any subsequently spawned PowerShell inherits the bypass:
$env:PSExecutionPolicyPreference = "bypass"Older NetSPI write-ups catalog a dozen more variants (download-and-IEX cradles, reading the script into a variable and Invoke-Expression-ing it, swapping powershell.exe for a renamed copy, etc.) — MITRE ATT&CK cites that list directly under mitigation M1026 with the caveat that policy restriction “can be bypassed depending on environment configuration.”
What bypassing does not defeat
Execution policy bypass changes nothing about the controls that actually matter:
- AMSI still scans script content at execution time, including in-memory
IEXcradles — see powershell-constrained-language-mode and invoke-webrequest-download-cradles for how that intersects with download-and-execute patterns. - Constrained Language Mode still blocks dangerous .NET types and COM objects when AppLocker/WDAC script enforcement is active.
- AppLocker/WDAC binary rules still block unsigned or disallowed scripts outright.
Microsoft’s actual preventive recommendations (MITRE M1045/M1038) are code-signing enforcement and application control (Constrained Language via WDAC/AppLocker), with JEA to sandbox remote admin sessions — none of which an -ex bypass flag touches.
Detection
Execution-policy tampering shows up in process command lines and PowerShell logging:
- Sysmon EID 1 / Security EID 4688:
powershell.exewith-ep bypass,-executionpolicy bypass,-enc,-nop,-w hidden - PowerShell Script Block Logging (EID 4104) catches
Set-ExecutionPolicyinvocations and the$env:PSExecutionPolicyPreferenceassignment - Registry auditing on
HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell(ExecutionPolicyvalue) for persistent changes
Related
- powershell-constrained-language-mode — the control that does restrict language capability, and its own bypass trade-offs
- invoke-webrequest-download-cradles — download-to-disk vs. download-to-memory and where AMSI/Defender intervene
- windows-scripting-host — sibling script interpreter (T1059.005) often used when PowerShell itself is constrained
- powershell-base64-encoding — the
-encflag that rides beside-ep bypassin detection rules - powershell-unicode-quote-equivalence — parser-level quote tricks that defeat naive sanitizers
- powershell-modules — module loading is the legitimate counterpart to the IEX cradles this page warns about
- shortcut-modification —
powershell.exe -WindowStyle Hiddenis the standard launcher for hijacked shortcuts