Bug 2527220 (CVE-2026-84721)
| Summary: | CVE-2026-84721 automation-controller: automation-controller: Email notification backend allows SSRF via user-controlled SMTP host/port (internal port-scan oracle, SMTP password exfil) | ||
|---|---|---|---|
| Product: | [Other] Security Response | Reporter: | OSIDB Bzimport <bzimport> |
| Component: | vulnerability | Assignee: | Product Security <prodsec-ir-bot> |
| Status: | NEW --- | QA Contact: | |
| Severity: | medium | Docs Contact: | |
| Priority: | medium | ||
| Version: | unspecified | CC: | dschmidt, jlanda, kshier, 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 server-side request forgery flaw was found in the Ansible Automation Platform
automation-controller email notification backend. The email backend passes the user-supplied SMTP
host and port from a notification template directly to the SMTP client without validating that
the target is not an internal, loopback, link-local, or reserved address. An authenticated user
with organization notification-admin permission can create or modify an email notification
template pointing at an arbitrary internal address, trigger a test, and have the controller task
process open a raw TCP connection to that address. The resulting connection error is reflected
back through the notification record, providing a three-state internal port-scan oracle (open,
closed, filtered) over the control-plane's cluster network, including the in-cluster Kubernetes
API. When a shared organization template holds a stored SMTP password, redirecting the host can
also cause that credential to be transmitted to an attacker-controlled server.
|
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 | ||
A flaw was found in the Ansible Automation Platform automation-controller notification subsystem. CustomEmailBackend (awx/main/notifications/email_backend.py:24) is a thin subclass of django.core.mail.backends.smtp.EmailBackend and does not override open()/send_messages() to validate its connect target, so the user-controlled notification_configuration.host and .port reach smtplib.SMTP(host, port, timeout=...) with no allow-list and no private/loopback/ link-local/reserved rejection. An organization-scoped Notification Admin (awx.notification_admin_role, checked by NotificationTemplateAccess.can_add/can_change at awx/main/access.py:2578/2582 — a delegatable, non-superuser role) can create or PATCH an email notification template with host/port pointing at any internal address and POST the template's /test/ endpoint. The dispatcher runs the backend on the controller-task pod, which opens a raw TCP connection to the attacker-chosen host:port. The smtplib exception string is persisted verbatim into Notification.error (a plain TextField, awx/main/models/notifications.py:219) and is readable over the API, producing a three-state oracle: "[Errno 111] Connection refused" (closed), "timed out" (filtered), and "Connection unexpectedly closed: timed out" or an SMTPResponseException (open). Because the transport is a raw socket, the flaw reaches non-HTTP internal services (for example Redis, PostgreSQL, receptor) and the in-cluster Kubernetes API, and can send SMTP-protocol bytes into those listeners. In addition, the SMTP AUTH exchange transmits the template's stored, otherwise write-only password to the configured host, so a user who can change the host on a shared organization template can exfiltrate its stored SMTP password. The HTTP-based notification backends (webhook, grafana, mattermost, rocketchat) were hardened against this on the 2.7 branch via awx/main/notifications/url_validation.py::validate_url(); the email backend (and the IRC backend) were omitted from that hardening. That guard is present only on the 2.7 branch; on the 2.5, 2.6, and development branches it is absent and no notification backend validates its connect target. Upstream: https://github.com/ansible/tower (awx) Affected file: awx/main/notifications/email_backend.py:24 (CustomEmailBackend, no host validation); awx/main/models/notifications.py:219 (Notification.error oracle sink); awx/main/access.py:2559-2582 (notification_admin_role). Guard (2.7 only): awx/main/notifications/url_validation.py::validate_url (added by tower #7875).