Find SMBv1 using Wireshark
In a recent Wireshark training, a student had the task of determining which IPs use SMBv1 within a corporate environment. The student showed me this link on Ask Wireshark that has a capture filter for finding SMBv1 packets. This capture filter looks a bit complicated at first. It basically only captures port 139 and 445 and then tries to determine if SMBv1 is used by looking for the magic version number 0xff534d42
. As the TCP header might be anywhere from 20 to 60 bytes in size, the capture filter uses arithmetic to determine the header's size (actually the offset) and then searches for the magic value at the correct offset. It then adds 4 bytes to jump over the NetBIOS header to the SMB header:
(tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00 and tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xff534d42
My first reaction to this filter was that it seems a bit overcomplicated. Wireshark has display filters that can perfectly decode the SMB protocol, independent of the offsets or whether there is NetBIOS/TCP. However, I can see how a capture filter can be useful here to filter the traffic while capturing, so as not to write a lot of data to disk. So let's do a deeper dive into how to enhance these capture filters.
Finding SMB Versions Using Capture Filters
For this, we used 4 sample files that exclusively contain SMBv1, v2, v3, and v311 each. As tshark
cannot re-filter on a capture filter, we will use tcpdump
to verify if the capture filters work.
Here is a table with all the magic numbers for identifying the different protocols. I added the network order because, on the Microsoft page, the order is not correct for writing capture filters.
ProtocolId (4 bytes): The protocol identifier. The value MUST be set to 0x424D53FE, also represented as (in network order) 0xFE, 'S', 'M', and 'B'.
Protocol | Network Order | Normal Order |
---|---|---|
SMBv1 | 0xff534d42 | 0x424d53ff |
SMBv2 | 0xfe534d42 | 0x424d53fe |
SMBv3 | 0xfd534d42 | 0x424d53fd |
SMB 3.1.1 | 0xfd534d42 | 0x424d53fd |
SMB 3.1.1 (compressed) | 0xfc534d42 | 0x424d53fc |
Using the information provided, we can filter out all the packets of a specific SMB version.
Find SMBv1 Packets Using a Capture Filter
To filter SMBv1 packets, you can use the following tcpdump command:
tcpdump -r smb1.pcapng "(tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00 and tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xff534d42"
reading from PCAP-NG file smb1.pcapng
14:44:32.709123 IP 192.168.0.1.17704 > 192.168.0.2.microsoft-ds: Flags [P.], seq 1:52, ack 2746319828, win 8192, length 51
14:44:32.709853 IP 192.168.0.2.microsoft-ds > 192.168.0.1.17704: Flags [P.], seq 1:106, ack 51, win 43690, length 105
14:44:32.850415 IP 192.168.0.1.17704 > 192.168.0.2.microsoft-ds: Flags [P.], seq 51:180, ack 106, win 8192, length 129
14:44:32.851609 IP 192.168.0.2.microsoft-ds > 192.168.0.1.17704: Flags [P.], seq 106:180, ack 180, win 44488, length 74
14:44:32.991305 IP 192.168.0.1.17704 > 192.168.0.2.microsoft-ds: Flags [P.], seq 180:245, ack 180, win 8192, length 65
14:44:32.992029 IP 192.168.0.2.microsoft-ds > 192.168.0.1.17704: Flags [P.], seq 180:230, ack 245, win 44488, length 50
Find SMBv2 Packets Using a Capture Filter
To filter SMBv2 packets, you can use the following tcpdump command:
tcpdump -r smb2.pcapng "(tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00 and tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfe534d42"
reading from PCAP-NG file smb2.pcapng
17:43:06.686127 IP 169.254.128.18.49155 > 169.254.128.15.microsoft-ds: Flags [P.], seq 1164876382:1164876618, ack 1916121561, win 254, length 236
17:43:06.686621 IP 169.254.128.15.microsoft-ds > 169.254.128.18.49155: Flags [P.], seq 1:245, ack 236, win 3330, length 244
17:43:06.687262 IP 169.254.128.18.49155 > 169.254.128.15.microsoft-ds: Flags [P.], seq 236:457, ack 245, win 253, length 221
17:43:06.687443 IP 169.254.128.15.microsoft-ds > 169.254.128.18.49155: Flags [P.], seq 245:457, ack 457, win 3330, length 212
17:43:06.687798 IP 169.254.128.18.49155 > 169.254.128.15.microsoft-ds: Flags [P.], seq 457:565, ack 457, win 252, length 108
17:43:06.687927 IP 169.254.128.15.microsoft-ds > 169.254.128.18.49155: Flags [P.], seq 457:537, ack 565, win 3330, length 80
17:43:06.691167 IP 169.254.128.18.49155 > 169.254.128.15.microsoft-ds: Flags [P.], seq 565:673, ack 537, win 251, length 108
[...]
Find SMBv3 Packets Using a Capture Filter
To filter SMBv3 packets, you can use the following tcpdump command:
tcpdump -r smb3.pcapng "(tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00 and tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfd534d42"
reading from PCAP-NG file smb3.pcapng
12:02:16.548245 IP 10.160.64.139.38166 > 10.160.65.202.microsoft-ds: Flags [P.], seq 743360267:743360469, ack 1517249477, win 490, options [nop,nop,TS val 3180665817 ecr 162696635], length 202
12:02:16.548347 IP 10.160.65.202.microsoft-ds > 10.160.64.139.38166: Flags [P.], seq 1:193, ack 202, win 8232, options [nop,nop,TS val 162696640 ecr 3180665817], length 192
12:02:16.551965 IP 10.160.64.139.38166 > 10.160.65.202.microsoft-ds: Flags [P.], seq 202:386, ack 193, win 507, options [nop,nop,TS val 3180665818 ecr 162696640], length 184
12:02:16.552110 IP 10.160.65.202.microsoft-ds > 10.160.64.139.38166: Flags [P.], seq 193:401, ack 386, win 8231, options [nop,nop,TS val 162696643 ecr 3180665818], length 208
12:02:16.555464 IP 10.160.64.139.38166 > 10.160.65.202.microsoft-ds: Flags [P.], seq 386:563, ack 401, win 524, options [nop,nop,TS val 3180665819 ecr 162696643], length 177
[...]
Find SMBv311 Packets Using a Capture Filter
To filter SMBv311 packets, you can use the same capture filter as for SMBv3:
tcpdump -r smb311.pcapng "(tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00 and tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfd534d42"
reading from PCAP-NG file smb311.pcapng
19:44:09.829884 IP 192.168.100.168.44718 > 10.160.67.244.microsoft-ds: Flags [P.], seq 1081496955:1081497115, ack 3820537110, win 501, options [nop,nop,TS val 3790114163 ecr 2296985989], length 160
19:44:09.854982 IP 10.160.67.244.microsoft-ds > 192.168.100.168.44718: Flags [P.], seq 1:137, ack 160, win 255, options [nop,nop,TS val 2296986047 ecr 3790114163], length 136
19:44:09.862502 IP 192.168.100.168.44718 > 10.160.67.244.microsoft-ds: Flags [P.], seq 160:320, ack 137, win 501, options [nop,nop,TS val 3790114195 ecr 2296986047], length 160
19:44:09.888820 IP 10.160.67.244.microsoft-ds > 192.168.100.168.44718: Flags [P.], seq 137:273, ack 320, win 254, options [nop,nop,TS val 2296986081 ecr 3790114195], length 136
[...]
The Problem
While capturing all SMB traffic might seem feasible initially, this approach becomes impractical for long-term baselining of a large network due to the sheer volume of data. To optimize, we can focus on capturing only the negotiation packets, which are sufficient for initial analysis.
By filtering specifically for negotiation commands, we can significantly reduce the amount of captured data.
Optimized Capture Filters
SMBv1 Negotiation Packets
To capture only the SMBv1 negotiation packets, we use the command code 0x72
:
(tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00 and tcp[((tcp[12:1] & 0xF0) >> 2) + 8:1] = 0x72 and tcp[((tcp[12] & 0xf0) >> 2) + 4:4] == 0xff534d42
SMBv2 and SMBv3 Negotiation Packets
For SMBv2 and SMBv3, the offset for the command field changes, and the command field is 2 bytes:
((tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00 and (tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfe534d42 or tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfd534d42) and tcp[((tcp[12:1] & 0xF0) >> 2) + 16:2] = 0x0000)
Combined Filter for SMBv1, SMBv2, and SMBv3 Negotiation Packets
To capture negotiation packets for all SMB versions, combine the filters:
((tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00) and ((tcp[((tcp[12:1] & 0xF0) >> 2) + 8:1] = 0x72 and tcp[((tcp[12] & 0xf0) >> 2) + 4:4] == 0xff534d42) or ((tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfe534d42 or tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfd534d42) and tcp[((tcp[12:1] & 0xF0) >> 2) + 16:2] = 0x0000))
Reducing Data Volume Further
Capturing Only Negotiation Requests
To capture only negotiation requests, filter packets destined for the SMB server's IP address (e.g., 192.168.1.10
):
dst host 192.168.1.10 and ((tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00) and ((tcp[((tcp[12:1] & 0xF0) >> 2) + 8:1] = 0x72 and tcp[((tcp[12] & 0xf0) >> 2) + 4:4] == 0xff534d42) or ((tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfe534d42 or tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfd534d42) and tcp[((tcp[12:1] & 0xF0) >> 2) + 16:2] = 0x0000))
Using Packet Slicing
Packet slicing can further reduce the volume of captured data. For example, to capture up to 120 bytes of each packet (enough to include headers and the initial part of the SMB payload):
- Ethernet header: 14 bytes
- IP header: 20 bytes (minimum)
- TCP header: 60 bytes (maximum)
- SMB header: 17 bytes (for command, offset 16, length 2 bytes)
This totals 111 bytes, so we capture slightly more for good measure as the SMBv1 dissector fails to decode too aggressively sliced packets. The SMBv2 dissector is a bit better at this.
tcpdump -s 120 -r smb_traffic.pcapng "dst host 192.168.1.10 and ((tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00) and ((tcp[((tcp[12:1] & 0xF0) >> 2) + 8:1] = 0x72 and tcp[((tcp[12] & 0xf0) >> 2) + 4:4] == 0xff534d42) or ((tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfe534d42 or tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfd534d42) and tcp[((tcp[12:1] & 0xF0) >> 2) + 16:2] = 0x0000))"
This setup should reduce the amount of captured data while still providing the necessary information for analyzing SMB negotiation packets across different SMB versions.
With a bit of testing, it seems that adjusting the packet slicing to 200 bytes allows us to capture enough of the packet to include the dialects field, which is helpful for in-depth analysis of the negotiated protocols.
tcpdump -s 200 -r smb_traffic.pcapng "dst host 192.168.1.10 and ((tcp port 139 or 445) and tcp[((tcp[12:1] & 0xF0) >> 2):1] = 0x00) and ((tcp[((tcp[12:1] & 0xF0) >> 2) + 8:1] = 0x72 and tcp[((tcp[12] & 0xf0) >> 2) + 4:4] == 0xff534d42) or ((tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfe534d42 or tcp[((tcp[12:1] & 0xF0) >> 2) + 4:4] = 0xfd534d42) and tcp[((tcp[12:1] & 0xF0) >> 2) + 16:2] = 0x0000))"
This approach ensures that you capture sufficient packet data to analyze SMB negotiation protocols in depth while minimizing unnecessary data collection. We are now down from terabytes of data in half a year to maybe a few GB.
Using Display Filters
Once we have captured all the negotiation packets, we need to apply display filters or field extractions with Tshark to get the protocols.
Wireshark has protocol filters for SMBv1 and SMBv2 but not a separate one for SMBv2. So smb
gets you all the SMBv1 packets, but smb2
gets you all the SMBv2 and SMBv3 packets.
At the beginning of an SMB connection, the two endpoints negotiate the protocol, and here we see how the client sends the supported protocols.
These are all Legacy SMB protocols. Most commonly in Windows networks, you will find NT LM 0.12
, aka SMBv1.
Protocol Name | Dialect Identifier | Description | Wireshark Filter |
---|---|---|---|
SMBv1 NT LAN Manager | NT LM 0.12 | NT LAN Manager extended protocol supporting Windows NT and OS/2 LAN Manager 2.1 features. Also known as NT LANMAN. | smb.dialect == "NT LM 0.12" |
Core Protocol | PCLAN1.0 | Early IBM implementation of SMB Protocol. | smb.dialect == "PCLAN1.0" |
Core Protocol | PC NETWORK PROGRAM 1.0 | MSNET SMB Protocol, also known as the "core protocol". Identical to "PCLAN1.0". | smb.dialect == "PC NETWORK PROGRAM 1.0" |
Xenix Extensions | xenix1.1 | Extensions to SMB to support the XENIX operating system. | smb.dialect == "xenix1.1" |
Xenix Extensions | XENIX CORE | Another dialect supporting XENIX extensions, sent by Windows NT and OS/2. | smb.dialect == "XENIX CORE" |
CorePlus | MICROSOFT NETWORKS 1.03 | "CorePlus" dialect with minor extensions to the core protocol, including raw read and write commands. | smb.dialect == "MICROSOFT NETWORKS 1.03" |
LAN Manager 1.0 | LANMAN1.0 | Extended protocol to support OS/2 system functions and file system features. | smb.dialect == "LANMAN1.0" |
DOS LAN Manager 1.0 | MICROSOFT NETWORKS 3.0 | DOS LAN Manager 1.0 extended protocol, identical to "LANMAN1.0" but translates OS/2 error codes to DOS. | smb.dialect == "MICROSOFT NETWORKS 3.0" |
LAN Manager 1.2 | LANMAN1.2 | Adds support for additional OS/2 commands and features to "LANMAN1.0". | smb.dialect == "LANMAN1.2" |
LAN Manager 2.0 | LM1.2X002 | LAN Manager 2.0 extended protocol for OS/2. Also known as LANMAN2.0. | smb.dialect == "LM1.2X002" |
DOS LAN Manager 2.0 | DOS LM1.2X002 | DOS version of LAN Manager 2.0, translating OS/2 error codes to DOS. Also known as DOS LANMAN2.0. | smb.dialect == "DOS LM1.2X002" |
LAN Manager 2.1 | LANMAN2.1 | LAN Manager 2.1 extended protocol. | smb.dialect == "LANMAN2.1" |
DOS LAN Manager 2.1 | DOS LANMAN2.1 | DOS LAN Manager 2.1 extended protocol, translating OS/2 error codes to DOS. | smb.dialect == "DOS LANMAN2.1" |
One-liner to find any of these: |
smb.dialect in {"NT LM 0.12", "PCLAN1.0", "PC NETWORK PROGRAM 1.0", "xenix1.1", "XENIX CORE", "MICROSOFT NETWORKS 1.03", "LANMAN1.0", "MICROSOFT NETWORKS 3.0", "LANMAN1.2", "LM1.2X002", "DOS LM1.2X002", "LANMAN2.1", "DOS LANMAN2.1"}
Finding Legacy SMBv1 Packets Using Display Filters
Actually, we don't need to complicate things too much. To find legacy SMBv1 traffic, we can just use the simple display filter smb
or smb.cmd == 0x72
(negotiated requests).
SMBv2 and higher
If we have SMB2 or higher, the dissector changes to smb2
, and the .dialect
field is now not a string anymore but a 2-byte field. So, we have to write the filters a bit differently, like smb2.dialect in {0x0202, 0x0210, 0x0300, 0x0302, 0x0311}
.
Newer SMB versions
Protocol Name | Dialect Identifier | Description | Wireshark Filter |
---|---|---|---|
SMB2 | 0x0202 | SMB 2.0 protocol. | smb2.dialect == 0x0202 |
SMB2 | 0x0210 | SMB 2.1 protocol. | smb2.dialect == 0x0210 |
SMB3 | 0x0300 | SMB 3.0 protocol. | smb2.dialect == 0x0300 |
SMB3 | 0x0302 | SMB 3.0.2 protocol. | smb2.dialect == 0x0302 |
SMB3 | 0x0311 | SMB 3.1.1 protocol. | smb2.dialect == 0x0311 |
SMB 3.1.1 (compressed) | 0x0311 (with compression flag) | SMB 3.1.1 with compression. | smb2.dialect == 0x0311 && smb2.flags & 0x08 |
Find IPs using SMBv1
To find the IPs of all clients negotiating SMBv1 from the captured packets and output which IPs use which dialects, you can use the following command:
tshark -r smb_traffic.pcapng -T fields -e ip.src -e smb.dialect -e smb2.dialect | sort | uniq