Skip to content

Commit

Permalink
feat(python): os command injection (CWE-78)
Browse files Browse the repository at this point in the history
  • Loading branch information
elsapet committed May 14, 2024
1 parent fc6d428 commit fd99be4
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
129 changes: 129 additions & 0 deletions rules/python/lang/os_command_injection.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
imports:
- python_shared_common_external_input
patterns:
- pattern: $<OS>.$<METHOD>($<...>$<EXTERNAL_INPUT>$<...>)
filters:
- variable: OS
detection: python_lang_os_command_injection_external_os_init
- variable: METHOD
values:
- system
- popen
- popen2
- popen3
- popen4
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: getattr($<OS>, "system")($<...>$<EXTERNAL_INPUT>$<...>)
filters:
- variable: OS
detection: python_lang_os_command_injection_external_os_init
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: subprocess.$<METHOD>($<SUBPROC_EXTERNAL_INPUT>$<...>)
filters:
- variable: METHOD
values:
- call
- check_call
- check_output
- run
- Popen
- variable: SUBPROC_EXTERNAL_INPUT
detection: python_lang_os_command_injection_external_input_subproc
scope: result
- pattern: $<OS>.$<METHOD>($<_>, $<...>$<EXTERNAL_INPUT>$<...>, $<...>)
filters:
- variable: OS
detection: python_lang_os_command_injection_external_os_init
- variable: METHOD
values:
- spawnl
- spawnle
- spawnlp
- spawnlpe
- spawnv
- spawnve
- spawnvp
- spawnvpe
- posix_spawn
- posix_spawnp
- startfile
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: $<OS>.$<METHOD>($<_>, $<BASH>, ["-c", $<...>$<EXTERNAL_INPUT>$<...>], $<...>)
filters:
- variable: OS
detection: python_lang_os_command_injection_external_os_init
- variable: BASH
regex: (.*)(sh|bash|ksh|csh|tcsh|zsh)
- variable: METHOD
values:
- spawnv
- spawnve
- spawnvp
- spawnvp
- spawnvpe
- posix_spawn
- posix_spawnp
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: $<OS>.$<METHOD>($<_>, $<BASH>, "-c", $<...>$<EXTERNAL_INPUT>$<...>, $<...>)
filters:
- variable: OS
detection: python_lang_os_command_injection_external_os_init
- variable: BASH
regex: (.*)(sh|bash|ksh|csh|tcsh|zsh)
- variable: METHOD
values:
- spawnl
- spawnle
- spawnlp
- spawnlpe
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
auxiliary:
- id: python_lang_os_command_injection_external_os_init
patterns:
- os
- __import__("os")
- id: python_lang_os_command_injection_external_input_subproc
patterns:
- pattern: $<...>$<EXTERNAL_INPUT>$<...>
filters:
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: |
[$<...>$<EXTERNAL_INPUT>$<...>]
filters:
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
languages:
- python
severity: critical
metadata:
description: Unsanitized user input in OS command
remediation_message: |-
## Description
Directly incorporating external or user-defined input into an OS command exposes the system to possible command injection attacks. This vulnerability allows attackers to execute unauthorized commands on the operating system, potentially leading to a compromise of system integrity.
## Remediations
- **Do not** use OS commands that include dynamic input directly. Instead, explore safer alternatives such as libraries or built-in functions that achieve the same goal without executing system commands.
- **Do** use hardcoded values for any input that is incorporated into OS commands. This approach minimizes the risk by ensuring only predefined inputs are used, thus preventing attackers from injecting malicious commands. Use safe lists or dictionaries if you need to be dynamic.
## References
- [OWASP command injection explained](https://owasp.org/www-community/attacks/Command_Injection)
cwe_id:
- 78
id: python_lang_os_command_injection
documentation_url: https://docs.bearer.com/reference/rules/python_lang_os_command_injection
20 changes: 20 additions & 0 deletions tests/python/lang/os_command_injection/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const {
createNewInvoker,
getEnvironment,
} = require("../../../helper.js")
const { ruleId, ruleFile, testBase } = getEnvironment(__dirname)

describe(ruleId, () => {
const invoke = createNewInvoker(ruleId, ruleFile, testBase)

test("os_command_injection", () => {
const testCase = "main.py"

const results = invoke(testCase)

expect(results).toEqual({
Missing: [],
Extra: []
})
})
})
29 changes: 29 additions & 0 deletions tests/python/lang/os_command_injection/testdata/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Use bearer:expected python_lang_os_command_injection to flag expected findings

my_os = __import__("os")
# bearer:expected python_lang_os_command_injection
getattr(my_os, "system")(input())

def bad():
unsafe = input("what hack today?")
# bearer:expected python_lang_os_command_injection
subprocess.run([unsafe, "exit 0"], shell=True, capture_output=True)
# bearer:expected python_lang_os_command_injection
os.system(unsafe)

# bearer:expected python_lang_os_command_injection
subprocess.check_output(
unsafe,
stderr=subprocess.STDOUT,
shell=True)

def bad2():
unsafe = sys.argv[1]
# bearer:expected python_lang_os_command_injection
os.spawnlp(os.P_WAIT, unsafe)
# bearer:expected python_lang_os_command_injection
os.spawnve(os.P_WAIT, "/bin/bash", ["-c", unsafe], os.environ)

def ok():
os.spawnve(os.P_WAIT, "/bin/ls", ["-a"], os.environ)
subprocess.run(["dir"], shell=False)

0 comments on commit fd99be4

Please sign in to comment.