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

New rule for "silent execution" of popen based on Pytorch attack. Some cleanup of processing whl files #119

Merged
merged 3 commits into from
Jan 3, 2023
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ GuardDog comes with 2 types of heuristics:
| [Unusual domain extension](https://github.com/DataDog/guarddog/blob/main/guarddog/analyzer/sourcecode/shady-links.yml) | Usage of a domain name with an extension frequently used by malware (e.g. `.xyz` or `.top`) |
| [Dynamic execution of hidden data from an image](https://github.com/DataDog/guarddog/blob/main/guarddog/analyzer/sourcecode/steganography.yml) | The package uses steganography to extract a payload from an image and execute it |
| [Use of a common obfuscation method](https://github.com/DataDog/guarddog/blob/main/guarddog/analyzer/sourcecode/obfuscation.yml) | The package uses an obfuscation method commonly used by malware, such as running `eval` on hexadecimal strings |
| [Silent execution of a process](https://github.com/DataDog/guarddog/blob/main/guarddog/analyzer/sourcecode/silent-process-execution.yml) | The package spawns a subprocess without capturing its output |

### Package metadata heuristics

Expand Down
7 changes: 7 additions & 0 deletions guarddog/analyzer/sourcecode/silent-process-execution.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
rules:
- id: silent-process-execution
pattern: subprocess.$FUNC(..., stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, ...)
message: subprocess called to a file while silencing stdout, stderr and stdin to /dev/null
languages:
- python
severity: WARNING
7 changes: 3 additions & 4 deletions guarddog/scanners/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import sys

import pathos # type: ignore
import tarsafe # type:ignore
import tempfile
from abc import abstractmethod

Expand Down Expand Up @@ -192,14 +191,14 @@ def scan_local(self, path, rules=None) -> dict:
rules = set(rules)

if os.path.exists(path):
if path.endswith('.tar.gz'):
if path.endswith('.tar.gz') or path.endswith('.tgz') or path.endswith('.zip') or path.endswith('.whl'):
with tempfile.TemporaryDirectory() as tmpdirname:
tarsafe.open(path).extractall(tmpdirname)
safe_extract(path, tmpdirname)
return self.analyzer.analyze_sourcecode(tmpdirname, rules=rules)
elif os.path.isdir(path):
return self.analyzer.analyze_sourcecode(path, rules=rules)
else:
raise Exception(f"Path {path} is not a directory nor a tar.gz archive.")
raise Exception(f"Path {path} is not a directory nor an archive type supported by GuardDog.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! We'll need to decouple that code from the utils one at some point, but it's "good enough" for now

raise Exception(f"Path {path} does not exist.")

@abstractmethod
Expand Down
2 changes: 1 addition & 1 deletion guarddog/utils/archives.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def safe_extract(source_archive: str, target_directory: str) -> None:
"""
if source_archive.endswith('.tar.gz') or source_archive.endswith('.tgz'):
tarsafe.open(source_archive).extractall(target_directory)
elif source_archive.endswith('.zip'):
elif source_archive.endswith('.zip') or source_archive.endswith('.whl'):
with zipfile.ZipFile(source_archive, 'r') as zip:
for file in zip.namelist():
# Note: zip.extract cleans up any malicious file name such as directory traversal attempts
Expand Down
14 changes: 14 additions & 0 deletions tests/analyzer/sourcecode/silent-process-execution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from .autotuner import Config, Heuristics, autotune, heuristics # noqa: F401
from .jit import JITFunction, KernelInterface, version_key # noqa: F401

try:
import subprocess
import os

# ruleid: silent-process-execution
subprocess.Popen(os.path.join(os.path.dirname(os.path.abspath(__file__)), "foobar"),
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL)
except:
pass