Skip to content

OpenAgents HTTP Transport: Unauthenticated SSRF via test_default_model Endpoint #566

Description

@honysyang

Summary

The POST /api/admin/default-model/test endpoint accepts user-supplied provider and base_url without authentication. When provider is 'custom' or 'openai-compatible', the base_url flows to SimpleGenericProvider which creates an AsyncOpenAI client pointing to the attacker-specified URL, enabling unauthenticated SSRF to internal cloud metadata, private networks, and localhost.

Details

Root cause: test_default_model in http.py L4542-4631 extracts provider, model_name, api_key, base_url from JSON body with no _require_admin() check. base_url flows through create_model_provider() L498-508 where 'custom'/'openai-compatible' branch passes it to SimpleGenericProvider. In providers.py L791-809, SimpleGenericProvider.init() creates AsyncOpenAI(base_url=attacker_url) which issues HTTP requests to attacker-controlled destinations on chat_completion(). No URL validation or IP restrictions exist.

Core vulnerable code path:

# sdk/src/openagents/sdk/transports/http.py:4542-4592
async def test_default_model(self, request):
    """Test the default LLM model configuration."""
    try:
        data = await request.json()
        provider = data.get("provider")
        model_name = data.get("model_name")
        api_key = data.get("api_key")
        base_url = data.get("base_url")

        if not provider:
            return web.json_response({"success": False, "error_message": "provider is required"}, status=400)
        if not model_name:
            return web.json_response({"success": False, "error_message": "model_name is required"}, status=400)
        if not api_key:
            return web.json_response({"success": False, "error_message": "api_key is required"}, status=400)

        from openagents.config.llm_configs import create_model_provider, MODEL_CONFIGS

        effective_provider = provider
        effective_api_base = base_url

        if not effective_api_base and provider in MODEL_CONFIGS:
            effective_api_base = MODEL_CONFIGS[provider].get("api_base")

SSRF entry point. User-controlled base_url extracted from JSON body. No _require_admin() authorization check.

# sdk/src/openagents/config/llm_configs.py:498-508
elif provider == "custom" or provider == "openai-compatible":
        if not api_base:
            raise ValueError("API base URL is required")

        return SimpleGenericProvider(
            model_name=model_name, api_base=api_base, api_key=api_key, **kwargs
        )

'custom' branch passes user-supplied api_base directly to SimpleGenericProvider with no URL validation.

# sdk/src/openagents/lms/providers.py:791-809
class SimpleGenericProvider(BaseModelProvider):
    """Generic provider for OpenAI-compatible APIs."""
    def __init__(self, model_name: str, api_base: str, api_key=None, **kwargs):
        self.model_name = model_name
        self.api_base = api_base
        from openai import AsyncOpenAI
        if not api_key:
            logger.warning(f"No API key provided")
        self.client = AsyncOpenAI(base_url=api_base, api_key=api_key or "dummy")

SSRF sink. AsyncOpenAI(base_url=attacker_url) creates HTTP client pointing to attacker-controlled destination.

POC

Prerequisites: OpenAgents HTTP Transport on port 8700, openai package installed, network access. Step 1: POST /api/admin/default-model/test with body {"provider":"custom","model_name":"gpt-4","api_key":"dummy","base_url":"http://169.254.169.254/latest/meta-data/"}. Server issues request to AWS metadata. Step 2: Probe internal services with body {"provider":"custom","model_name":"gpt-4","api_key":"dummy","base_url":"http://localhost:6379/"}. Step 3: Scan internal network by iterating IPs/ports. Expected: server issues HTTP requests to attacker-specified URLs, enabling metadata theft and internal reconnaissance.

Impact

High: Unauthenticated SSRF. Cloud metadata theft, internal network scanning, exploitation of internal APIs. CVSS 7.5.

Remediation

  1. Add _require_admin() to test_default_model. 2. URL allowlist for known LLM endpoints. 3. Block RFC1918/cloud-metadata IPs. 4. Enforce HTTPS-only. 5. DNS rebinding protections.

Disclosure Notes

Source-code audit of OpenAgents v1. Affected/patch versions to be confirmed.

Supplemental Information

Affected products

  • Ecosystem: self-hosted
  • Package name: OpenAgents
  • Affected versions: v1
  • Patched versions: to be confirmed

Severity

  • Scoring method: CVSS v3.1
  • Score: 7.5
  • Vector string: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Weaknesses

  • CWE: CWE-918 Server-Side Request Forgery (SSRF)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions