HexBrain

Nextcloud · Report #3913007

SMTP header injection via C-escape `\r` in the recipient display name

Validstatus
23upvotes
18 Oct 2026disclosed

How it was found

Vulnerability Summary

Roundcube strips CR/LF from address input in rcmail_sendmail::email_input_format() (program/include/rcmail_sendmail.php:760) and then, twenty-five lines later, calls stripcslashes() on the recipient's display name at program/include/rcmail_sendmail.php:790. An authenticated user who submits the two-character sequence \r inside a quoted display name passes the strip regexp untouched — there is no control character in the input yet — and stripcslashes() converts it into a real 0x0D after the sanitizer has run.

Nothing downstream removes it. format_email_recipient() (program/lib/Roundcube/bootstrap.php:329-333) escapes only ". Mail_mimePart::encodeHeader() folds only /\r?\n[\s\t]*/, so a lone CR is never matched, and its non-ASCII test preg_match('#([^\s\x21-\x7E]){1}#') classifies CR as whitespace, so no RFC2047 encoding is applied either. Finally Net_SMTP::quotedata() (vendor/pear/net_smtp/Net/SMTP.php:1489) normalises every lone CR into a full CRLF, turning the injected text into real header lines on the wire.

Steps to Reproduce

  1. Log in to Roundcube as any user.
  2. Send POST /?_task=mail&_action=send with a valid _token and the body parameter — note \r is backslash followed by the letter r, not a control character:
    _to="x\rBcc: [email protected]\rSender: [email protected]\rX-Pad: p" <[email protected]>
    
  3. program/include/rcmail_sendmail.php:112 reads the raw value; the CRLF strip at program/include/rcmail_sendmail.php:760 matches nothing.
  4. program/include/rcmail_sendmail.php:790 — $name = stripcslashes($name); materialises three real 0x0D bytes inside the display name.
  5. program/include/rcmail_sendmail.php:202 assigns the result to $headers['To']; :384 hands it to Mail_mime; program/lib/Roundcube/rcube.php:1802 serialises the headers, which leaves the lone CR intact.
  6. program/lib/Roundcube/rcube_smtp.php:321 calls Net_SMTP::data(), whose quotedata() promotes each CR to CRLF.
  7. Run the executable proof, which exercises the real repository and vendor code end to end:
    php roundcubemail-master_VULNHUNT_RESULTS_2026-08-03-095945/exploit_tests/test_vuln_001_smtp_header_injection_recipient.php
    
    Result: PASS. On-the-wire output:
    To: "x\r\n
    Bcc: [email protected]\r\n
    Sender: [email protected]\r\n
    X-Pad: p"\r\n
     <[email protected]>\r\n
    
    Bcc: [email protected] and Sender: [email protected] are asserted to be standalone CRLF-delimited header lines.

Impact

Any authenticated user gains arbitrary RFC-822 header emission on messages relayed with the server's own SMTP submission credentials — a capability the compose form deliberately does not expose (rcmail_sendmail.php:198-269 builds the header array from a fixed key set). Concretely they can:

  • forge From:/Sender:/Reply-To:, bypassing the server-side identity lookup at rcmail_sendmail.php:147-175 and the identities_level pinning enforced at program/actions/settings/identity_save.php:64-79 — sender spoofing from a domain whose SPF/DKIM the mail server legitimately satisfies;
  • add covert Bcc: recipients;
  • forge trust-signal headers (Received:, Authentication-Results:, X-Spam-Status:) that downstream MTAs and spam filters consume;
  • inject an empty line to terminate the header block early and control the raw message body the MTA parses.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original