From 8efddf87d985fe77253795e9d8a23d1014d1145a Mon Sep 17 00:00:00 2001 From: Tim Wojtulewicz Date: Fri, 5 Sep 2025 15:39:17 -0700 Subject: [PATCH] SMB: Init lanman time and set TZ correctly before calling mktime This makes a change to the record for smb1-negotiate-response as well. Times and dates are two 2-byte sections, not three. Switch the parsing to just use two uint16s, and pass those into the lanman time conversion function. --- src/analyzer/protocol/smb/smb-time.pac | 33 ++++++++++++------ .../protocol/smb/smb1-com-negotiate.pac | 4 +-- .../out | 8 +++++ testing/btest/Traces/README | 3 ++ .../Traces/smb/cifs_negotiate_lanman.pcap | Bin 0 -> 693 bytes .../protocols/smb/smb1-negotiate-lanman.zeek | 19 ++++++++++ 6 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.smb.smb1-negotiate-lanman/out create mode 100644 testing/btest/Traces/smb/cifs_negotiate_lanman.pcap create mode 100644 testing/btest/scripts/base/protocols/smb/smb1-negotiate-lanman.zeek diff --git a/src/analyzer/protocol/smb/smb-time.pac b/src/analyzer/protocol/smb/smb-time.pac index d30a151ce1..ec2775a8d7 100644 --- a/src/analyzer/protocol/smb/smb-time.pac +++ b/src/analyzer/protocol/smb/smb-time.pac @@ -1,6 +1,6 @@ %header{ double filetime2zeektime(uint64_t ts); -double time_from_lanman(SMB_time* t, SMB_date* d, uint16_t tz); +double time_from_lanman(uint32_t smb_time, uint32_t smb_date, uint16_t tz); zeek::RecordValPtr SMB_BuildMACTimes(uint64_t modify, uint64_t access, uint64_t create, uint64_t change); @@ -14,17 +14,30 @@ double filetime2zeektime(uint64_t ts) return (static_cast(ts) / 10000000.0) - 11644473600.0; } -double time_from_lanman(SMB_time* t, SMB_date* d, uint16_t tz) +double time_from_lanman(uint32_t smb_time, uint32_t smb_date, uint16_t tz) { - tm lTime; - lTime.tm_sec = ${t.two_seconds} * 2; - lTime.tm_min = ${t.minutes}; - lTime.tm_hour = ${t.hours}; - lTime.tm_mday = ${d.day}; - lTime.tm_mon = ${d.month}; - lTime.tm_year = 1980 + ${d.year}; + tm lTime{0}; + + // Lanman uses this format for time/date: + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/32-bit-windows-time-date-formats + // Seconds is in 2-second increments in the data. + lTime.tm_sec = (smb_time & 0x1f) * 2; + lTime.tm_min = (smb_time >> 5) & 0x3f; + lTime.tm_hour = (smb_time >> 11) & 0x1f; + + lTime.tm_mday = smb_date & 0x1f; + // tm_mon is zero-indexed, so adjust for that. + lTime.tm_mon = ((smb_date >> 5) & 0x0f) - 1; + // The year in the data is the number of years from 1980, while tm_year is the + // number of years since 1900. + lTime.tm_year = ((smb_date >> 9) & 0x7f) + 80; lTime.tm_isdst = -1; - return mktime(&lTime) + tz; + + // The timezone passed in the data is the number of minutes from UTC, while + // tm_gmtoff is the number of seconds east of UTC. Adjust for that. + lTime.tm_gmtoff = static_cast(tz) * 60; + + return timegm(&lTime); } zeek::RecordValPtr SMB_BuildMACTimes(uint64_t modify, uint64_t access, diff --git a/src/analyzer/protocol/smb/smb1-com-negotiate.pac b/src/analyzer/protocol/smb/smb1-com-negotiate.pac index 57529a507f..9f2828b601 100644 --- a/src/analyzer/protocol/smb/smb1-com-negotiate.pac +++ b/src/analyzer/protocol/smb/smb1-com-negotiate.pac @@ -181,8 +181,8 @@ type SMB1_negotiate_lanman_response(header: SMB_Header) = record { max_number_vcs : uint16; raw_mode : uint16; # expanded in &let session_key : uint32; - server_time : SMB_time; - server_date : SMB_date; + server_time : uint16; + server_date : uint16; server_tz : uint16; encryption_key_length : uint16; reserved : uint16; # must be zero diff --git a/testing/btest/Baseline/scripts.base.protocols.smb.smb1-negotiate-lanman/out b/testing/btest/Baseline/scripts.base.protocols.smb.smb1-negotiate-lanman/out new file mode 100644 index 0000000000..b407cefc71 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.smb.smb1-negotiate-lanman/out @@ -0,0 +1,8 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +smb1_negotiate_request +[command=114, status=0, flags=24, flags2=51283, tid=0, pid=4660, uid=0, mid=1] +[PC NETWORK PROGRAM 1.0, LANMAN1.0, LM1.2X002, LANMAN2.1] +smb1_negotiate_response +[command=114, status=0, flags=152, flags2=51283, tid=0, pid=4660, uid=0, mid=1] +[core=, lanman=[word_count=13, dialect_index=3, security_mode=[user_level=T, challenge_response=T, signatures_enabled=, signatures_required=], max_buffer_size=4356, max_mpx_count=32, max_number_vcs=1, raw_mode=[read_raw=F, write_raw=F], session_key=0, server_time=1758862898.0, encryption_key=\x11"3DUfw\x88, primary_domain=\xe4\xbd\x97\xe4\xad\x92\xe5\x89\x87\xe5\x95\x8fP], ntlm=] +Parsed Response Server Time: 2025-09-26-05:01:38T diff --git a/testing/btest/Traces/README b/testing/btest/Traces/README index a01d06902b..f2389909f5 100644 --- a/testing/btest/Traces/README +++ b/testing/btest/Traces/README @@ -56,3 +56,6 @@ Trace Index/Sources: - smb_v2_only_non_zero_reserved1.pcap Provided by @predator89090 on #4730 https://github.com/zeek/zeek/issues/4730 +- smb/cifs_negotiate_lanman.pcap + Generated with scapy/chatgpt by @Mohan-Dhawan + https://github.com/zeek/zeek/issues/4545 \ No newline at end of file diff --git a/testing/btest/Traces/smb/cifs_negotiate_lanman.pcap b/testing/btest/Traces/smb/cifs_negotiate_lanman.pcap new file mode 100644 index 0000000000000000000000000000000000000000..fffaa573caa926f98517081212e08535e78bcf34 GIT binary patch literal 693 zcmca|c+)~A1{MYw`2U}Qff2|tmb;b#5;KEh203VsMvh<3|8ph=Yh0zgx?K}<0yWnh>FbRP&~n9>39(YtSGK7yDc@#_QU zLy$IxnE%1PPDLP=MDPg^g#ac(;Lx*VUvk{ z#|(-&q;Q9r0`%)INRse}`*j9(zw$COFf*_SDu5FWC?JeAtfIXbIe;u7cnE`n!9B=7 cGyv?qgjWdf!Icn5Rwj19s5}Gq9y0?207G out +# @TEST-EXEC: TEST_DIFF_CANONIFIER= btest-diff out + +event smb1_negotiate_request(c: connection, hdr: SMB1::Header, dialects: string_vec) + { + print "smb1_negotiate_request"; + print hdr; + print dialects; + } + +event smb1_negotiate_response(c: connection, hdr: SMB1::Header, response: SMB1::NegotiateResponse) + { + print "smb1_negotiate_response"; + print hdr; + print response; + print fmt("Parsed Response Server Time: %DT", response$lanman$server_time); + }