Safeguard
Application Security•

Detecting and Mitigating PowerShell Profile Abuse (T1546.013)

A PowerShell profile script runs whenever PowerShell starts. Learn how it becomes persistence, which files to watch and how to reduce exposure.

Safeguard Security Team
2 min read

PowerShell Profile abuse (MITRE ATT&CK T1546.013) is a Windows technique for persistence and privilege escalation. A PowerShell profile, profile.ps1, is a script that runs when PowerShell starts. It is meant as a logon script to customize the user environment.

How attackers use it

PowerShell supports several profiles, depending on the user and the host program. The PowerShell console, the ISE and Visual Studio Code can each have their own, and an administrator can set up a profile that applies to all users and hosts on the computer.

MITRE explains that an adversary can edit these profiles to add commands, functions, modules and drives. The modified script then runs each time a user opens a PowerShell session, unless PowerShell is launched with the -NoProfile flag.

Privilege escalation is possible when a higher privileged account, such as a domain administrator, loads and runs a profile that an attacker has written to.

How to detect it

MITRE's strategy links file events to process starts:

  • Creation or modification of files in known profile locations.
  • PowerShell launches afterward that do not use -NoProfile.
  • Profile scripts that load unusual modules or launch external programs, especially in elevated contexts.

Script block logging in PowerShell can help show what a profile did, though MITRE's strategy is file and process centered.

How to reduce the risk

  1. Sign and enforce. Allow only signed PowerShell scripts, and sign profiles so changes break the signature.
  2. Lock the files. Make profiles immutable and changeable only by certain administrators.
  3. Avoid profiles where not needed. Run remote and automated scripts with the NoProfile flag so local profiles do not execute.

Check your own environment

In PowerShell, read-only:

$PROFILE | Select-Object *
Test-Path $PROFILE.AllUsersAllHosts
Get-Item $PROFILE.AllUsersAllHosts, $PROFILE.CurrentUserAllHosts -ErrorAction SilentlyContinue | Select-Object FullName, LastWriteTime

The first shows the profile paths for the current host. The others show whether the broadest profiles exist and when they last changed. A profile that nobody on the team created is the finding.

Quick check

What flag stops a profile from running?

NoProfile. Using it for automated and remote script runs prevents local profiles from executing.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.