When Your PowerShell Profile Is the Backdoor
Attackers plant download cradles, in-memory loaders, and LOLBins in profile.ps1 so they re-run on every shell start. Here is what WinSentinel now flags.
A PowerShell profile is just a script — profile.ps1 or
Microsoft.PowerShell_profile.ps1 — that PowerShell runs
automatically every time a shell of the matching host starts. It is meant for
conveniences: aliases, a custom prompt, a couple of module imports. That
auto-run behavior is exactly why attackers love it. Drop one line in the right
profile and your payload re-arms on every new session, survives reboots,
and needs no scheduled task, service, or registry Run key to hunt for. It is
MITRE ATT&CK T1546.013 — Event Triggered Execution: PowerShell
Profile, and it is quietly one of the most durable footholds on a
Windows box.
Why the profile is such a good hiding spot
There are up to six profile paths per host (per-user vs. all-users, current-host
vs. all-hosts), and almost nobody audits them. A benign profile and a malicious one
live in the same file, so a defender scrolling past Set-Alias lines can
miss a download cradle three lines down. The machine-wide profiles under
$PSHOME are worse: they run for every user on the box, so a
single write there is a shared backdoor.
What actually shows up in a weaponized profile
The tell is rarely subtle once you know the vocabulary. Malicious profiles cluster around a handful of behaviors:
- Download cradles —
Net.WebClient.DownloadString,iwr/irmagainst a URL,Start-BitsTransfer, or thecertutil -urlcacheLOLBin fetch, usually piped straight intoiex. - In-memory loaders —
Add-Type -MemberDefinitionor-TypeDefinitionto compile native/C# on the fly,[Reflection.Assembly]::Load($bytes)and[AppDomain]::CurrentDomain.Load(...)to run a payload that never touches disk. - LOLBins —
InstallUtil,RegAsm,RegSvcs,mshta,regsvr32 /i:http,rundll32 javascript:: signed Microsoft binaries that execute attacker code. - COM shell execution —
New-Object -ComObject WScript.Shellthen.ShellExecute(...). - Defense evasion — AMSI reflection bypasses
(
[Ref].Assembly.GetType('...AmsiUtils')),Set-MpPreference -DisableRealtimeMonitoring,-ExecutionPolicy Bypass,-NoProfile/-w hiddenrelaunch wrappers. - Privilege & credential moves —
Start-Process -Verb RunAsself-elevation, or an embeddedConvertTo-SecureString 'password' -AsPlainText -Force.
None of these belong in a profile whose job is to set your prompt.
What WinSentinel flags
WinSentinel’s PowerShell Security module reads every profile path, then scans the
content for those patterns with a case-insensitive substring match tuned to avoid
false positives (the URL-gated iwr http/irm http tokens, for
example, won’t trip on the word “confirm”). A per-user profile with a
suspicious pattern is a Warning; the same pattern in a machine-wide
profile is Critical, because it executes for everyone. A machine-wide
profile that looks clean is still surfaced as Info — it is a high-value persistence
target worth confirming.
winsentinel --audit --category PowerShell
Every finding comes with the offending path and the exact reason it matched, so you can open the file, decide whether you put that line there, and remove it if you didn’t.
Cleaning up
If a finding is unexpected, treat it as a live incident, not a config nit: inspect the
script, remove or restore it, and rotate any credentials that shell may have handled —
a profile that ran a download cradle has been running attacker code in your session for as
long as the file has been there. Then lock the surface down: prefer signed profiles,
restrict write access to the machine-wide paths under $PSHOME, and turn on
Script Block Logging so the next tamper is captured even if the profile scan is bypassed.
The whole point of the free single-machine tool is that this check runs locally, on demand or on a schedule, with no cloud and no account. Fleet-wide rollups of these findings across many machines are a Pro feature — but the detection itself is free, forever.