Bug 2527189 (CVE-2026-84707)

Summary: CVE-2026-84707 automation-controller: automation-controller-container: automation-controller: host_filter SmartFilter ORM traversal exposes JobEvent/AdHocCommandEvent event_data and stdout to users without permission on the job, enabling blind charact ...
Product: [Other] Security Response Reporter: Thomas Eagle <teagle>
Component: vulnerabilityAssignee: Product Security <prodsec-ir-bot>
Status: NEW --- QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: dschmidt, ikhan, jlanda, kshier, rbobbitt, security-response-team, simaishi, stcannon, teagle, yguenane
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: ---
Doc Text:
A flaw was found in Red Hat Ansible Automation Platform's automation-controller. The host_filter query parameter on the hosts list API is parsed into a raw Django ORM filter whose lookup path is validated only against a sensitive-field blocklist, with no authorization check on the database relations it traverses. Because job event and ad-hoc command output fields are not on that blocklist, an authenticated user holding only the Read role on an inventory can construct filters that traverse into the output of jobs they have no permission to view and use the returned host count as a boolean oracle. Using regular-expression lookups, the attacker can extract, character by character, the output (which routinely contains plaintext credentials, tokens, and command results) of jobs and ad-hoc commands belonging to other organizations, resulting in cross-tenant disclosure of job output
Story Points: ---
Clone Of: Environment:
Last Closed: Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Deadline: 2026-10-01   

Description Thomas Eagle 2026-09-02 00:14:52 UTC
A flaw was found in automation-controller (AWX). GET /api/controller/v2/hosts/
accepts a host_filter query parameter that HostList.get_queryset (awx/api/views/
__init__.py:1938-1944) parses with SmartFilter.query_from_string and intersects
with the RBAC-filtered host queryset via `qs &= filter_qs`; that intersection
restricts only which Host rows are returned, not which related rows the lookup's
JOINs traverse. SmartFilter.BoolOperand (awx/main/utils/filters.py:151-170)
validates the user-supplied lookup PATH only by calling FieldLookupBackend().
get_field_from_lookup(Host, k) and then executes Host.objects.filter(**{k: v})
directly, including __regex and __icontains lookups. FieldLookupBackend (django-
ansible-base ansible_base/rest_filters/utils.py:9-69) rejects only fields in
PASSWORD_FIELDS/encrypted_fields, fields marked __prevent_search__, and traversal
loops -- it performs no authorization check on the rows the relation traversal
reaches. JobEvent.event_data/stdout and AdHocCommandEvent.event_data/stdout
(awx/main/models/events.py:215,253,695,705) are not wrapped in prevent_search(),
unlike extra_vars, job_env, job_args and survey_passwords which are. Consequently a
user with only the Read role on an inventory can submit host_filter values such as
last_job__job_events__event_data__regex=<pattern>,
job_events_as_primary_host__stdout__icontains=<substring>, or
ad_hoc_command_events__event_data__regex=<pattern> and read the returned host count
as a one-bit oracle. Since __regex is accepted, this allows efficient character-by-
character extraction of the event_data/stdout of jobs and ad-hoc commands the
caller is explicitly denied (HTTP 403) via the direct API, for any job that ever
ran against a host in a readable inventory -- including jobs in other organizations
when a host spans inventories across organizations. Job output routinely contains
plaintext credentials, API tokens, command output and host facts. This is a
horizontal privilege escalation from inventory-read to read-arbitrary-job-output.
Discovered internally (pentest-aap); verified live on AAP 2.7 / automation-
controller 4.8.1 (the forbidden job's playbook_uuid was extracted exactly); still
present on devel.
    Upstream: github.com/ansible/awx (awx/api/views/__init__.py HostList.get_queryset;
              awx/main/utils/filters.py SmartFilter.BoolOperand; sensitive fields in
              awx/main/models/events.py) and github.com/ansible/django-ansible-base
              (ansible_base/rest_filters/utils.py get_fields_from_path).

Comment 2 Jon Orris 2026-09-23 20:51:23 UTC
This issue has been addressed in the following products:

  Red Hat Ansible Automation Platform 2.6 for RHEL 10
  Red Hat Ansible Automation Platform 2.6 for RHEL 9

Via RHSA-2026:71113 https://access.redhat.com/errata/RHSA-2026:71113

Comment 3 Jon Orris 2026-09-23 21:07:58 UTC
This issue has been addressed in the following products:

  Red Hat Ansible Automation Platform 2.5 for RHEL 9
  Red Hat Ansible Automation Platform 2.5 for RHEL 8

Via RHSA-2026:71114 https://access.redhat.com/errata/RHSA-2026:71114