Summary
smtp's Attachment.to_string interpolates filename and cid straight into MIME headers with no sanitisation. A filename containing CRLF injects arbitrary headers into the outgoing message, or terminates the DATA transaction early.
Details
vlib/net/smtp/smtp.v:996-1004:
fn (a &Attachment) to_string() string {
crlf := '\r\n'
cid := if a.cid != '' {
'Content-ID: <${a.cid}>${crlf}'
} else {
''
}
return 'Content-Type: application/octet-stream${crlf}${cid}Content-Transfer-Encoding: base64${crlf}Content-Disposition: attachment; filename="${a.filename}"${crlf}${crlf}${fold_base64(base64.encode(a.bytes))}'
}
This text is placed verbatim into the SMTP DATA payload.
The module is explicitly aware of this threat elsewhere:
smtp.v:355 strips CRLF with the comment "preventing header or protocol injection when the value is later placed into SMTP DATA"
Mail.subject is RFC2047-encoded when it contains control bytes (smtp.v:606)
Attachment.filename and Attachment.cid get neither treatment. Consequences:
- A
" in the filename terminates the quoted-string early and injects new parameters.
- A
\r\n in the filename injects arbitrary headers, for example a Bcc:.
- A
\r\n.\r\n terminates the DATA transaction early, after which the remainder of the attachment name is interpreted as further SMTP commands by the server.
The attacker-controlled value is the attachment filename, which in a web application is typically a user-supplied upload name.
Reproducer
import net.smtp
fn main() {
mut c := smtp.new_client()
c.connect('localhost', 25) or { panic(err) }
c.send(smtp.Mail{
from: 'a@b.c'
to: 'x@y.z'
body: 'hi'
attachments: [smtp.Attachment{
filename: 'x"\r\nBcc: victim@example.com\r\nX'
bytes: [1]
}]
})
}
The sent DATA contains:
Content-Disposition: attachment; filename="x"
Bcc: victim@example.com
X"
Suggested fix
Reject or strip CR and LF in both fields, and escape " and \ inside the quoted-string:
fn sanitize_filename(s string) string {
s.replace('\r', '').replace('\n', '').replace('\\', '\\\\').replace('"', '\\"')
}
Rejecting is better than stripping: a filename containing a newline is not a legitimate filename. A guard at the top of Attachment.to_string (or in Mail.send) that returns an error for \r or \n in filename/cid matches what the rest of the module already does.
Environment
- V 0.5.2, commit
a60fb589
- Verified byte-identical against current
master (890d55b9)
- Reported by reading
vlib/net/smtp/smtp.v; the header assembly was traced by hand and is directly reproducible against any SMTP server
Summary
smtp'sAttachment.to_stringinterpolatesfilenameandcidstraight into MIME headers with no sanitisation. A filename containing CRLF injects arbitrary headers into the outgoing message, or terminates theDATAtransaction early.Details
vlib/net/smtp/smtp.v:996-1004:This text is placed verbatim into the SMTP
DATApayload.The module is explicitly aware of this threat elsewhere:
smtp.v:355strips CRLF with the comment "preventing header or protocol injection when the value is later placed into SMTP DATA"Mail.subjectis RFC2047-encoded when it contains control bytes (smtp.v:606)Attachment.filenameandAttachment.cidget neither treatment. Consequences:"in the filename terminates the quoted-string early and injects new parameters.\r\nin the filename injects arbitrary headers, for example aBcc:.\r\n.\r\nterminates theDATAtransaction early, after which the remainder of the attachment name is interpreted as further SMTP commands by the server.The attacker-controlled value is the attachment filename, which in a web application is typically a user-supplied upload name.
Reproducer
The sent
DATAcontains:Suggested fix
Reject or strip CR and LF in both fields, and escape
"and\inside the quoted-string:Rejecting is better than stripping: a filename containing a newline is not a legitimate filename. A guard at the top of
Attachment.to_string(or inMail.send) that returns an error for\ror\ninfilename/cidmatches what the rest of the module already does.Environment
a60fb589master(890d55b9)vlib/net/smtp/smtp.v; the header assembly was traced by hand and is directly reproducible against any SMTP server