Join our Newsletter — 33% off our NHI Course
Home› Guides› M1039 Environment Variable Permissions: Locking Down Variables, History…
Guide SOC & Detection Engineering

M1039 Environment Variable Permissions: Locking Down Variables, History and Secrets

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 12 November 2026 12 min read
On this page

Environment variables decide where a shell writes its history, which libraries a program loads and, in most pipelines, which credentials a process holds, yet they are usually writable by anyone who controls the session. M1039 Environment Variable Permissions is the MITRE ATT&CK mitigation that restricts who can change them. Its formal mapping in ATT&CK v20 is narrow, two techniques about command history, but the control it describes reaches further: configuration file permissions, CI/CD variable access and process scoping. This guide covers what ATT&CK maps, how to implement each measure, the closely related techniques ATT&CK assigns to other mitigations, and how the same discipline protects secrets held in environment variables. It is for Linux, platform and DevSecOps engineers and for detection teams. The basics of handling .env files are in the linked guide and are not repeated here.

Key takeaways

  • The mapping is small but specific. ATT&CK v20 maps M1039 to T1690 Prevent Command History Logging and T1070.003 Clear Command History, both about protecting HISTFILE, HISTCONTROL and HISTFILESIZE.
  • The measures are broader than the mapping. ATT&CK's description covers file permissions on shell and systemd configuration, RBAC on CI/CD variables, process scoping in containers and auditing changes.
  • Hijacking variables sit next door. LD_PRELOAD (T1574.006), PATH (T1574.007) and COR_PROFILER (T1574.012) abuse is mapped by ATT&CK to other mitigations, but locking down the same configuration files helps against them.
  • Secrets in variables are an identity problem. Every process in a job or container inherits the environment, so a token in an environment variable is exposed to every script that runs there.
  • Read-only history is a speed bump. It raises the cost of hiding commands, but forwarding command logs off the host is what makes them reliable evidence.

What M1039 covers

ATT&CK describes M1039 as restricting the modification of environment variables to authorised users and processes through strict permissions and policies (attack.mitre.org, mitigation version 1.1, last modified 11 December 2024). Its four measures, with ATT&CK's own examples:

Measure in ATT&CK textATT&CK use case and exampleWhere it applies
Restrict write accessFile permissions on .bashrc, .bash_profile, .zshrc and systemd service files; only root or administrators may modify /etc/environment or /etc/profileLinux and macOS hosts
Secure access controlsRBAC in CI/CD tools such as Jenkins or GitLab so only specific users can modify variablesPipelines and deployment tools
Restrict process scopeUse containers to isolate variables to specific containers so other processes do not inherit themContainers and orchestration
Audit changesUse auditd to monitor changes to /etc/environment and application environment filesLinux hosts

Techniques M1039 mitigates

TacticTechniqueHow M1039 helps (per ATT&CK usage text)
Defense ImpairmentT1690 Prevent Command History LoggingPrevent users from changing the HISTCONTROL, HISTFILE and HISTFILESIZE environment variables
StealthT1070.003 Clear Command HistoryMaking the history-related variables read-only may ensure history is preserved

Both techniques rely on Bash behaviour documented in the GNU Bash manual: if HISTFILE is unset or null, the shell does not save history on exit; HISTSIZE set to 0 stops commands being saved to the history list; HISTFILESIZE set to 0 truncates the history file; and HISTCONTROL values such as ignorespace skip lines that begin with a space. An attacker who can change any of these hides commands without deleting a file. See the T1070 Indicator Removal Defensive Guide.

Related techniques mapped to other mitigations

ATT&CK v20 does not map these to M1039, but they abuse environment variables, and the file permissions M1039 describes reduce the places an attacker can set them. The mitigations listed are ATT&CK's own.

TechniqueVariable abusedATT&CK mitigations
T1574.006 Dynamic Linker HijackingLD_PRELOAD and similar loader variablesM1028 Operating System Configuration, M1038 Execution Prevention
T1574.007 Path Interception by PATH Environment VariablePATHM1022 Restrict File and Directory Permissions, M1038, M1047 Audit
T1574.012 COR_PROFILERCOR_PROFILER and related .NET profiler variablesM1018 User Account Management, M1024 Restrict Registry Permissions, M1038
T1546.004 Unix Shell Configuration ModificationShell startup files that set variables and run commandsM1022 Restrict File and Directory Permissions

