# REPRO-2026-00359: CVE-2026-84502: Ansible Automation Platform automation-controller — Project scm_url argument injection into git ls-remote --upload-pack yields RCE on controller task pod ## Summary Status: published Severity: critical CVSS: Unknown CWE: CWE-88 (Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')) Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00359 CVE: CVE-2026-84502 ## Package Name: ansible/awx Ecosystem: github Affected: Unknown Fixed: Unknown ## Root Cause # Root Cause Analysis: CVE-2026-84502 ## Summary CVE-2026-84502 is an authenticated argument-injection vulnerability in Ansible AWX / Automation Controller Project SCM handling. AWX commit `a5b2179a3e693dd8e9f4c186636d93f9a436ada4` accepts a Git Project `scm_url` beginning with `-`. During a real ProjectUpdate, that value reaches Git as an option rather than a repository URL. An attacker-controlled `--upload-pack=` value therefore selects a shell command that Git executes, yielding command execution in the ProjectUpdate execution environment. ## Impact - **Affected component:** `awx/main/models/projects.py`, specifically Project `scm_url` validation, together with the AWX ProjectUpdate path and the Ansible Git module's `git ls-remote` invocation. - **Tested affected build:** AWX commit `a5b2179a3e693dd8e9f4c186636d93f9a436ada4` (`24.6.2.dev949+ga5b2179a3`). - **Affected product family:** Red Hat Ansible Automation Platform Automation Controller / upstream AWX versions that accept dash-prefixed Git SCM URLs. - **Risk:** Critical. An authenticated user able to create a Git Project and launch its update can execute attacker-selected commands in the controller's ProjectUpdate execution environment, threatening confidentiality, integrity, availability, tenant separation, and adjacent in-cluster resources. ## Impact Parity - **Disclosed/claimed maximum impact:** Authenticated remote code execution through the AWX API. - **Reproduced impact:** Two independent real API ProjectUpdate attempts executed attacker-selected commands and created unique target-local command-output markers. The first marker contained `root` from `whoami`; the second contained `awx-1` from `hostname`. - **Parity:** `full` - **Not demonstrated:** No claimed impact remains unproved. The proof did not attempt persistence, lateral movement, credential theft, or destructive activity because command execution itself establishes the claimed impact. ## Root Cause AWX's vulnerable `Project.clean_scm_url()` does not reject an `scm_url` whose first character is `-`. The API therefore stores input such as `--upload-pack=whoami>MARKER;:x` as if it were a normal Git URL. A ProjectUpdate passes that value into the Ansible Git path, which invokes `git ls-remote` without terminating option processing before the attacker-controlled positional value. Git interprets `--upload-pack=...` as its upload-pack option and uses the supplied string as a shell command. The `:x` suffix supplies the trailing argument shape needed by the resulting invocation. The effective remediation tested in this run rejects leading-dash values in `Project.clean_scm_url()` with `ValidationError(_('SCM URL must not begin with a dash.'))`. With that validation active, both same-class control requests failed closed with HTTP 400 and no marker. The ticket identifies the downstream fix as RHSA-2026:71115; an upstream fix commit was not supplied, so the reproduction applies the precise validation change as a runtime negative control. ## Reproduction Steps 1. Run `bundle/repro/reproduction_steps.sh` from any directory. The script derives its bundle path portably and uses the prepared project cache when available. 2. The script checks out exact AWX commit `a5b2179a3e693dd8e9f4c186636d93f9a436ada4`, starts the real upstream AWX Docker Compose deployment, waits for the HTTPS API, configures a digest-bound minimal control-plane execution environment, and authenticates to the API. 3. It creates two distinct Git Projects whose `scm_url` values begin with attacker-controlled `--upload-pack=` options, launches each ProjectUpdate through `POST /api/v2/projects//update/`, and verifies unique command-output marker files in the real ProjectUpdate target path. 4. It then applies the leading-dash validation remediation, reloads API workers, and submits two fresh same-class requests. Both controls must return HTTP 400 and produce no marker. 5. Expected terminal output includes two `Vulnerable attempt` lines, two `Fixed attempt` lines, and `CONFIRMED CVE-2026-84502 through the real AWX API and ProjectUpdate task path`. ## Evidence The latest successful current-run proof is under `bundle/repro/proof-20260924T032301Z-28737/` and is bound by `bundle/repro/runtime_manifest.json`. Key artifacts include: - `vulnerable-1-create-request.json` / `vulnerable-1-create-response.json`: API acceptance of the first dash-prefixed SCM URL with HTTP 201. - `vulnerable-1-update-response.json` and `vulnerable-1-update-status.txt`: real ProjectUpdate launch with HTTP 202. - `vulnerable-1-stdout.txt`: ProjectUpdate output showing `git ls-remote` with the injected `--upload-pack` value. - `vulnerable-1-marker.txt`: target-local output `root` from attacker-selected `whoami`. - `vulnerable-2-marker.txt`: target-local output `awx-1` from attacker-selected `hostname`. - `fixed-1-create-response.json` and `fixed-2-create-response.json`: `{"scm_url":["SCM URL must not begin with a dash."]}` with HTTP 400. - `fixed-1-marker-negative-control.txt` and `fixed-2-marker-negative-control.txt`: marker absence recorded after the fixed requests reached the API. - `target-commit.txt`, `target-digest.txt`, `awx-image-digest.txt`, `ee-image-digest.txt`, and `controller-inspect.json`: immutable source and runtime identity evidence. - `bundle/logs/reproduction_steps.log`: diagnostic transcript. Its latest run records API health, both vulnerable successes, both fixed rejections, and final confirmation. Latest transcript excerpts: ```text [+] AWX API healthy [+] Vulnerable attempt 1: create=201 update=202 marker=awxv117902202139203 [+] Vulnerable attempt 2: create=201 update=202 marker=awxv217902202233650 [+] Fixed attempt 1: create=400 marker absent [+] Fixed attempt 2: create=400 marker absent [+] CONFIRMED CVE-2026-84502 through the real AWX API and ProjectUpdate task path ``` The immediately preceding successful proof directory, `bundle/repro/proof-20260924T032109Z-27250/`, independently contains the same two-vulnerable/two-fixed divergence, satisfying consecutive-run repeatability. No sanitizer was used. ## Recommendations / Next Steps 1. Reject Git SCM URLs beginning with `-` before persistence and before any ProjectUpdate scheduling. 2. Where supported, add an explicit `--` option terminator before untrusted positional arguments passed to Git; input validation and safe argument construction should be layered rather than treated as alternatives. 3. Avoid shell command construction for SCM subprocesses and pass a fixed argument vector with `shell=False` semantics. 4. Upgrade affected Automation Controller deployments to the Red Hat release containing RHSA-2026:71115 or a vendor-confirmed later fixed build. 5. Add API and model tests covering leading `-`, encoded or whitespace-adjacent variants, update of an existing Project, and every SCM operation that consumes the URL. 6. Add an end-to-end regression test that proves malicious Project creation/update is rejected before an execution job starts and that no filesystem or callback marker appears. 7. Review ProjectUpdate execution-environment credentials and cluster permissions because successful exploitation occurs inside a control-plane task context. ## Additional Notes - **Idempotency:** The reproducer generated unique Projects and marker names for every attempt and completed successfully in at least two consecutive current-run executions. It restores the vulnerable source after the fixed controls. - **Boundary:** This is a production-path proof through the running AWX HTTPS API and the real ProjectUpdate/ansible-runner/Git path, not a direct parser or Git-only harness. - **Authentication precondition:** Exploitation requires an authenticated user authorized to create a Git Project and trigger its update. - **Environment adaptation:** The script uses a digest-pinned AWX image and a bounded minimal execution environment to support nested container execution. Those adaptations preserve the affected AWX API and ProjectUpdate semantics. - **Artifact size:** Large controller diagnostic output is retained only as non-promoted diagnostics and is not required as proof; compact request, response, stdout, identity, marker, and negative-control artifacts provide the verdict evidence. ## Reproduction Details Reproduced: 2026-09-24T07:20:51.591Z Duration: 4135 seconds Tool calls: 633 Turns: Unknown Handoffs: 3 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00359 pruva-verify CVE-2026-84502 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00359&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00359/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-84502 - Source: https://access.redhat.com/security/cve/CVE-2026-84502 ## Artifacts - bundle/repro/rca_report.md (analysis, 8272 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 19361 bytes) - bundle/repro/proof-20260924T032301Z-28737/awx-image-digest.txt (other, 72 bytes) - bundle/repro/proof-20260924T032301Z-28737/controller-inspect.json (other, 20830 bytes) - bundle/repro/proof-20260924T032301Z-28737/ee-image-digest.txt (other, 65 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-1-create-request.json (other, 143 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-1-create-response.json (other, 51 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-1-create-status.txt (other, 4 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-1-marker-name.txt (other, 20 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-1-marker-negative-control.txt (other, 7 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-2-create-request.json (other, 144 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-2-create-response.json (other, 51 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-2-create-status.txt (other, 4 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-2-marker-name.txt (other, 21 bytes) - bundle/repro/proof-20260924T032301Z-28737/fixed-2-marker-negative-control.txt (other, 7 bytes) - bundle/repro/proof-20260924T032301Z-28737/healthcheck-request.txt (other, 49 bytes) - bundle/repro/proof-20260924T032301Z-28737/healthcheck-response.json (other, 859 bytes) - bundle/repro/proof-20260924T032301Z-28737/runtime-configuration.txt (other, 105 bytes) - bundle/repro/proof-20260924T032301Z-28737/service-start-command.txt (other, 141 bytes) - bundle/repro/proof-20260924T032301Z-28737/target-commit.txt (other, 41 bytes) - bundle/repro/proof-20260924T032301Z-28737/target-digest.txt (other, 65 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-1-create-response.json (other, 2829 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-1-create-status.txt (other, 4 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-1-marker-name.txt (other, 20 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-1-update-detail.json (other, 3892 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-1-update-request.json (other, 3 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-1-update-status.txt (other, 4 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-create-request.json (other, 144 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-create-response.json (other, 2831 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-create-status.txt (other, 4 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-marker-name.txt (other, 20 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-stdout.txt (other, 1545 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-update-detail.json (other, 3894 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-update-request.json (other, 3 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-update-response.json (other, 2013 bytes) - bundle/repro/proof-20260924T032301Z-28737/vulnerable-2-update-status.txt (other, 4 bytes) - bundle/repro/runtime_manifest.json (other, 9361 bytes) - bundle/repro/validation_verdict.json (other, 1367 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00359 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00359/artifacts/bundle/repro/reproduction_steps.sh - Web: https://www.pruva.dev/reproductions/REPRO-2026-00359 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://www.pruva.dev