{"id":"CVE-2026-62677","aliases":["GHSA-p8rw-8qj3-hf33","PYSEC-2026-3875"],"url":"https://o3.security/vulnerability/CVE-2026-62677","summary":"Omnigent: Unvalidated os_env.cwd in agent bundle yields arbitrary host filesystem access on runners without OMNIGENT_RUNNER_WORKSPACE","details":"### Summary\n\nAn authenticated, non-admin user can obtain **arbitrary host-filesystem read/write** (and host environment-secret disclosure) on an Omnigent **runner** by uploading an agent bundle whose `os_env.cwd` points outside any intended workspace (e.g. `/` or `/home/<victim>`). The `cwd` field is taken **verbatim** from the bundle with no validation, normalization, or boundary check anywhere in the spec pipeline.\n\nThis is a **different sink** from GHSA-jrrm-9hc7-2v3h (CWE-94, shared-agent bundle overwrite -> stdio MCP RCE). It shares the bundle-upload *vector* but is reached through the user's **own** session-scoped agent and is **not** addressed by that advisory's proposed shared-agent guard.\n\n### Preconditions\n\n- Runner realizes a session-scoped uploaded bundle **without** `OMNIGENT_RUNNER_WORKSPACE` set. When that env var is set (CLI- and host-launched sessions set it), the spec `cwd` is overridden and the attack is neutralized — so this is deployment-gated, not universal.\n- Attacker is any authenticated user (no admin scope; `_require_user` only checks identity). No shared-agent overwrite needed.\n\n### Details (verified against code)\n\n1. **Parse — no validation.** `omnigent/spec/parser.py:696` stores `cwd=str(cwd_raw)` verbatim. Absolute paths (`/`, `/etc`), `../..`, etc. are all accepted. The `sandbox.type` is likewise author-chosen and `\"none\"` is legal.\n2. **Validate — cwd unconstrained.** `omnigent/spec/validator.py` `_validate_os_env` checks only fork/scratch/egress combinations; it never references `cwd` (the sole mention, line ~526, is a comment). No boundary is applied to the cwd itself. The boundary in `server/schemas.py` validates a *caller-supplied workspace against* the spec cwd (treating the author cwd as trusted) and only for host-launched sessions — it does not bound the cwd.\n3. **Sink.** `omnigent/inner/os_env.py:890` sets `cwd = Path(spec.cwd or os.getcwd()).resolve(strict=False)` as the environment root; `os_env.py:934` does `shutil.copytree(src=cwd, ...)` when `fork=true`. All agent file/shell tools are bounded by `_assert_within_cwd` (`os_env.py:1040`), which checks `resolved.relative_to(cwd)` — but since **cwd is attacker-controlled**, `cwd=/` makes the entire host filesystem in-bounds for read and write; `fork=true` with `cwd=/home/victim` copies that tree into the agent-readable workspace.\n4. **Decisive gate.** `omnigent/runner/resource_registry.py:648-654`: `cwd = default_cwd` only when `self._runner_workspace is not None or spec_os_env.cwd in (None, \".\", \"./\")`; otherwise `cwd = spec_os_env.cwd` (the attacker's absolute path). So `OMNIGENT_RUNNER_WORKSPACE` is the only thing standing between the spec and the host FS — and it is an operational control, not an in-code guard. A code comment at `tool_dispatch.py:~4207` claims cwd \"is treated as a boundary at session-create time,\" which is not true on this path.\n\n### Attack path\n\n1. Authenticated user sends `POST /v1/sessions` (multipart) with an agent bundle whose `config.yaml` contains:\n   ```yaml\n   os_env:\n     cwd: \"/\"            # or /home/<victim>, with fork: true for one-shot exfil\n     sandbox: { type: none }\n   ```\n2. On a runner without `OMNIGENT_RUNNER_WORKSPACE`, the agent's `sys_os_read`/`write`/`edit`/`shell` tools now operate over the whole host filesystem, and (sandbox inactive) inherit the runner's full environment — exposing host secrets via e.g. `sys_os_shell(\"env\")`.\n\n### Impact\n\nArbitrary host-filesystem read/write and runner-environment secret disclosure, available to any authenticated user on an affected deployment. High severity (deployment-conditional).\n\n### Suggested fix\n\nAdd a control on the `cwd` field itself in `omnigent/spec/_validate_os_env` (and/or at parse): reject absolute paths and `..` traversal, and require `cwd` to resolve within the runner workspace / an allow-listed root. Do not rely on `OMNIGENT_RUNNER_WORKSPACE` being set as the sole defense. Consider also disallowing bundle-author `sandbox.type: none` for server-realized (non-CLI) sessions.\n\n### Related\n\nGHSA-jrrm-9hc7-2v3h (same bundle-upload vector, different sink; that fix does not cover this).","published":"2026-08-21T17:44:37.312Z","modified":"2026-09-20T11:30:45.028001632Z","cvss":{"score":8.8,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"},"epss":{"score":0.00446,"percentile":0.38012,"asOf":"2026-09-17"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"omnigent","fixedVersion":"0.3.0"}],"fix":{"url":"https://github.com/omnigent-ai/omnigent/commit/7ca0cca3c9a65c04c489edf68f0e080424a26868","label":"omnigent-ai/omnigent@7ca0cca"},"references":[{"type":"WEB","url":"https://github.com/omnigent-ai/omnigent/releases/tag/v0.3.0"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/62xxx/CVE-2026-62677.json"},{"type":"ADVISORY","url":"https://github.com/omnigent-ai/omnigent/security/advisories/GHSA-p8rw-8qj3-hf33"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-62677"},{"type":"FIX","url":"https://github.com/omnigent-ai/omnigent/commit/7ca0cca3c9a65c04c489edf68f0e080424a26868"},{"type":"FIX","url":"https://github.com/omnigent-ai/omnigent/pull/1417"},{"type":"PACKAGE","url":"https://github.com/omnigent-ai/omnigent"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-20T11:30:45.028001632Z"}}