Nextcloud · Report #3913012
Persistent SMTP header injection via identity `organization` / `name`
How it was found
Vulnerability Summary
program/actions/settings/identity_save.php:49-52 copies every identity column
straight from the POST body. The handler validates only email, reply-to and
bcc (via rcube_utils::check_email() at
program/actions/settings/identity_save.php:82-97); name and organization
are never validated and never CRLF-stripped. rcube_charset::clean() — the only
transformation get_input_string() applies — preserves CR and LF verbatim.
The stored value is read back on every send and assigned unconditionally at
program/include/rcmail_sendmail.php:216-218
($headers['Organization'] = $identity_arr['organization'];). Organization is
an unstructured header, and Mail_mimePart::encodeHeader() short-circuits
that case: when the value is pure ASCII — its non-ASCII probe
preg_match('#([^\s\x21-\x7E]){1}#') treats CR and LF as \s — and
strlen($name.': '.$value) <= 78, it returns the value with no folding and no
RFC2047 encoding at all. The raw CRLF reaches the wire as a header separator
without Net_SMTP::quotedata() even needing to normalise anything.
Steps to Reproduce
- Log in to Roundcube as any user.
- Save an identity —
POST /?_task=settings&_action=save-identitywith a valid_tokenand:
PHP percent-decodes this into real CR/LF bytes._organization=Acme%0D%0ABcc:%[email protected]%0D%0AFrom:%[email protected] program/actions/settings/identity_save.php:49-52copies_organizationinto$save_data['organization']with no filtering; the email validation loop atprogram/actions/settings/identity_save.php:82-97never inspects it.rcube_user::update_identity()persists the value byte-for-byte through a parameterised UPDATE.- Compose and send any message using that identity —
POST /?_task=mail&_action=send. program/include/rcmail_sendmail.php:148→:652-664reads the identity row back;program/include/rcmail_sendmail.php:216-218assigns it to$headers['Organization'].program/lib/Roundcube/rcube.php:1802serialises the headers throughMail_mimePart::encodeHeader(), whose unstructured short-ASCII branch applies no transformation;program/lib/Roundcube/rcube_smtp.php:321writes them to the wire.- Run the executable proof against the real repository and vendor code:
Result: PASS. On-the-wire output:php roundcubemail-master_VULNHUNT_RESULTS_2026-08-03-095945/exploit_tests/test_vuln_002_smtp_header_injection_identity.phpOrganization: Acme\r\n Bcc: [email protected]\r\n From: [email protected]\r\n
Impact
Every message the account sends silently carries attacker-authored headers until
the identity is edited — covert Bcc recipients, forged From:/Sender:, and
forged trust signals consumed by downstream MTAs and spam filters, all emitted
with the server's SMTP submission credentials.
The most damaging case is a deployment that sets identities_level to 1 or 3
specifically to stop users changing their sender address. That control unsets
only email (program/actions/settings/identity_save.php:69-71), leaving
organization writable — so the user injects their own From: header and
defeats the pinning outright.