Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(python): os command injection (CWE-78) #398

Merged
merged 3 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions rules/python/lang/os_command_injection.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
imports:
- python_shared_common_external_input
- python_shared_lang_import1
patterns:
- pattern: $<OS>($<...>$<EXTERNAL_INPUT>$<...>)
filters:
- variable: OS
detection: python_shared_lang_import1
scope: cursor
filters:
- variable: MODULE1
values: [os]
- variable: NAME
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_module
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: $<SUBPROCESS>($<EXTERNAL_INPUT>$<...>)
filters:
- variable: SUBPROCESS
detection: python_shared_lang_import1
scope: cursor
filters:
- variable: MODULE1
values: [subprocess]
- variable: NAME
values:
- call
- check_call
- check_output
- run
- Popen
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: $<OS>($<_>, $<...>$<EXTERNAL_INPUT>$<...>, $<...>)
filters:
- variable: OS
detection: python_shared_lang_import1
scope: cursor
filters:
- variable: MODULE1
values: [os]
- variable: NAME
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>($<_>, $<BASH>, ["-c", $<...>$<EXTERNAL_INPUT>$<...>], $<...>)
filters:
- variable: OS
detection: python_shared_lang_import1
scope: cursor
filters:
- variable: MODULE1
values: [os]
- variable: NAME
values:
- spawnv
- spawnve
- spawnvp
- spawnvp
- spawnvpe
- posix_spawn
- posix_spawnp
- variable: BASH
regex: (.*)(sh|bash|ksh|csh|tcsh|zsh)
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
- pattern: $<OS>.$<METHOD>($<_>, $<BASH>, "-c", $<...>$<EXTERNAL_INPUT>$<...>, $<...>)
filters:
- variable: OS
detection: python_shared_lang_import1
scope: cursor
filters:
- variable: MODULE1
values: [os]
- variable: NAME
values:
- spawnl
- spawnle
- spawnlp
- spawnlpe
- variable: BASH
regex: (.*)(sh|bash|ksh|csh|tcsh|zsh)
- variable: EXTERNAL_INPUT
detection: python_shared_common_external_input
scope: result
auxiliary:
- id: python_lang_os_command_injection_external_os_module
patterns:
- os
elsapet marked this conversation as resolved.
Show resolved Hide resolved
- __import__("os")
- import $<!>os
- import os as $<!>$<_>
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: []
})
})
})
31 changes: 31 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,31 @@
# 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())

import os
import subprocess as subproc
def bad():
unsafe = input("what hack today?")
# bearer:expected python_lang_os_command_injection
subproc.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
subproc.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)
subproc.run(["dir"], shell=False)
Loading