Merge remote-tracking branch 'security/topic/awelzel/125-ftp-timeout-three'

* security/topic/awelzel/125-ftp-timeout-three:
  testing/ftp: Add tests and pcaps with invalid reply lines
  ftp: Harden reply handing a bit and don't raise bad replies to script-land
  ftp: ignore invalid commands
This commit is contained in:
Tim Wojtulewicz 2023-02-01 10:47:21 -07:00
commit 9a0dc30e35
18 changed files with 203 additions and 11 deletions

View file

@ -63,6 +63,20 @@ static uint32_t get_reply_code(int len, const char* line)
return 0;
}
// The minimal length of an FTP command is 3 characters (PWD, MKD,
// RMD, ...) and should only contain printable ascii.
static bool is_ftp_cmd(int len, const char* s)
{
if ( len < 3 )
return false;
for ( int i = 0; i < len; i++ )
if ( ! isprint(s[i]) || isspace(s[i]) )
return false;
return true;
}
void FTP_Analyzer::DeliverStream(int length, const u_char* data, bool orig)
{
analyzer::tcp::TCP_ApplicationAnalyzer::DeliverStream(length, data, orig);
@ -91,10 +105,13 @@ void FTP_Analyzer::DeliverStream(int length, const u_char* data, bool orig)
util::get_word(end_of_line - line, line, cmd_len, cmd);
line = util::skip_whitespace(line + cmd_len, end_of_line);
if ( cmd_len == 0 )
if ( ! is_ftp_cmd(cmd_len, cmd) )
{
// Weird("FTP command missing", end_of_line - orig_line, orig_line);
cmd_str = new StringVal("<missing>");
if ( AnalyzerConfirmed() )
Weird("FTP_invalid_command");
// Ignore the whole line.
return;
}
else if ( BifConst::FTP::max_command_length > 0 &&
static_cast<zeek_uint_t>(cmd_len) > BifConst::FTP::max_command_length )
@ -149,25 +166,39 @@ void FTP_Analyzer::DeliverStream(int length, const u_char* data, bool orig)
}
else
{ // a new reply
if ( reply_code > 0 && length > 3 && line[3] == '-' )
cont_resp = 0;
if ( reply_code == 0 )
{
AnalyzerViolation("non-numeric reply code", (const char*)data, length);
return;
}
else if ( reply_code < 100 )
{
AnalyzerViolation("invalid reply code", (const char*)data, length);
return;
}
else if ( length > 3 && line[3] == '-' )
{ // a continued reply
pending_reply = reply_code;
line = util::skip_whitespace(line + 4, end_of_line);
cont_resp = 1;
}
else if ( length > 3 && line[3] != ' ' )
{
// This is a proper reply code, but there's no space after
// the reply code even though the line is long enough.
AnalyzerViolation("invalid reply line", (const char*)data, length);
return;
}
else
{ // a self-contained reply
if ( reply_code > 0 )
line += 3;
else
AnalyzerViolation("non-numeric reply code", (const char*)data, length);
line += 3;
if ( line < end_of_line )
line = util::skip_whitespace(line, end_of_line);
else
line = end_of_line;
cont_resp = 0;
}
}