python.lang.security.audit.dangerous-spawn-process-tainted-env-args.dangerous-spawn-process-tainted-env-args

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Found user controlled content when spawning a process. This is dangerous because it allows a malicious actor to execute commands.

Run Locally

Run in CI

Defintion

rules:
  - id: dangerous-spawn-process-tainted-env-args
    mode: taint
    options:
      symbolic_propagation: true
    pattern-sources:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-either:
                      - pattern: os.environ
                      - pattern: os.environ.get('$FOO', ...)
                      - pattern: os.environb
                      - pattern: os.environb.get('$FOO', ...)
                      - pattern: os.getenv('$ANYTHING', ...)
                      - pattern: os.getenvb('$ANYTHING', ...)
              - patterns:
                  - pattern-either:
                      - patterns:
                          - pattern-either:
                              - pattern: sys.argv
                              - pattern: sys.orig_argv
                      - patterns:
                          - pattern-inside: |
                              $PARSER = argparse.ArgumentParser(...)
                              ...
                          - pattern-inside: |
                              $ARGS = $PARSER.parse_args()
                          - pattern: <... $ARGS ...>
                      - patterns:
                          - pattern-inside: |
                              $PARSER = optparse.OptionParser(...)
                              ...
                          - pattern-inside: |
                              $ARGS = $PARSER.parse_args()
                          - pattern: <... $ARGS ...>
                      - patterns:
                          - pattern-either:
                              - pattern-inside: |
                                  $OPTS, $ARGS = getopt.getopt(...)
                                  ...
                              - pattern-inside: |
                                  $OPTS, $ARGS = getopt.gnu_getopt(...)
                                  ...
                          - pattern-either:
                              - patterns:
                                  - pattern-inside: |
                                      for $O, $A in $OPTS:
                                        ...
                                  - pattern: $A
                              - pattern: $ARGS
    pattern-sinks:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-not: os.$METHOD($MODE, "...", ...)
                  - pattern-inside: os.$METHOD($MODE, $CMD, ...)
                  - pattern: $CMD
                  - metavariable-regex:
                      metavariable: $METHOD
                      regex: (spawnl|spawnle|spawnlp|spawnlpe|spawnv|spawnve|spawnvp|spawnvp|spawnvpe|posix_spawn|posix_spawnp|startfile)
              - patterns:
                  - pattern-not: os.$METHOD($MODE, "...", ["...","...",...], ...)
                  - pattern-inside: os.$METHOD($MODE, $BASH, ["-c",$CMD,...],...)
                  - pattern: $CMD
                  - metavariable-regex:
                      metavariable: $METHOD
                      regex: (spawnv|spawnve|spawnvp|spawnvp|spawnvpe|posix_spawn|posix_spawnp)
                  - metavariable-regex:
                      metavariable: $BASH
                      regex: (.*)(sh|bash|ksh|csh|tcsh|zsh)
              - patterns:
                  - pattern-not: os.$METHOD($MODE, "...", "...", "...", ...)
                  - pattern-inside: os.$METHOD($MODE, $BASH, "-c", $CMD,...)
                  - pattern: $CMD
                  - metavariable-regex:
                      metavariable: $METHOD
                      regex: (spawnl|spawnle|spawnlp|spawnlpe)
                  - metavariable-regex:
                      metavariable: $BASH
                      regex: (.*)(sh|bash|ksh|csh|tcsh|zsh)
    message: Found user controlled content when spawning a process. This is
      dangerous because it allows a malicious actor to execute commands.
    metadata:
      source-rule-url: https://bandit.readthedocs.io/en/latest/plugins/b605_start_process_with_a_shell.html
      cwe:
        - "CWE-78: Improper Neutralization of Special Elements used in an OS
          Command ('OS Command Injection')"
      owasp:
        - A01:2017 - Injection
        - A03:2021 - Injection
      references:
        - https://semgrep.dev/docs/cheat-sheets/python-command-injection/
      asvs:
        section: "V5: Validation, Sanitization and Encoding Verification Requirements"
        control_id: 5.3.8 OS Command Injection
        control_url: https://github.com/OWASP/ASVS/blob/master/4.0/en/0x13-V5-Validation-Sanitization-Encoding.md#v53-output-encoding-and-injection-prevention-requirements
        version: "4"
      category: security
      technology:
        - python
      confidence: MEDIUM
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: MEDIUM
      impact: HIGH
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Command Injection
    languages:
      - python
    severity: ERROR

Examples

dangerous-spawn-process-tainted-env-args.py

import os
import shlex
import sys
from somewhere import something

# ok:dangerous-spawn-process-tainted-env-args
os.spawnlp(os.P_WAIT, "ls")

# ok:dangerous-spawn-process-tainted-env-args
os.spawnlpe(os.P_WAIT, "ls")

# ok:dangerous-spawn-process-tainted-env-args
os.spawnv(os.P_WAIT, "/bin/ls")

# ok:dangerous-spawn-process-tainted-env-args
os.spawnve(os.P_WAIT, "/bin/ls", ["-a"], os.environ)

# fn:dangerous-spawn-process-tainted-env-args
os.spawnlp(os.P_WAIT, something())

# fn:dangerous-spawn-process-tainted-env-args
os.spawnlpe(os.P_WAIT, something())

# fn:dangerous-spawn-process-tainted-env-args
os.spawnv(os.P_WAIT, something())

# fn:dangerous-spawn-process-tainted-env-args
os.spawnve(os.P_WAIT, something(), ["-a"], os.environ)

# fn:dangerous-spawn-process-tainted-env-args
os.spawnve(os.P_WAIT, "/bin/bash", ["-c", something()], os.environ)

# fn:dangerous-spawn-process-tainted-env-args
os.spawnl(os.P_WAIT, "/bin/bash", "-c", something())


def run_payload(shell_command: str) -> None:
    args = shlex.split(shell_command)
    path = args[0]
    # fn:dangerous-spawn-process-tainted-env-args
    pid = os.posix_spawn(path, args, os.environ)
    os.waitpid(pid, 0)


cmd = sys.argv[2]

# ruleid:dangerous-spawn-process-tainted-env-args
os.spawnlp(os.P_WAIT, cmd)

# ruleid:dangerous-spawn-process-tainted-env-args
os.spawnlpe(os.P_WAIT, cmd)

# ruleid:dangerous-spawn-process-tainted-env-args
os.spawnv(os.P_WAIT, cmd)

# ruleid:dangerous-spawn-process-tainted-env-args
os.spawnve(os.P_WAIT, cmd, ["-a"], os.environ)

# ruleid:dangerous-spawn-process-tainted-env-args
os.spawnve(os.P_WAIT, "/bin/bash", ["-c", cmd], os.environ)

# ruleid:dangerous-spawn-process-tainted-env-args
os.spawnl(os.P_WAIT, "/bin/bash", "-c", cmd)


def run_payload() -> None:
    shell_command = sys.argv[2]
    args = shlex.split(shell_command)
    path = args[0]
    # ruleid:dangerous-spawn-process-tainted-env-args
    pid = os.posix_spawn(path, args, os.environ)
    os.waitpid(pid, 0)