Lambda Command Injection via os.spawn*()

CRITICAL

Lambda event data flows to os.spawn*() functions, enabling process execution with attacker-controlled arguments in the Lambda execution environment.

Rule Information

Language
Python
Category
AWS Lambda
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonawslambdacommand-injectionos-spawntaint-analysisinter-proceduralCWE-78OWASP-A03
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-LAMBDA-SEC-003 --project .
1
2
3
4
5
6
7
8
9
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

About This Rule

Understanding the vulnerability and how it is detected

This rule detects OS command injection vulnerabilities in AWS Lambda functions where untrusted event data flows into os.spawn*() family functions: os.spawnl(), os.spawnle(), os.spawnlp(), os.spawnlpe(), os.spawnv(), os.spawnve(), os.spawnvp(), and os.spawnvpe().

Lambda functions receive input from the event dictionary populated by API Gateway, SQS, SNS, S3, DynamoDB Streams, and other AWS triggers. Fields such as event.get("body"), event.get("queryStringParameters"), and event["Records"] are fully attacker-controllable in public-facing deployments. There is no sanitization layer between the raw event payload and application code.

The os.spawn*() family is a lower-level process spawning API that predates the subprocess module. Variants ending in 'p' (spawnlp, spawnvp) search PATH for the executable, making them susceptible to PATH manipulation attacks when combined with user-controlled executable names. The 'l' variants take arguments as individual strings; when any argument position contains attacker-controlled event data, the attacker controls a process argument. In Lambda, successful exploitation yields access to the execution role's AWS credentials stored in environment variables, enabling further attacks against the broader AWS environment.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Direct Process Spawning with Attacker-Controlled Arguments

os.spawn*() spawns a new process directly. When event data controls argument positions, an attacker can influence the behavior of the spawned process. Argument injection remains possible if the target binary interprets attacker-controlled flags as commands (e.g., passing '-e cmd' to certain binaries).

2

PATH Manipulation via spawnlp/spawnvp Variants

The 'p' variants (spawnlp, spawnvp, spawnlpe, spawnvpe) search the PATH environment variable for the executable. If an attacker can control the executable name argument and the PATH contains writable directories, the spawn can be redirected to an attacker-controlled binary in /tmp.

3

AWS Credential Exfiltration

Spawned processes in the Lambda environment inherit the parent's environment variables, including AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN. An attacker who controls process arguments can spawn a process that reads and exfiltrates these credentials via the network.

4

Execution Environment Reconnaissance

Injected arguments can cause spawned processes to list filesystem contents, read configuration files, or probe the network. Lambda execution environments contain deployed code, environment variables with secrets, and /tmp data that can be enumerated and exfiltrated through injected process arguments.

How to Fix

Recommended remediation steps

  • 1Replace all os.spawn*() calls with subprocess.run() using a list of arguments and shell=False to use the modern, well-documented safe API.
  • 2When using spawn variants that search PATH ('p' variants), always pass the full absolute path to the executable instead of relying on PATH lookup.
  • 3Validate all event fields with strict allowlists or regular expressions before they appear in any argument position of a spawn or subprocess call.
  • 4Apply least-privilege IAM policies to the Lambda execution role to minimize the scope of credential exfiltration if injection occurs.
  • 5Monitor Lambda CloudWatch logs for unexpected process output or error patterns that may indicate active exploitation.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule performs inter-procedural taint analysis with global scope. Sources are Lambda event dictionary access calls: calls("event.get"), calls("event.__getitem__"), including event.get("body"), event.get("queryStringParameters"), event.get("pathParameters"), and event["Records"]. Sinks are calls("os.spawnl"), calls("os.spawnle"), calls("os.spawnlp"), calls("os.spawnlpe"), calls("os.spawnv"), calls("os.spawnve"), calls("os.spawnvp"), and calls("os.spawnvpe") with tainted values tracked via .tracks(0) for the executable argument and additional argument positions. The analysis follows taint through variable assignments, list construction, and function boundaries.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-78 ranked #5 in 2023 Most Dangerous Software Weaknesses
OWASP Top 10
A03:2021 - Injection
PCI DSS v4.0
Requirement 6.2.4 - protect against injection attacks
NIST SP 800-53
SI-10: Information Input Validation
AWS Security Best Practices
Validate all inputs; apply least-privilege execution roles

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Lambda Command Injection via os.spawn*()

os.system() always invokes the shell, so any shell metacharacter in user input can chain additional commands. os.spawn*() does not invoke a shell by default, so traditional shell metacharacter injection does not work in the same way. However, spawn is still dangerous when event data controls argument positions: the target binary may interpret injected flags as commands, and PATH-searching variants (spawnlp, spawnvp) can be directed to attacker-controlled executables.
Python's official documentation recommends using the subprocess module in preference to os.spawn*(). subprocess.run() with a list provides the same process-spawning capability with a cleaner interface, better error handling, and well-documented security considerations. There is no modern use case where os.spawn*() is preferable to subprocess.run().
The 'p' variants (spawnlp, spawnvp, spawnlpe, spawnvpe) search the PATH environment variable for the executable. This creates an additional attack vector: if the executable name is attacker-controlled or if /tmp appears in PATH, the spawn can execute an unintended binary. Non-'p' variants require the full path, limiting this specific risk. Both variants are dangerous when argument positions contain event data.
Download the S3 object to /tmp using the boto3 S3 client with a sanitized, hardcoded local filename. Then pass that known-safe local path to subprocess.run() with a list. Never derive the local filename or subprocess arguments directly from the S3 key, bucket name, or event metadata without strict validation.
Search all Lambda function source files for 'os.spawn'. Review each call site to determine whether any argument position can be influenced by event data, environment variables, or database values. For each finding, replace the spawn call with subprocess.run() using a list and add input validation.

New feature

Get these findings posted directly on your GitHub pull requests

The Lambda Command Injection via os.spawn*() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works