See the T1574 Hijack Execution Flow Defensive Guide and T1546 Event Triggered Execution Defensive Guide.

Implementing M1039 on hosts

  • System-wide files: /etc/environment, /etc/profile, /etc/profile.d/*, /etc/bash.bashrc and systemd unit files (including Environment= and EnvironmentFile= targets) should be owned by root and not writable by group or others. Check them in configuration management, not by hand.
  • History variables: NHI Mgmt Group recommends setting HISTFILE, HISTSIZE, HISTFILESIZE and HISTCONTROL in a root-owned file under /etc/profile.d and marking them with the Bash readonly builtin, so that an interactive login session cannot reassign or unset them. This is the measure ATT&CK's usage text describes.
  • User startup files: On shared jump hosts and bastions, make users' .bashrc and .bash_profile root-owned or managed, because a user-writable startup file can undo system settings before the prompt appears.
  • Service accounts: Service accounts that need no shell should have a non-login shell; those that do should not be able to write their own startup files.
  • Windows: System environment variables live in the registry under HKLM and require administrative rights to change; per-user variables under HKCU do not. Restrict and monitor the HKLM location, which links M1039 to M1024 Restrict Registry Permissions.

Environment variables, pipelines and secrets

For non-human identities the bigger risk is not that a variable is changed but that it is read. A process's environment is inherited by every child process, and on Linux the initial environment of a running program is exposed at /proc/<pid>/environ, with access governed by a ptrace read-access check. The M1039 measures still apply, extended by NHI Mgmt Group recommendations:

  • Secure access controls in CI/CD: GitLab documents that project variables need the Maintainer role, group variables the Owner role, and instance variables administrator access. Protected variables reach only pipelines on protected branches or tags. Masked variables are replaced with [MASKED] in job logs, but GitLab states masking does not stop a malicious job reading the value, and that anyone who can push changes to .gitlab-ci.yml could expose variables. Treat the right to edit pipeline definitions as access to every variable the pipeline can reach.
  • Restrict process scope: Kubernetes guidance says that when only one container in a pod needs a Secret, the volume mount or environment variable configuration should be defined so other containers do not get it, and that applications must avoid logging secret data after reading it. Prefer mounted files or file-type variables (GitLab stores the value in a temporary file and puts the path in the variable) for high-value credentials, and give each step only the variables it needs.
  • Short-lived credentials: A workload identity token that expires in minutes is far less valuable in a leaked environment than a long-lived API key.

The Codecov breach shows why scope matters: a modified uploader script, run inside customers' CI jobs, captured the full set of environment variables, including tokens and cloud keys, and sent them to an attacker-controlled server. Every secret in the job's environment was exposed to a third-party script that needed none of them. The 230 million AWS cloud environments campaign shows the file side of the same problem: .env files served by misconfigured web applications yielded more than 90,000 unique variables, including AWS access keys and GitHub tokens. See the Environment Variables and .env Files guide and How to find secrets in Lambda environment variables.

Choosing the approach

NeedApproachTrade-off
Preserve shell history on serversRead-only history variables plus off-host command loggingRead-only alone is bypassed by other shells or interpreters
Stop tampering with system-wide variablesRoot-owned configuration files, managed by configuration management, with file integrity monitoringChanges need a deployment rather than a quick edit
Protect pipeline secretsProtected, masked or hidden variables; minimum roles to edit pipelines; per-step scopingMasking hides logs, not the value from the job
Protect container secretsMounted secret files per container, external secret store, short-lived tokensApplications may need changes to read files

Small teams get most value from two moves: root-owned system configuration and restricting who can edit pipeline definitions. Larger estates should add file integrity monitoring on the configuration files and a secrets manager so long-lived credentials leave environment variables altogether.

Implementation approach

  1. Inventory. List system-wide environment files, systemd units with environment settings, and every CI/CD variable with its scope, protection flag and owner.
  2. Fix permissions. Apply root ownership and remove group and world write on system files through configuration management, starting with bastions and production servers.
  3. Pilot read-only history. Deploy the /etc/profile.d settings to a pilot group of servers and confirm administrators' tooling still works.
  4. Tighten pipeline access. Move secrets to protected variables, require merge request review for pipeline file changes, and remove secrets that no job uses.
  5. Roll back safely. Keep configuration changes in version control so a breaking change is reverted by redeploying the previous version.

What M1039 does not stop

  • Other interpreters and shells: Read-only Bash variables do nothing for commands run through Python, a different shell or a non-interactive session that does not read the profile.
  • Root: An attacker with root can edit the files, kill the shell before history is flushed, or delete the history file (T1070.003). Off-host logging (M1029 Remote Data Storage) is the counter.
  • Reading secrets: Permission on who can change a variable does not limit who can read it; process scoping and secret stores do that.
  • Malicious code inside the job: As the Codecov case shows, code running in the pipeline sees the pipeline's environment regardless of RBAC on the settings page.

Common failure patterns

  • History treated as evidence. Investigations rely on ~/.bash_history, which the attacker controls, instead of logs forwarded off the host.
  • World-writable profile scripts. A script under /etc/profile.d deployed with loose permissions lets any user set variables for every login.
  • Global CI variables. Cloud keys defined at group or instance level reach every project and every job, including those that never need them.
  • Masking mistaken for protection. Teams assume a masked variable is safe from scripts in the job.
  • Secrets in images and manifests. Values baked into container images or committed in manifests, readable by anyone with repository or registry access.

Detection and measurement

  • Audit file changes: watch /etc/environment, /etc/profile, /etc/profile.d and systemd unit directories with auditd. The auditctl manual notes that the -w watch form is deprecated for performance and gives the syscall form, for example -a always,exit -F arch=b64 -F path=/etc/environment -F perm=wa, with a key for ausearch.
  • Detect history tampering: alert on commands that unset or reassign HISTFILE, set HISTSIZE or HISTFILESIZE to 0, or truncate history files.
  • Pipeline audit: log changes to CI/CD variables and to pipeline definition files, and alert on new unprotected variables holding credentials.
MetricWhat good looks like
System environment files with correct ownership and mode100% of managed Linux hosts
Servers forwarding command logs off-hostAll production and bastion hosts
CI/CD credentials stored as protected variablesAll production credentials
Long-lived secrets in environment variablesFalling quarter on quarter as workloads move to short-lived tokens
History tampering alerts reviewed within SLAAll

The MITRE D3FEND knowledge graph relates M1039 to D3-ACH Application Configuration Hardening (modifying an application's configuration to reduce its attack surface) and D3-SFA System File Analysis (monitoring system files such as configuration files for modification or tampering). These match the restrict-write and audit measures above.

Related mitigationRelationship to M1039
M1022 Restrict File and Directory PermissionsProtects the files that set variables
M1028 Operating System ConfigurationAlso mapped to T1690; system-level history and loader settings
M1029 Remote Data StorageKeeps logs and history off the host, mapped to T1070.003
M1038 Execution PreventionBlocks payloads loaded through PATH or LD_PRELOAD abuse

See M1022 Restrict File and Directory Permissions, M1029 Remote Data Storage and M1038 Execution Prevention.

Framework mapping

FrameworkReferenceRelevance
NIST SP 800-53 Rev. 5CM-6 Configuration Settings; AC-6 Least PrivilegeLocked configuration and minimal rights to change it
NIST SP 800-53 Rev. 5AU-9 Protection of Audit Information; SI-7 Software, Firmware, and Information IntegrityProtecting history and detecting configuration tampering
NIST CSF 2.0PR.PS-01; PR.AA-05Configuration management; least-privilege access
CIS Controls v8.14.1 Secure Configuration Process; 8.9 Centralize Audit LogsManaged configuration and off-host logs
ISO/IEC 27001:2022 Annex A8.9 Configuration management; 8.15 LoggingConfiguration control and log protection

Practitioner checklist

  • Make /etc/environment, /etc/profile, /etc/profile.d and systemd environment files root-owned and not group or world writable.
  • Set history variables read-only on servers and bastions through a managed /etc/profile.d file.
  • Forward command and audit logs off the host so history is not the only record.
  • Audit changes to environment configuration files with syscall-form auditd rules.
  • Restrict who can edit CI/CD variables and pipeline definitions; review pipeline changes before they run.
  • Scope secrets to the jobs, steps and containers that need them; prefer mounted files for high-value credentials.
  • Replace long-lived secrets in environment variables with short-lived workload credentials.
  • Alert on HISTFILE, HISTSIZE or HISTFILESIZE tampering.

Standards and references

Related NHI Mgmt Group resources: Environment Variables and .env Files · How to find secrets in Lambda environment variables · T1070 Indicator Removal Defensive Guide · M1037 Filter Network Traffic

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 12 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org