From b002160f024307db258510262f5838fd45aaa3e3 Mon Sep 17 00:00:00 2001 From: Gregor Maier Date: Mon, 20 Jun 2011 14:09:15 -0700 Subject: [PATCH] checkpoint --- policy/smb.bro | 42 ++++++++++-- src/SMB.cc | 151 +++++++++++++++++++++++++++++++++---------- src/SMB.h | 13 ++++ src/event.bif | 2 +- src/smb-protocol.pac | 5 +- 5 files changed, 173 insertions(+), 40 deletions(-) diff --git a/policy/smb.bro b/policy/smb.bro index 8854051420..dabf0d94b1 100644 --- a/policy/smb.bro +++ b/policy/smb.bro @@ -49,10 +49,25 @@ global next_fid = 0; # request/reply matching, but the more specific event takes care of printing. # It's all a hack.... global more_specific_cmds: set[count]; +# Transaction commands and other commands for which request/reply matching +# doesn't work. We ignore them all. +# transaction commands re-use PID:MID. We'd have to look into the actual +# transaction command to match requests/replies. +global smb_ignore_cmds: set[count]; event bro_init() { add more_specific_cmds[0x2e]; # read_andx add more_specific_cmds[0x2f]; # write_andx + + add smb_ignore_cmds[0x25]; # transaction + add smb_ignore_cmds[0x26]; # transaction_secondary + add smb_ignore_cmds[0x32]; # transaction2 + add smb_ignore_cmds[0x33]; # transaction2_secondary + add smb_ignore_cmds[0xA0]; # nt_transact + add smb_ignore_cmds[0xA1]; # nt_transact_secondary + + add smb_ignore_cmds[0xA4]; # nt_cancel + } function smb_new_cmd_info(hdr: smb_hdr, body_len: count): smb_cmd_info @@ -112,11 +127,11 @@ function fmt_msg_prefix(cid: conn_id, is_orig: bool, hdr: smb_hdr): string function smb_log_cmd(c: connection, info: smb_cmd_info) { local msg = ""; - msg = fmt("COMMAND %s (%d) %d:%d %.6f %.6f %d %.6f %.6f %d %s", + msg = fmt("COMMAND %s (%d) %d:%d %.6f %.6f %d %.6f %.6f %d %s %d %s %s %d", info$cmdstr, info$cmd, info$pid, info$mid, info$req_first_time, info$req_last_time, info$req_body_len, info$rep_first_time, info$rep_last_time, info$rep_body_len, - get_fid(c$id, info$fid)); + get_fid(c$id, info$fid), info$file_payload, c$id$orig_h, c$id$resp_h, c$id$resp_p); print smb_log, msg; } @@ -146,6 +161,8 @@ function smb_set_fid(cid: conn_id, hdr: smb_hdr, fid: count) { # smb_messge takes care of error / mismatch handling, so we can # just punt here + if (hdr$command == 0x2f) + print fmt("in set_fid: %d", fid); if (cid !in smb_sessions) return; local cur_session = smb_sessions[cid]; @@ -154,6 +171,8 @@ function smb_set_fid(cid: conn_id, hdr: smb_hdr, fid: count) local info = cur_session[hdr$pid, hdr$mid]; info$fid = fid; + if (hdr$command == 0x2f) + print fmt("end of set_fid: %d %d", info$fid, fid); } function smb_set_file_payload(cid: conn_id, hdr: smb_hdr, payload_len: count) @@ -176,6 +195,14 @@ function smb_set_file_payload(cid: conn_id, hdr: smb_hdr, payload_len: count) event smb_message(c: connection, hdr: smb_hdr, is_orig: bool, cmd: string, body_length: count, body: string) { ###print smb_log, fmt("%s %s %d", fmt_msg_prefix(c$id, is_orig, hdr), cmd, body_length); + + if (hdr$command==0x24 && hdr$mid == 0xffff) + # opLock break notification event from server. + # ignore it. + return; + if (hdr$command in smb_ignore_cmds) + return; + if (c$id !in smb_sessions) smb_sessions[c$id] = table(); local cur_session = smb_sessions[c$id]; @@ -188,15 +215,15 @@ event smb_message(c: connection, hdr: smb_hdr, is_orig: bool, cmd: string, body_ if (is_orig) { if ([hdr$pid,hdr$mid] in cur_session) - print smb_log, fmt("Mismatch: got a request but already have request queued: %s %s", - mismatch_fmt_info(cur_session[hdr$pid,hdr$mid]), mismatch_fmt_hdr(hdr,cmd)); + print smb_log, fmt("Mismatch: got a request but already have request queued: %s %s %s", + mismatch_fmt_info(cur_session[hdr$pid,hdr$mid]), mismatch_fmt_hdr(hdr,cmd), id_string(c$id)); cur_session[hdr$pid, hdr$mid] = smb_new_cmd_info(hdr, body_length); cur_session[hdr$pid, hdr$mid]$cmdstr = cmd; } else { if ([hdr$pid,hdr$mid] !in cur_session) - print smb_log, fmt("Mismatch: got a reply but no request queued: %s", mismatch_fmt_hdr(hdr,cmd)); + print smb_log, fmt("Mismatch: got a reply but no request queued: %s %s", mismatch_fmt_hdr(hdr,cmd), id_string(c$id)); else { local info = cur_session[hdr$pid, hdr$mid]; @@ -249,6 +276,11 @@ event smb_com_write_andx_response(c: connection, hdr: smb_hdr) } +event smb_error(c: connection, hdr: smb_hdr, cmd: count, cmd_str: string, errtype: count, error: count) + { + print smb_log, fmt("ERROR: %s %s (0x%2x): %d %08x", id_string(c$id), cmd_str, cmd, errtype, error); + } + event connection_state_remove(c: connection) { delete smb_sessions[c$id]; diff --git a/src/SMB.cc b/src/SMB.cc index 30ac77f0a1..a55aa7fd79 100644 --- a/src/SMB.cc +++ b/src/SMB.cc @@ -157,8 +157,8 @@ void SMB_Session::Deliver(int is_orig, int len, const u_char* data, int next_command = hdr.command(); - fprintf(stderr, "SMB command: %02x %s len %-7d dur %.6lf\n", next_command, - SMB_command_name[next_command], len, + fprintf(stderr, "SMB command: 0x%02x %s (%d) len %-7d dur %.6lf\n", next_command, + SMB_command_name[next_command], is_orig, len, last_time-first_time); int ncmds = 0; @@ -218,25 +218,23 @@ void SMB_Session::ParseMessage(int is_orig, int cmd, // What if there's an error? // if ( hdr.status->status() || hdr.status->dos_error() ) // The command code in the header might be right, but - // the response is probably mangled :-(. + // the response is probably mangled :-. int ci = hdr.status()->val_case_index(); - if ( (ci == 1 && hdr.status()->status()) || - (ci == 0 && (hdr.status()->dos_error()->error_class() || - hdr.status()->dos_error()->error())) ) + unsigned int error = 0; + + switch ( ci ) { + case 0: + error = hdr.status()->dos_error()->error_class() << 24 || + hdr.status()->dos_error()->error(); + break; + case 1: + error = hdr.status()->status(); + break; + } + + if (error) { - unsigned int error = 0; - - switch ( ci ) { - case 0: - error = hdr.status()->dos_error()->error_class() << 24 || - hdr.status()->dos_error()->error(); - break; - case 1: - error = hdr.status()->status(); - break; - } - val_list* vl = new val_list; StringVal* cmd_str = get_SMB_command_str(cmd); Ref(cmd_str); @@ -245,11 +243,10 @@ void SMB_Session::ParseMessage(int is_orig, int cmd, vl->append(BuildHeaderVal(hdr)); vl->append(new Val(cmd, TYPE_COUNT)); vl->append(cmd_str); - vl->append(new StringVal(body.length(), - (const char*) body.data())); + vl->append(new Val(ci, TYPE_COUNT)); + vl->append(new Val(error, TYPE_COUNT)); analyzer->ConnectionEvent(smb_error, vl); - // Is this the right behavior? return; } @@ -594,7 +591,8 @@ int SMB_Session::ParseReadAndxResponse(binpac::SMB::SMB_header const& hdr, resp.Parse(body.data(), body.data() + body.length()); set_andx(0, resp.andx()); - int data_count = resp.data_length(); + uint32_t data_len = resp.data_len_high(); + data_len = (data_len<<16) + resp.data_len(); const u_char* data = resp.data().begin(); if ( smb_com_read_andx_response ) @@ -602,13 +600,13 @@ int SMB_Session::ParseReadAndxResponse(binpac::SMB::SMB_header const& hdr, val_list* vl = new val_list; vl->append(analyzer->BuildConnVal()); vl->append(BuildHeaderVal(hdr)); - vl->append(new Val((resp.data_len_high()<<16)+(resp.data_len()), TYPE_COUNT)); + vl->append(new Val(data_len, TYPE_COUNT)); //vl->append(new StringVal(data_count, (const char*) data)); - analyzer->ConnectionEvent(smb_com_read_andx, vl); + analyzer->ConnectionEvent(smb_com_read_andx_response, vl); } - CheckRPC(0, data_count, data); + CheckRPC(0, data_len, data); return 0; } @@ -620,7 +618,8 @@ int SMB_Session::ParseWriteAndx(binpac::SMB::SMB_header const& hdr, req.Parse(body.data(), body.data() + body.length()); set_andx(1, req.andx()); - int data_count = req.data_length(); + uint32_t data_len = req.data_len_high(); + data_len = (data_len<<16) + req.data_len(); const u_char* data = req.data().begin(); if ( smb_com_write_andx ) @@ -629,13 +628,13 @@ int SMB_Session::ParseWriteAndx(binpac::SMB::SMB_header const& hdr, vl->append(analyzer->BuildConnVal()); vl->append(BuildHeaderVal(hdr)); vl->append(new Val(req.fid(), TYPE_COUNT)); - vl->append(new Val((req.data_len_high()<<16)+(req.data_len()), TYPE_COUNT)); + vl->append(new Val(data_len, TYPE_COUNT)); //vl->append(new StringVal(data_count, (const char*) data)); analyzer->ConnectionEvent(smb_com_write_andx, vl); } - CheckRPC(1, data_count, data); + CheckRPC(1, data_len, data); return 0; } @@ -1012,6 +1011,7 @@ Val* SMB_Session::BuildHeaderVal(binpac::SMB::SMB_header const& hdr) unsigned int status = 0; +#if 0 try { // FIXME: does this work? We need to catch exceptions :-( @@ -1023,13 +1023,16 @@ Val* SMB_Session::BuildHeaderVal(binpac::SMB::SMB_header const& hdr) catch ( const binpac::Exception& ) { // do nothing } +#endif + uint32_t pid = hdr.pid_high(); + pid = (pid<<16) + hdr.pid(); r->Assign(0, new Val(hdr.command(), TYPE_COUNT)); r->Assign(1, new Val(status, TYPE_COUNT)); r->Assign(2, new Val(hdr.flags(), TYPE_COUNT)); r->Assign(3, new Val(hdr.flags2(), TYPE_COUNT)); r->Assign(4, new Val(hdr.tid(), TYPE_COUNT)); - r->Assign(5, new Val(hdr.pid(), TYPE_COUNT)); + r->Assign(5, new Val(pid, TYPE_COUNT)); r->Assign(6, new Val(hdr.uid(), TYPE_COUNT)); r->Assign(7, new Val(hdr.mid(), TYPE_COUNT)); r->Assign(8, new Val(first_time, TYPE_TIME)); @@ -1127,17 +1130,31 @@ Contents_SMB::Contents_SMB(Connection* conn, bool orig, SMB_Session* s) { smb_session = s; state = WAIT_FOR_HDR; + resync_state = INSYNC; first_time = last_time = 0.0; hdr_buf.Init(4,4); msg_len = 0; msg_type = 0; } +void Contents_SMB::Init() + { + TCP_SupportAnalyzer::Init(); + + NeedResync(); + } Contents_SMB::~Contents_SMB() { } + +void Contents_SMB::Undelivered(int seq, int len, bool orig) + { + TCP_SupportAnalyzer::Undelivered(seq, len, orig); + NeedResync(); + } + void Contents_SMB::DeliverSMB(int len, const u_char* data) { // Check the 4-byte header. @@ -1147,18 +1164,83 @@ void Contents_SMB::DeliverSMB(int len, const u_char* data) //dshdr[0], dshdr[1], dshdr[2], dshdr[3], msg_type, msg_len, data[0], data[1], data[2], data[3])); - SetSkip(1); + NeedResync(); } else smb_session->Deliver(IsOrig(), len, data, first_time, last_time); } +bool Contents_SMB::CheckResync(int& len, const u_char*& data, bool orig) + { + + if (resync_state == INSYNC) + return true; + + // This is an attempt to re-synchronize the stream after a content gap. + // Returns true if we are in sync. + // Returns false otherwise (we are in resync mode) + // + // We try to look for the beginning of a SMB message, assuming + // SMB messages start at packet boundaries (though they may span + // over multiple packets) (note that the data* of DeliverStream() + // usually starts at a packet boundrary). + // + + // Now lets see whether data points to the beginning of a + // SMB message. If the resync processs is successful, we should + // be at the beginning of a frame. + + + if ( len < 36 ) + { + // Ignore small chunks. + // 4 byte NetBIOS header (or length field) + 32 Byte SMB header + Conn()->Weird(fmt("SMB resync: discard %d bytes\n", + len)); + NeedResync(); + return false; + } + + + const u_char *xdata = data; + int xlen = len; + bool discard_this_chunk = false; + + // Check if it's a data message + if (xdata[0]!=0x00) + discard_this_chunk = true; + + // Check if the flags / high-byte of the message length is < 1 + if (xdata[1] > 1) + discard_this_chunk = true; + + // check if the SMB header starts with \xFFSMB + if (strncmp((const char*) (xdata+4), "\xffSMB", 4)!=0) + discard_this_chunk = true; + + if (discard_this_chunk) + { + NeedResync(); + return false; + } + + resync_state = INSYNC; + first_time = last_time = 0.0; + hdr_buf.Init(4,4); + msg_len = 0; + msg_type = 0; + fprintf(stderr, "Resync successful\n"); + return true; + + } + void Contents_SMB::DeliverStream(int len, const u_char* data, bool orig) { TCP_SupportAnalyzer::DeliverStream(len, data, orig); - if (Skipping()) - return; + + if (!CheckResync(len, data, orig)) + return; // Not in sync yet. Still resyncing last_time = network_time; while ( len > 0 ) @@ -1191,10 +1273,13 @@ void Contents_SMB::DeliverStream(int len, const u_char* data, bool orig) { const u_char *dummy_p = msg_buf.GetBuf(); int dummy_len = (int) msg_buf.GetFill(); - if (msg_type == 0x00 && dummy_len >= 4) + if (msg_type == 0x00 && dummy_len >= 32) DeliverSMB(dummy_len, dummy_p); else if (msg_type == 0x00) + { Conn()->Weird(fmt("SMB too short: len=%d", msg_len)); + NeedResync(); + } else Conn()->Weird(fmt("SMB other msg type: %x", msg_type)); state = WAIT_FOR_HDR; diff --git a/src/SMB.h b/src/SMB.h index b77622b778..cb07a89503 100644 --- a/src/SMB.h +++ b/src/SMB.h @@ -188,6 +188,18 @@ protected: WAIT_FOR_HDR, WAIT_FOR_DATA } state_t; + typedef enum { + NEED_RESYNC, + INSYNC, + } resync_state_t; + virtual void Init(); + virtual bool CheckResync(int& len, const u_char*& data, bool orig); + virtual void Undelivered(int seq, int len, bool orig); + virtual void NeedResync() { + resync_state = NEED_RESYNC; + state = WAIT_FOR_HDR; + } + void DeliverSMB(int len, const u_char* data); SMB_Session* smb_session; @@ -199,6 +211,7 @@ protected: double first_time; // timestamp of first packet of current message double last_time; // timestamp of last pakcet of current message state_t state; + resync_state_t resync_state; }; class SMB_Analyzer : public TCP_ApplicationAnalyzer { diff --git a/src/event.bif b/src/event.bif index 6bd1f3df6d..a2c6a37611 100644 --- a/src/event.bif +++ b/src/event.bif @@ -232,7 +232,7 @@ event smb_com_setup_andx%(c: connection, hdr: smb_hdr%); event smb_com_generic_andx%(c: connection, hdr: smb_hdr%); event smb_com_close%(c: connection, hdr: smb_hdr%); event smb_com_logoff_andx%(c: connection, hdr: smb_hdr%); -event smb_error%(c: connection, hdr: smb_hdr, cmd: count, cmd_str: string, data: string%); +event smb_error%(c: connection, hdr: smb_hdr, cmd: count, cmd_str: string, errtype: count, error: count%); event dns_message%(c: connection, is_orig: bool, msg: dns_msg, len: count%) &group="dns"; event dns_request%(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count%) &group="dns"; diff --git a/src/smb-protocol.pac b/src/smb-protocol.pac index bdbbfd5085..6b7f85bbdd 100644 --- a/src/smb-protocol.pac +++ b/src/smb-protocol.pac @@ -115,7 +115,10 @@ type SMB_header = record { status : SMB_error(err_status_type); flags : uint8; flags2 : uint16; - pad : padding[12]; + #pad : padding[12]; + pid_high : uint16; + security_features: uint8[8]; + reserved : uint16; tid : uint16; pid : uint16; uid : uint16;