mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
SMTP: No state update for bad BDAT commands
OSS-Fuzz found that providing an invalid BDAT line would tickle an assert in UpdateState(). The BDAT state was never initialized, but within UpdateState() that was expected. This also removes the AnalyzerViolation() call for bad BDAT commands and instead raises a weird. The SMTP analyzer is very lax and not triggering the violation allows to parse the server's response to such an invalid command. PCAP files produced by a custom Python SMTP client against Postfix.
This commit is contained in:
parent
5ad11e00e3
commit
ae2a5c83a4
7 changed files with 134 additions and 25 deletions
|
@ -263,31 +263,12 @@ void SMTP_Analyzer::ProcessLine(int length, const char* line, bool orig) {
|
|||
RequestEvent(cmd_len, cmd, data_len, line);
|
||||
}
|
||||
|
||||
// For the BDAT command, parse out the chunk-size from the line
|
||||
// and switch the ContentLineAnalyzer into plain delivery mode
|
||||
// assuming things look valid.
|
||||
if ( cmd_code == detail::SMTP_CMD_BDAT ) {
|
||||
const auto [chunk_size, is_last_chunk, error] = detail::parse_bdat_arg(end_of_line - line, line);
|
||||
if ( ! error ) {
|
||||
assert(chunk_size >= 0);
|
||||
auto* cl = orig ? cl_orig : cl_resp;
|
||||
cl->SetPlainDelivery(chunk_size);
|
||||
// See above, might have already done so.
|
||||
bool do_update_state = cmd_code != detail::SMTP_CMD_END_OF_DATA;
|
||||
|
||||
if ( ! bdat ) {
|
||||
assert(! mail);
|
||||
// This is the first BDAT chunk.
|
||||
BeginData(orig);
|
||||
bdat = std::make_unique<detail::SMTP_BDAT_Analyzer>(Conn(), mail,
|
||||
zeek::BifConst::SMTP::bdat_max_line_length);
|
||||
}
|
||||
|
||||
bdat->NextChunk(is_last_chunk ? detail::ChunkType::Last : detail::ChunkType::Intermediate,
|
||||
chunk_size);
|
||||
}
|
||||
else {
|
||||
AnalyzerViolation(error, line, length);
|
||||
}
|
||||
}
|
||||
if ( cmd_code == detail::SMTP_CMD_BDAT )
|
||||
// Do not update state if this isn't a valid BDAT command.
|
||||
do_update_state = ProcessBdatArg(end_of_line - line, line, orig);
|
||||
else if ( bdat ) {
|
||||
// Non-BDAT command from client but still have BDAT state,
|
||||
// close it out. This can happen when a client started to
|
||||
|
@ -297,7 +278,7 @@ void SMTP_Analyzer::ProcessLine(int length, const char* line, bool orig) {
|
|||
EndData();
|
||||
}
|
||||
|
||||
if ( cmd_code != detail::SMTP_CMD_END_OF_DATA )
|
||||
if ( do_update_state )
|
||||
UpdateState(cmd_code, 0, orig);
|
||||
}
|
||||
}
|
||||
|
@ -866,6 +847,31 @@ void SMTP_Analyzer::UnexpectedReply(int cmd_code, int reply_code) {
|
|||
|
||||
void SMTP_Analyzer::ProcessData(int length, const char* line) { mail->Deliver(length, line, true /* trailing_CRLF */); }
|
||||
|
||||
bool SMTP_Analyzer::ProcessBdatArg(int arg_len, const char* arg, bool orig) {
|
||||
// For the BDAT command, parse out the chunk-size from the line
|
||||
// and switch the ContentLineAnalyzer into plain delivery mode
|
||||
// assuming things look valid.
|
||||
const auto [chunk_size, is_last_chunk, error] = detail::parse_bdat_arg(arg_len, arg);
|
||||
if ( error ) {
|
||||
Weird("smtp_invalid_bdat_command", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* cl = orig ? cl_orig : cl_resp;
|
||||
cl->SetPlainDelivery(chunk_size);
|
||||
|
||||
if ( ! bdat ) {
|
||||
// This is the first BDAT chunk.
|
||||
BeginData(orig);
|
||||
bdat = std::make_unique<detail::SMTP_BDAT_Analyzer>(Conn(), mail, zeek::BifConst::SMTP::bdat_max_line_length);
|
||||
}
|
||||
|
||||
bdat->NextChunk(is_last_chunk ? detail::ChunkType::Last : detail::ChunkType::Intermediate, chunk_size);
|
||||
|
||||
// All good.
|
||||
return true;
|
||||
}
|
||||
|
||||
void SMTP_Analyzer::BeginData(bool orig) {
|
||||
state = detail::SMTP_IN_DATA;
|
||||
skip_data = false; // reset the flag at the beginning of the mail
|
||||
|
|
|
@ -59,6 +59,7 @@ protected:
|
|||
void NewReply(int reply_code, bool orig);
|
||||
void ProcessExtension(int ext_len, const char* ext);
|
||||
void ProcessData(int length, const char* line);
|
||||
bool ProcessBdatArg(int arg_len, const char* arg, bool orig);
|
||||
|
||||
void UpdateState(int cmd_code, int reply_code, bool orig);
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 220, >, example.com ESMTP Postfix (Debian/GNU)
|
||||
smtp_request, CHhAvVGS1DHFjwGM9, T, EHLO, localhost
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, example.com
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, PIPELINING
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, SIZE 10240000
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, ETRN
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, STARTTLS
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, ENHANCEDSTATUSCODES
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, 8BITMIME
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, DSN
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, SMTPUTF8
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, EHLO, CHUNKING
|
||||
smtp_request, CHhAvVGS1DHFjwGM9, T, MAIL, FROM:<zeek@localhost>
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, MAIL, 2.1.0 Ok
|
||||
smtp_request, CHhAvVGS1DHFjwGM9, T, RCPT, TO:<root@localhost>
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 250, RCPT, 2.1.5 Ok
|
||||
smtp_request, CHhAvVGS1DHFjwGM9, T, BDAT,
|
||||
smtp_reply, CHhAvVGS1DHFjwGM9, F, 521, BDAT, 5.5.4 Syntax: BDAT count [LAST]
|
||||
smtp_request, CHhAvVGS1DHFjwGM9, T, QUIT,
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 220, >, example.com ESMTP Postfix (Debian/GNU)
|
||||
smtp_request, ClEkJM2Vm5giqnMf4h, T, EHLO, localhost
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, example.com
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, PIPELINING
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, SIZE 10240000
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, ETRN
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, STARTTLS
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, ENHANCEDSTATUSCODES
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, 8BITMIME
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, DSN
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, SMTPUTF8
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, EHLO, CHUNKING
|
||||
smtp_request, ClEkJM2Vm5giqnMf4h, T, MAIL, FROM:<zeek@localhost>
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, MAIL, 2.1.0 Ok
|
||||
smtp_request, ClEkJM2Vm5giqnMf4h, T, RCPT, TO:<root@localhost>
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 250, RCPT, 2.1.5 Ok
|
||||
smtp_request, ClEkJM2Vm5giqnMf4h, T, BDAT, 1234 SCRAMBLE
|
||||
smtp_reply, ClEkJM2Vm5giqnMf4h, F, 521, BDAT, 5.5.4 Syntax: BDAT count [LAST]
|
||||
smtp_request, ClEkJM2Vm5giqnMf4h, T, QUIT,
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 220, >, example.com ESMTP Postfix (Debian/GNU)
|
||||
smtp_request, C4J4Th3PJpwUYZZ6gc, T, EHLO, localhost
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, example.com
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, PIPELINING
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, SIZE 10240000
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, ETRN
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, STARTTLS
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, ENHANCEDSTATUSCODES
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, 8BITMIME
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, DSN
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, SMTPUTF8
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, EHLO, CHUNKING
|
||||
smtp_request, C4J4Th3PJpwUYZZ6gc, T, MAIL, FROM:<zeek@localhost>
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, MAIL, 2.1.0 Ok
|
||||
smtp_request, C4J4Th3PJpwUYZZ6gc, T, RCPT, TO:<root@localhost>
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 250, RCPT, 2.1.5 Ok
|
||||
smtp_request, C4J4Th3PJpwUYZZ6gc, T, BDAT, SCRAMBLE
|
||||
smtp_reply, C4J4Th3PJpwUYZZ6gc, F, 521, BDAT, 5.5.4 Syntax: BDAT count [LAST]
|
||||
smtp_request, C4J4Th3PJpwUYZZ6gc, T, QUIT,
|
|
@ -0,0 +1,13 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
#separator \x09
|
||||
#set_separator ,
|
||||
#empty_field (empty)
|
||||
#unset_field -
|
||||
#path smtp
|
||||
#open XXXX-XX-XX-XX-XX-XX
|
||||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p trans_depth helo mailfrom rcptto date from to cc reply_to msg_id in_reply_to subject x_originating_ip first_received second_received last_reply path user_agent tls fuids
|
||||
#types time string addr port addr port count string string set[string] string string set[string] set[string] string string string string addr string string string vector[addr] string bool vector[string]
|
||||
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33472 127.0.0.1 25 1 localhost zeek@localhost root@localhost - - - - - - - - - - - 521 5.5.4 Syntax: BDAT count [LAST] 127.0.0.1,127.0.0.1 - F (empty)
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 52364 127.0.0.1 25 1 localhost zeek@localhost root@localhost - - - - - - - - - - - 521 5.5.4 Syntax: BDAT count [LAST] 127.0.0.1,127.0.0.1 - F (empty)
|
||||
XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 46862 127.0.0.1 25 1 localhost zeek@localhost root@localhost - - - - - - - - - - - 521 5.5.4 Syntax: BDAT count [LAST] 127.0.0.1,127.0.0.1 - F (empty)
|
||||
#close XXXX-XX-XX-XX-XX-XX
|
|
@ -0,0 +1,13 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
#separator \x09
|
||||
#set_separator ,
|
||||
#empty_field (empty)
|
||||
#unset_field -
|
||||
#path weird
|
||||
#open XXXX-XX-XX-XX-XX-XX
|
||||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p name addl notice peer source
|
||||
#types time string addr port addr port string string bool string string
|
||||
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33472 127.0.0.1 25 smtp_invalid_bdat_command BDAT not followed by a valid chunk-size F zeek SMTP
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 52364 127.0.0.1 25 smtp_invalid_bdat_command BDAT chunk-size followed by junk F zeek SMTP
|
||||
XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 46862 127.0.0.1 25 smtp_invalid_bdat_command BDAT not followed by a valid chunk-size F zeek SMTP
|
||||
#close XXXX-XX-XX-XX-XX-XX
|
BIN
testing/btest/Traces/smtp/smtp-bdat-cmd-invalid.pcap
Normal file
BIN
testing/btest/Traces/smtp/smtp-bdat-cmd-invalid.pcap
Normal file
Binary file not shown.
|
@ -0,0 +1,18 @@
|
|||
# @TEST-DOC: Test invalid BDAT lines. Pcaps generated with a Python client against Postfix.
|
||||
#
|
||||
# @TEST-EXEC: zeek -b -r $TRACES/smtp/smtp-bdat-cmd-invalid.pcap %INPUT >out
|
||||
# @TEST-EXEC: btest-diff smtp.log
|
||||
# @TEST-EXEC: btest-diff weird.log
|
||||
# @TEST-EXEC: btest-diff out
|
||||
|
||||
@load base/protocols/conn
|
||||
@load base/protocols/smtp
|
||||
|
||||
event smtp_request(c: connection, is_orig: bool, command: string, arg: string) {
|
||||
print "smtp_request", c$uid, is_orig, command, arg;
|
||||
}
|
||||
|
||||
event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string,
|
||||
msg: string, cont_resp: bool) {
|
||||
print "smtp_reply", c$uid, is_orig, code, cmd, msg;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue