mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-06 19:35:13 +02:00
143 lines
3.9 KiB
Markdown
143 lines
3.9 KiB
Markdown
|
|
# Python Rules
|
||
|
|
|
||
|
|
Nyx detects Python vulnerabilities through AST patterns and taint analysis, covering code execution, command injection, deserialization, SQL injection, and weak crypto.
|
||
|
|
|
||
|
|
## Taint Labels
|
||
|
|
|
||
|
|
Python has moderate taint label coverage. Sources, sinks, and sanitizers are defined in `src/labels/python.rs`.
|
||
|
|
|
||
|
|
### Sources
|
||
|
|
|
||
|
|
| Matcher | Cap |
|
||
|
|
|---------|-----|
|
||
|
|
| `os.getenv`, `os.environ` | all |
|
||
|
|
| `request.args`, `request.form`, `request.json`, `request.headers`, `request.cookies`, `input` | all |
|
||
|
|
| `sys.argv` | all |
|
||
|
|
| `argparse.parse_args`, `urllib.request.urlopen`, `requests.get`, `requests.post` | all |
|
||
|
|
|
||
|
|
### Sanitizers
|
||
|
|
|
||
|
|
| Matcher | Cap |
|
||
|
|
|---------|-----|
|
||
|
|
| `html.escape` | HTML_ESCAPE |
|
||
|
|
| `shlex.quote` | SHELL_ESCAPE |
|
||
|
|
| `os.path.realpath` | FILE_IO |
|
||
|
|
|
||
|
|
### Sinks
|
||
|
|
|
||
|
|
| Matcher | Cap |
|
||
|
|
|---------|-----|
|
||
|
|
| `eval`, `exec` | SHELL_ESCAPE |
|
||
|
|
| `os.system`, `os.popen`, `subprocess.call`, `subprocess.run`, `subprocess.Popen`, `subprocess.check_output`, `subprocess.check_call` | SHELL_ESCAPE |
|
||
|
|
| `cursor.execute`, `cursor.executemany` | SHELL_ESCAPE |
|
||
|
|
| `send_file`, `send_from_directory` | FILE_IO |
|
||
|
|
| `open` | FILE_IO |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## AST Pattern Rules
|
||
|
|
|
||
|
|
### Code Execution
|
||
|
|
|
||
|
|
| Rule ID | Severity | Tier | Description |
|
||
|
|
|---------|----------|------|-------------|
|
||
|
|
| `py.code_exec.eval` | High | A | `eval()` — dynamic code execution |
|
||
|
|
| `py.code_exec.exec` | High | A | `exec()` — dynamic code execution |
|
||
|
|
| `py.code_exec.compile` | Medium | A | `compile()` with exec/eval mode |
|
||
|
|
|
||
|
|
### Command Execution
|
||
|
|
|
||
|
|
| Rule ID | Severity | Tier | Description |
|
||
|
|
|---------|----------|------|-------------|
|
||
|
|
| `py.cmdi.os_system` | High | A | `os.system()` — shell command execution |
|
||
|
|
| `py.cmdi.os_popen` | High | A | `os.popen()` — shell command execution |
|
||
|
|
| `py.cmdi.subprocess_shell` | High | B | `subprocess.*` with `shell=True` |
|
||
|
|
|
||
|
|
### Deserialization
|
||
|
|
|
||
|
|
| Rule ID | Severity | Tier | Description |
|
||
|
|
|---------|----------|------|-------------|
|
||
|
|
| `py.deser.pickle_loads` | High | A | `pickle.loads()` / `pickle.load()` — arbitrary object deserialization |
|
||
|
|
| `py.deser.yaml_load` | High | A | `yaml.load()` without SafeLoader |
|
||
|
|
| `py.deser.shelve_open` | Medium | A | `shelve.open()` — pickle-backed deserialization |
|
||
|
|
|
||
|
|
### SQL Injection
|
||
|
|
|
||
|
|
| Rule ID | Severity | Tier | Description |
|
||
|
|
|---------|----------|------|-------------|
|
||
|
|
| `py.sqli.execute_format` | Medium | B | `cursor.execute()` with string concatenation |
|
||
|
|
|
||
|
|
### Weak Crypto
|
||
|
|
|
||
|
|
| Rule ID | Severity | Tier | Description |
|
||
|
|
|---------|----------|------|-------------|
|
||
|
|
| `py.crypto.md5` | Low | A | `hashlib.md5()` — weak hash algorithm |
|
||
|
|
| `py.crypto.sha1` | Low | A | `hashlib.sha1()` — weak hash algorithm |
|
||
|
|
|
||
|
|
### Template Injection
|
||
|
|
|
||
|
|
| Rule ID | Severity | Tier | Description |
|
||
|
|
|---------|----------|------|-------------|
|
||
|
|
| `py.xss.jinja_from_string` | Medium | A | `jinja2.Template.from_string()` — template injection |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
### `py.deser.pickle_loads` — Unsafe deserialization
|
||
|
|
|
||
|
|
**Vulnerable:**
|
||
|
|
```python
|
||
|
|
import pickle
|
||
|
|
data = pickle.loads(request.body) # Arbitrary code execution
|
||
|
|
```
|
||
|
|
|
||
|
|
**Safe alternative:**
|
||
|
|
```python
|
||
|
|
import json
|
||
|
|
data = json.loads(request.body) # JSON is safe
|
||
|
|
```
|
||
|
|
|
||
|
|
### `py.cmdi.subprocess_shell` — Shell execution
|
||
|
|
|
||
|
|
**Vulnerable:**
|
||
|
|
```python
|
||
|
|
import subprocess
|
||
|
|
subprocess.call(user_input, shell=True) # Command injection
|
||
|
|
```
|
||
|
|
|
||
|
|
**Safe alternative:**
|
||
|
|
```python
|
||
|
|
import subprocess
|
||
|
|
import shlex
|
||
|
|
subprocess.call(shlex.split(user_input), shell=False)
|
||
|
|
# Or better: use an explicit command list
|
||
|
|
subprocess.call(["ls", "-la", user_dir])
|
||
|
|
```
|
||
|
|
|
||
|
|
### `py.deser.yaml_load` — Unsafe YAML
|
||
|
|
|
||
|
|
**Vulnerable:**
|
||
|
|
```python
|
||
|
|
import yaml
|
||
|
|
config = yaml.load(user_data) # Can instantiate arbitrary objects
|
||
|
|
```
|
||
|
|
|
||
|
|
**Safe alternative:**
|
||
|
|
```python
|
||
|
|
import yaml
|
||
|
|
config = yaml.safe_load(user_data) # Only basic Python types
|
||
|
|
```
|
||
|
|
|
||
|
|
### `py.sqli.execute_format` — SQL concatenation
|
||
|
|
|
||
|
|
**Vulnerable:**
|
||
|
|
```python
|
||
|
|
cursor.execute("SELECT * FROM users WHERE id=" + user_id)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Safe alternative:**
|
||
|
|
```python
|
||
|
|
cursor.execute("SELECT * FROM users WHERE id=?", (user_id,))
|
||
|
|
```
|