Safeguard
Application Security•

Detecting and Mitigating Shell Configuration Persistence (T1546.004)

Lines added to .bashrc, .profile or .zshrc run at every login or new terminal. See how it works on Linux and macOS and how to audit your own files.

Safeguard Security Team
4 min read

Unix Shell Configuration Modification (MITRE ATT&CK T1546.004) is a persistence and privilege escalation technique on Linux and macOS. Shells read configuration scripts at set points in a session. If an adversary adds a command to one of them, the command runs whenever that point is reached.

Anyone who has ever added an alias to a dotfile already knows how this works. The same convenience makes it a favorite hiding place.

How attackers use it

A login shell starts when a user opens a command line interface or logs in remotely, for example over SSH. It runs scripts from the system (/etc) and from the user's home directory (~/) to set up the environment. Those scripts usually set environment variables, create aliases and customize the session. They run at the permission level of their directory. When the shell exits, further scripts run to wrap up.

On Linux with bash, MITRE gives these examples:

  • /etc/profile and files in /etc/profile.d run each time any shell on the system launches. They typically need root to change.
  • ~/.bash_profile, ~/.bash_login and ~/.profile are sourced when a user opens a command line or connects remotely. The system runs only the first one that exists, in that order, so adversaries have used ~/.bash_profile to be sure theirs runs.
  • ~/.bashrc also runs if the connection is remote or another interactive shell starts, such as a new terminal tab.
  • ~/.bash_logout runs when a session ends. Some malware uses it to trigger on termination.

On macOS the idea is the same, but the default shell since macOS 10.15 is zsh. Terminal.app starts a zsh login shell and a zsh interactive shell. The login shell uses /etc/profile, /etc/zshenv, /etc/zprofile and /etc/zlogin, then ~/.zprofile and ~/.zlogin. The interactive shell uses ~/.zshrc. On exit, /etc/zlogout and ~/.zlogout run. For legacy programs, macOS runs /etc/bashrc on startup.

System files give a persistence that hits every user. User files give persistence at user level only, with no root needed.

How to detect it

MITRE's strategy has a Linux analytic and a macOS analytic:

  • Linux. Modifications to shell startup and logout scripts such as ~/.bashrc, ~/.bash_profile or /etc/profile, followed by odd process execution or network connections when a shell starts interactively or remotely.
  • macOS. Changes to zsh files such as ~/.zshrc, ~/.zlogin and /etc/zprofile, followed by unauthorized binaries or unexpected network traffic when Terminal.app opens.

An auditd watch on the files catches the write, in a rules file:

-w /etc/profile -p wa -k shell_profile
-w /etc/profile.d/ -p wa -k shell_profile
-w /root/.bashrc -p wa -k shell_profile

Watching home directories for every user is noisier, so many teams cover privileged accounts and service accounts first.

How to reduce the risk

MITRE lists one mitigation. Make these files immutable and changeable only by certain administrators. That limits how easily an adversary can set up user level persistence.

On shared servers, you can also keep configuration under central management, so the correct contents of a profile are known and drift is easy to spot.

Check your own environment

Read-only commands, Linux and macOS:

ls -la /etc/profile /etc/profile.d/ ~/.bash_profile ~/.bash_login ~/.profile ~/.bashrc ~/.bash_logout ~/.zprofile ~/.zshrc ~/.zlogin ~/.zlogout 2>/dev/null
grep -nE 'curl|wget|nc |base64|/tmp/|/dev/shm' /etc/profile /etc/profile.d/* ~/.bash_profile ~/.bashrc ~/.profile ~/.zshrc 2>/dev/null

The first shows which files exist and when they changed. The second searches for patterns that rarely belong in a shell startup file, such as downloaders and encoded strings. Neither command changes anything.

Then a short list:

  1. Do any startup files have modification times that do not match your last change window?
  2. Does any line start a program in the background?
  3. Does any line reach out to the network?
  4. For macOS, have you looked at zsh files as well as bash files?

Quick check

Why do adversaries pick ~/.bash_profile on Linux?

The system executes only the first existing file among .bash_profile, .bash_login and .profile, so using .bash_profile makes sure the commands run.

Which shell does macOS use by default on 10.15 and later?

zsh, so its files such as .zprofile, .zshrc and .zlogin are the ones to watch.

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.