Bug 2507452 (CVE-2026-17557) - CVE-2026-17557 lm-evaluation-harness: lm-evaluation-harness: SSRF via job-spec model.url with bearer credential forwarding
Summary: CVE-2026-17557 lm-evaluation-harness: lm-evaluation-harness: SSRF via job-spe...
Keywords:
Status: NEW
Alias: CVE-2026-17557
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-07-27 12:50 UTC by OSIDB Bzimport
Modified: 2026-10-21 06:54 UTC (History)
1 user (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-07-27 12:50:50 UTC
This flaw was identified by a Red Hat Product Security audit (Glasswing, RHOAIENG-70012, FIND-004).

lm-evaluation-harness's EvalHub adapter (main.py) builds the OpenAI-compatible completions URL directly from the JobSpec's model.url field, with no host/scheme allow-list, no RFC1918/link-local/loopback block, and no DNS-rebinding protection:

build_lmeval_config() (main.py):
    base = str(model_spec.url or "").rstrip("/")
    ...
    completions_url = f"{base}/v1/completions"
    ...
    return ("local-completions", {"model": model_name, "base_url": completions_url, ...}, gen_kwargs)

run_benchmark_job() (main.py) resolves the model's bearer credential and exports it into the process environment before the evaluation runs:
    creds = resolve_model_credentials()
    if creds.api_key:
        os.environ["OPENAI_API_KEY"] = creds.api_key

The upstream lm_eval "local-completions" backend (lm_eval/models/openai_completions.py, lm_eval/models/api_models.py) reads that same environment variable and attaches it as a Bearer token on every outbound completions request:

    # openai_completions.py
    @property
    def api_key(self):
        return os.environ.get("OPENAI_API_KEY", "")

    # api_models.py
    @cached_property
    def header(self) -> dict:
        return {"Authorization": f"Bearer {self.api_key}"}
    ...
    response = requests.post(self.base_url, json=..., headers=self.header, verify=self.verify_certificate)

job_spec.model.url is a tenant-controlled field of the JobSpec JSON mounted read-only at /meta/job.json (ConfigMap) in the evaluation Job pod. Any authenticated user able to submit an EvalHub evaluation job in their own namespace can set model.url to an arbitrary host, causing the eval pod to POST the resolved bearer credential to that host. The credential is the tenant's own model-auth Secret, mounted in cleartext at /var/run/secrets/model/api-key directly into the runtime container (eval-hub's buildRuntimeContainerVolumesAndMounts, internal/eval_hub/runtimes/k8s/job_builders.go). This allows:
- Credential disclosure to an attacker-controlled external listener.
- Blind SSRF against internal/link-local endpoints reachable from the evaluation Job pod's network namespace (in-cluster services, cloud metadata endpoints, etc.), gated only by whatever NetworkPolicy applies to that namespace.

Verified against red-hat-data-services/lm-evaluation-harness rhoai-3.4 (commit c4a30c07) and red-hat-data-services/eval-hub rhoai-3.4 (commit 4947496): eval-hub's job-building code passes evaluation.Model.URL through to job_spec.model.url unmodified, and mounts the tenant-supplied model-auth Secret directly into the eval container with no rewriting or credential indirection.

This is fixed on RHOAI 3.5 by an unrelated architecture change (eval-hub-sdk >= 0.4.x / lm-evaluation-harness commit 614ae2ff, merged 2026-07-16, implementing ADR EH-0004's sidecar credential-injection design): eval-hub's k8s runtime now rewrites job_spec.model.url server-side to always target a co-located sidecar container (only the original request path is preserved; scheme and host are replaced with the sidecar's localhost address), and the credential value the adapter ever holds is an opaque reference token resolvable only inside the sidecar process, which is the only container that mounts the real secret. This scopes the affected range to RHOAI 3.4 only.


Note You need to log in before you can comment on or make changes to this bug.