HexBrain

Nextcloud · Report #3913012

Persistent SMTP header injection via identity `organization` / `name`

Validstatus
19upvotes
18 Sept 2026disclosed

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

  1. Log in to Roundcube as any user.
  2. Save an identity — POST /?_task=settings&_action=save-identity with a valid _token and:
    _organization=Acme%0D%0ABcc:%[email protected]%0D%0AFrom:%[email protected]
    
    PHP percent-decodes this into real CR/LF bytes.
  3. program/actions/settings/identity_save.php:49-52 copies _organization into $save_data['organization'] with no filtering; the email validation loop at program/actions/settings/identity_save.php:82-97 never inspects it.
  4. rcube_user::update_identity() persists the value byte-for-byte through a parameterised UPDATE.
  5. Compose and send any message using that identity — POST /?_task=mail&_action=send.
  6. program/include/rcmail_sendmail.php:148 → :652-664 reads the identity row back; program/include/rcmail_sendmail.php:216-218 assigns it to $headers['Organization'].
  7. program/lib/Roundcube/rcube.php:1802 serialises the headers through Mail_mimePart::encodeHeader(), whose unstructured short-ASCII branch applies no transformation; program/lib/Roundcube/rcube_smtp.php:321 writes them to the wire.
  8. Run the executable proof against the real repository and vendor code:
    php roundcubemail-master_VULNHUNT_RESULTS_2026-08-03-095945/exploit_tests/test_vuln_002_smtp_header_injection_identity.php
    
    Result: PASS. On-the-wire output:
    Organization: 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.

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