Skip to content

Reject CR or LF in IMAP commands and arguments - #414

Open
Lyin-OTTF wants to merge 1 commit into
apache:masterfrom
Lyin-OTTF:fix/imap-crlf-guard
Open

Lyin-OTTF wants to merge 1 commit into
apache:masterfrom
Lyin-OTTF:fix/imap-crlf-guard

Conversation

@Lyin-OTTF

Copy link
Copy Markdown

IMAP.sendCommandWithID() concatenates the command ID, command and arguments into one control line terminated by CRLF, without checking the inputs. FTP.sendCommand(), POP3.sendCommand(), NNTP.sendCommand() and SMTP.sendCommand() all reject CR or LF for exactly this reason, so on IMAP a call like

imap.select("INBOX\r\nA001 DELETE INBOX");

writes two lines to the control connection and executes an attacker-chosen second command. Mailbox names are one realistic carrier (they regularly come from user configuration), and IMAP.quoteMailboxName() already rejects CR/LF - but that only covers arguments routed through it, while LOGIN credentials (via AuthenticatingIMAPClient.login) and any direct sendCommand() call bypass it.

The command layer is the last line of defense and was the only one of the five protocols missing the guard. The check mirrors the wording and placement of the POP3/NNTP/SMTP guards.

IMAPTest gains four cases covering CR, LF and CRLF in the argument and CR/CRLF in the command name. On an unconnected client they fail with NullPointerException before this change and throw IllegalArgumentException with it.

Verification: Windows, JDK 21. mvn test -Dtest=IMAPTest passes (19/19 with the fix); reverting only IMAP.java fails exactly the four new cases.

IMAP.sendCommandWithID() concatenates the command ID, command and arguments
into one control line terminated by CRLF, without checking the inputs.
FTP.sendCommand(), POP3.sendCommand(), NNTP.sendCommand() and
SMTP.sendCommand() all reject CR or LF for exactly this reason, so an IMAP
command such as

    imap.select("INBOX\r\nA001 DELETE INBOX")

writes two lines to the control connection and executes an attacker-chosen
second command. IMAP.quoteMailboxName() already rejects CR/LF, but that only
covers the arguments that go through it - LOGIN credentials and any direct
sendCommand() call bypass it. The command layer is the last line of defense
and was the only one of the five protocols missing the guard.

The check mirrors the wording and placement of the POP3/NNTP/SMTP guards.
IMAPTest gains four cases covering CR, LF and CRLF in the argument and CR/CRLF
in the command name; they fail with NPE on an unconnected client before this
change and throw IllegalArgumentException with it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant