From 3f5cb75a2a11279acd1b60bbdb35f95a47ad1089 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Tue, 15 Nov 2022 21:27:53 +0100 Subject: [PATCH] ftp: Introduce FTP::max_command_length oss-fuzz produced FTP traffic with a ~550KB long FTP command. Cap FTP command length at 100 bytes, log a weird if a command is larger than that and move on to the next. Likely it's not actual FTP traffic, but raising an analyzer violation would allow clients an easy way to disable the analyzer by sending an overly long command. The added test PCAP was generated using a fake Python socket server/client. --- scripts/base/frameworks/notice/weird.zeek | 1 + scripts/base/init-bare.zeek | 9 +++++++++ src/analyzer/protocol/ftp/FTP.cc | 11 +++++++++++ src/const.bif | 2 ++ .../conn.log | 11 +++++++++++ .../dpd.log | 11 +++++++++++ .../ftp.log | 15 +++++++++++++++ .../weird.log | 11 +++++++++++ testing/btest/Traces/ftp/fake-long-commands.pcap | Bin 0 -> 3991 bytes .../protocols/ftp/ftp-max-command-length.zeek | 12 ++++++++++++ 10 files changed, 83 insertions(+) create mode 100644 testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/conn.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/dpd.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/ftp.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/weird.log create mode 100644 testing/btest/Traces/ftp/fake-long-commands.pcap create mode 100644 testing/btest/scripts/base/protocols/ftp/ftp-max-command-length.zeek diff --git a/scripts/base/frameworks/notice/weird.zeek b/scripts/base/frameworks/notice/weird.zeek index 9d9a3dfbdd..aa7a412bbd 100644 --- a/scripts/base/frameworks/notice/weird.zeek +++ b/scripts/base/frameworks/notice/weird.zeek @@ -136,6 +136,7 @@ export { ["FIN_advanced_last_seq"] = ACTION_LOG, ["FIN_after_reset"] = ACTION_IGNORE, ["FIN_storm"] = ACTION_NOTICE_PER_ORIG, + ["FTP_max_command_length_exceeded"] = ACTION_LOG_PER_CONN, ["FTP_too_many_pending_commands"] = ACTION_LOG_PER_CONN, ["HTTP_bad_chunk_size"] = ACTION_LOG, ["HTTP_chunked_transfer_for_multipart_message"] = ACTION_LOG, diff --git a/scripts/base/init-bare.zeek b/scripts/base/init-bare.zeek index c1f73949cc..63bbd532ad 100644 --- a/scripts/base/init-bare.zeek +++ b/scripts/base/init-bare.zeek @@ -337,6 +337,15 @@ type ftp_port: record { valid: bool; ##< True if format was right. Only then are *h* and *p* valid. }; + +module FTP; + +## Limits the size of commands accepted by the FTP analyzer. Longer commands +## raise a FTP_max_command_length_exceeded weird and are discarded. +const max_command_length = 100 &redef; + +module GLOBAL; + ## Statistics about what a TCP endpoint sent. ## ## .. zeek:see:: conn_stats diff --git a/src/analyzer/protocol/ftp/FTP.cc b/src/analyzer/protocol/ftp/FTP.cc index 1142e092ff..bfdd42da34 100644 --- a/src/analyzer/protocol/ftp/FTP.cc +++ b/src/analyzer/protocol/ftp/FTP.cc @@ -96,6 +96,17 @@ void FTP_Analyzer::DeliverStream(int length, const u_char* data, bool orig) // Weird("FTP command missing", end_of_line - orig_line, orig_line); cmd_str = new StringVal(""); } + else if ( BifConst::FTP::max_command_length > 0 && + static_cast(cmd_len) > BifConst::FTP::max_command_length ) + { + // If the FTP command is unusually long, log a weird if the analyzer + // has previously been confirmed, but otherwise just ignore the whole + // line and move on to the next. + if ( AnalyzerConfirmed() ) + Weird("FTP_max_command_length_exceeded", util::fmt("%d", cmd_len)); + + return; + } else cmd_str = (new StringVal(cmd_len, cmd))->ToUpper(); diff --git a/src/const.bif b/src/const.bif index 2658376817..f28429b33c 100644 --- a/src/const.bif +++ b/src/const.bif @@ -11,6 +11,8 @@ const exit_only_after_terminate: bool; const digest_salt: string; const max_analyzer_violations: count; +const FTP::max_command_length: count; + const NFS3::return_data: bool; const NFS3::return_data_max: count; const NFS3::return_data_first_only: bool; diff --git a/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/conn.log b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/conn.log new file mode 100644 index 0000000000..c7fb619584 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/conn.log @@ -0,0 +1,11 @@ +### 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 conn +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents +#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string] +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 58634 127.0.0.1 21 tcp ftp 0.213412 358 313 SF - - 0 ShAdDaFf 23 1562 17 1205 - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/dpd.log b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/dpd.log new file mode 100644 index 0000000000..55429f6c38 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/dpd.log @@ -0,0 +1,11 @@ +### 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 dpd +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto analyzer failure_reason +#types time string addr port addr port enum string string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 58634 127.0.0.1 21 tcp FTP FTP::max_command_length exceeded +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/ftp.log b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/ftp.log new file mode 100644 index 0000000000..4a2c02d8c2 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/ftp.log @@ -0,0 +1,15 @@ +### 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 ftp +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p user password command arg mime_type file_size reply_code reply_msg data_channel.passive data_channel.orig_h data_channel.resp_h data_channel.resp_p fuid +#types time string addr port addr port string string string string string count count string bool addr addr port string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 58634 127.0.0.1 21 anonymous - USER anonymous - - 230 Response to USER command - - - - - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 58634 127.0.0.1 21 anonymous anonymous@zeek.org SYST - - - 200 Response to TYPEEEEEEEEEEEEE command - - - - - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 58634 127.0.0.1 21 anonymous anonymous@zeek.org PASV - - - 215 Response to SYSTTTTTTTTTTTTT command - - - - - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 58634 127.0.0.1 21 anonymous anonymous@zeek.org RETR ftp://127.0.0.1/./robots.txt - - 225 Response to RETR command - - - - - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 58634 127.0.0.1 21 anonymous anonymous@zeek.org PORT 114,115 - - 200 Response to PORT command - - - - - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/weird.log b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/weird.log new file mode 100644 index 0000000000..49022db073 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.ftp.ftp-max-command-length/weird.log @@ -0,0 +1,11 @@ +### 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 58634 127.0.0.1 21 FTP_max_command_length_exceeded 132 F zeek FTP +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Traces/ftp/fake-long-commands.pcap b/testing/btest/Traces/ftp/fake-long-commands.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8a31502e8acf12b7ddd7aae4a40a734572ff8239 GIT binary patch literal 3991 zcmc(hTTI(k9LG-!8JDFxG$iWwhbArok`3q|COTm)#Kv1D*vsUyAqC2rQeK zw!ruD`JHpx&2{eZ}`R}{^qPblMW9sX3x*0^X-Mj^|NU^{=Y3RuK<-lu~cRZl^Yj@GAms#PR)OG z2hAiqe7WyPJsGma*v(0tUyMamKRkqYsLG! zSrDXf8y6`)?Y@ADck_fI&$@|OlMmV1{z_RlDjI)f zQp@~2IONXkzDQt7g@XoW#s*+cVdmxy$zc#PH$CEDJrEYRaZzTU`!iJB{?}9yGM~L8 zE2?$1PG5L(EFPN-I}>pyb?A#nqoG*8)*t|Wxf>|$#ArnLaq6d1^E2htU`h7F22YeZ zSQkXcZCupgsM0}-ZKjIQ;Ai+Ve*A`j`e0kL-=C%Kc`Y1{RL3UuqbHe|i zu;Td30snalKPOoYVfcb_OZ;CD{w-5Q2>*8W5MubDo%pGYgkJ%_+zpgYf;A-kIR5j1 zzlj=r@c#w}A9b*2fIDvEBK(JyE>aAcDnf(zvI8B)2L29zKx5qky1A3OFA?h!q03n} z0qYbceOFJ4y2HqN3lw`GB5vd2NBv7;{kN$iBz@y8Sy6Qzl8!nicS@`a_gghkjuV-k zZ+Qw>cTt0w?1v53Dje)bh>qL1sKKIg0u{^FL<2rOO%WP2t?P;-$RyCw=DpWToAp1? z%VO4ZH0%3CFFxz8z2UX0O4(-Y>khDXmq(+)v}&J&Tfe1l#;_!19j0Oo}H=6`{>^ zk*+ujZ6fe09MiaOg02PkdE)L6x}5uI;9f&Xv+o(|j$+aq2QBZ|0&(9oRm3}XMM^rV zstwW|!{jpSUDZ|%lpZ3}EBrX?8emPYgeUu+=?!kXEbmwd70Xsl6`{eakLwMh$OK=7 zd%d(-pQ*FF5nt0}Zi-%f)+w0v0)D4@(=PRjn!SF;p7N9~gLm$8ru5R3dW1Kh(gI9r z3D24Pmh6q^?ABur=7)=o+qmd{dzC&?e8E%^+SJ$SiZPf{)aMQOoD=chcw(|TaWbI+ zyVhWNl)oWhw}k}p-T0$aLy{6F_Dh$*f(w&+0S|lVXRdVu_s{+0qr$Lb2gjD@FGr wD&8#+ikwLWFj-BaV)hH&3Mi&AqsLNF8KI2rLXo##h1QkW`p7$aMpRt+5B{mBk^lez literal 0 HcmV?d00001 diff --git a/testing/btest/scripts/base/protocols/ftp/ftp-max-command-length.zeek b/testing/btest/scripts/base/protocols/ftp/ftp-max-command-length.zeek new file mode 100644 index 0000000000..39f0bcca1f --- /dev/null +++ b/testing/btest/scripts/base/protocols/ftp/ftp-max-command-length.zeek @@ -0,0 +1,12 @@ +# @TEST-DOC: Artificially generated pcap with FTP commands of length > 100. Verify generation of the involved logs. +# +# @TEST-EXEC: zeek -b -r $TRACES/ftp/fake-long-commands.pcap %INPUT +# @TEST-EXEC: btest-diff conn.log +# @TEST-EXEC: btest-diff ftp.log +# @TEST-EXEC: btest-diff weird.log +# @TEST-EXEC: test ! -f reporter.log + +@load base/protocols/conn +@load base/protocols/ftp + +redef FTP::logged_commands += { "USER", "SYST" };