Summary
The ZAC Node backend exposes an unauthenticated POST /api/send endpoint that passes the request body, including the recipient and the attachments array, straight into nodemailer's sendMail. Because nodemailer reads a {path} attachment from local disk and fetches a {href} attachment from a URL, an unauthenticated attacker can exfiltrate server files by email, relay mail through the configured SMTP server, or trigger server-side requests.
Affected Component
- Package:
openziti/ziti-console(Ziti Admin Console) — Node backend - File:
server.js— the/api/sendhandler (and the related/api/messagehandler) - Sink: attacker-controlled recipient and
attachmentspassed to nodemailersendMail
Vulnerability Class
- Primary: CWE-306 — Missing Authentication for Critical Function
- Secondary: CWE-73 — External Control of File Name or Path (file read); CWE-918 — Server-Side Request Forgery
Attacker Model
An unauthenticated remote attacker with network access to the ZAC Node server. No credentials and no user interaction are required.
Preconditions
ZAC running via the Node backend (server.js) in node-api or classic mode, with an SMTP transporter configured (settings.mail.host set). Deployments that serve only the static Angular application, or that have no SMTP configured, are not affected.
Root Cause
/api/send builds the mail from the request body without authentication, taking both the recipient and the raw nodemailer attachments objects from the caller. nodemailer treats a {path:"..."} attachment as a local file to read and a {href:"..."} attachment as a URL to fetch, so the caller controls which file is read (or URL fetched) and where it is delivered.
Impact
Because both the recipient and the attachment objects come from the request, an unauthenticated attacker can abuse the endpoint three ways:
- Read server files via email. A
{path}attachment makes the server read that file (as raw bytes, so any accessible file) and email it to an attacker-chosen address — includingsettings.json(SMTP credentials) and session files holding controller session tokens that can be replayed against the controller. - Open mail relay. The attacker chooses the recipient and content, turning the configured SMTP server into a relay for spam or phishing.
- SSRF. A
{href}attachment makes the server fetch an arbitrary URL from the Node host.
The related POST /api/message endpoint shares the missing authentication but sends only to a fixed recipient and has no attachment sink, so its impact is limited to unauthenticated mail to that fixed address.
- CVSS v3.1: 8.6 (High)
- Vector:
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N
Not impacted: static-only (SPA) deployments, or deployments with no SMTP configured; no remote code execution.
Package
openziti/ziti-console (app-ziti-console, Node backend / node-api mode)
Affected Versions
- app-ziti-console
< 4.5.3
Patched Versions
- app-ziti-console
4.5.3
Remediation
Upgrade to 4.5.3, which removes the unused /api/send and /api/message handlers (the console builds and sends enrollment email content client-side, so they were dead code). If you cannot upgrade: leave SMTP unconfigured (settings.mail.host unset), block POST /api/send and POST /api/message at a reverse proxy, or run ZAC in static-asset (SPA) mode instead of the Node proxy.
References
- GHSA: https://github.com/openziti/ziti-console/security/advisories/GHSA-mwjf-9478-gjmc
- CVE: (pending)
Credits
Reported by dmuensterer and sajdakabir.