Sunday, June 24, 2018

SMB version detection in masscan

My Internet-scale port scanner, masscan, supports "banner checking", grabbing basic information from a service after it connects to a port. It's less comprehensive than nmap's version and scripting checks, but it's better than just recording which ports are open.

I recently extended this banner checking to include SMB. It's a complicated protocol so requires a lot more work than just grabbing text banners like you see on FTP. Implementing this, I've found that nmap and smbclient often fail to get version information. They seem focused on getting the information from a standard location in SMBv1 packets, which gives a text string indicating version. There's another place you get get it, from the NTLMSSP pluggable authentication chunks, which gives version numbers in the form of major version, minor version. and build number. Sometimes the SMBv1 information is missing, either because newer Windows version disable SMBv1 by default (supporting only SMBv2) or because they've disabled null/anonymous sessions. They still give NTLMSSP version info, though.


For example, running masscan in my local bar, I get the following result:

Banner on port 445/tcp on 10.1.10.200: [smb] SMBv1  time=2018-06-24 22:18:13 TZ=+240  domain=SHIPBARBO version=6.1.7601 ntlm-ver=15 domain=SHIPBARBO name=SHIPBARBO domain-dns=SHIPBARBO name-dns=SHIPBARBO os=Windows Embedded Standard 7601 Service Pack 1 ver=Windows Embedded Standard 6.1

The top version string comes from NTLMSSP, with 6.1.7601, which means Windows 6.1 (Win7) build number 7601. The bottom version string comes from the SMBv1 packets, which consists of strings.

The nmap and smbclient programs will get the SMBv1 part, but not the NTLMSSP part.

This seems to be a problem with Rapid7's "National Exposure Index" which tracks SMB exposure (amongst other things). It's missing about 300,000 machines that report NT_STATUS_ACCESS_DENIED from smbclient rather than the numeric version info from NTLMSSP authentication.

The smbclient information does have the information internally. For example, you could run the following command to put the debug level at '10' to grab it:

$ smbclient -U "" -N -L 10.1.10.95 -d10

You'll get something like the following output:


It appears to get the Windows 6.1 numbers, though for some reason it's missing the build number.

To run masscan to grab this, run:

# masscan --banners -p445 10.1.10.95

In the above example, I also used the "--hello smbv1" parameter, to grab both the SMBv1 and NTLMSSP version info. Otherwise, it'll default to SMBv2 if available, and only return:

Discovered open port 445/tcp on 10.1.10.95
Banner on port 445/tcp on 10.1.10.95: [smb] SMBv2  guid=6db701a0-a419-4be9-9084-6052b19a2e56 time=2018-06-24 22:37:42  domain=SHIPSERVER version=6.1.7601 ntlm-ver=15 domain=SHIPSERVER name=SHIPSERVER domain-dns=SHIPSERVER name-dns=SHIPSERVER

Note if you do a port 445 scan of the entire Internet, you'll get about 3,000,000 responses. You probably don't want to script running 3 million instances of masscan, but instead run it once for all those addresses. Do do this, run:

# masscan --banners -p445 -iL ips.txt --rate 100000

This will load the IP addresses from the file "ips.txt". The format is one address, CIDR address, or range per line, with lines starting with # ignored as comments. It'll take about 10 seconds to read in a file containing 3 million addresses, so don't be worried if it seems to hang for a bit. It doesn't matter if you sort the file or not: masscan sorts the file itself internally, then randomizes the order when transmitting packets.

By default, masscan transmits at a rate of 100 packets per second, in order to avoid accidentally melting networks. You'll probably want a faster rate, such as 100,000 packets per second. Masscan will give a status line estimating completion time, but in this case, it'll be wildly inaccurate. The estimate is based upon getting no response from servers, which is the norm when doing massive scans. But in this case, all the servers will response which will cause masscan to send at least an ACK packet followed by at least one data packet. This will usually continue with two more data packets and some FINs and FIN-ACKs. All these extra packets fit within the "rate" specified, which means the effective rate at establishing new connections will be a lot lower than the estimate.

If you want to just scan the entire Internet for SMB on port 445, the command would be:

# masscan --banners -p445 0.0.0.0/0 --rate 100000

I love scanning the /0 subnet.

The version information can be in different locations in the output line, depending on the target. To extract it, you can use grep:

grep -Eo "version=[0-9\.]*" scan.txt

Or, to grab only the numbers portion:

grep -Eo "version=[0-9\.]*" scan.txt | cut -d= -f2

To interpret the version numbers, this seems to be a good resource. I'm repeating the details here in case the link rots:

Operating SystemVersion Details
Version Number
Windows 10Windows 10 (1803)10.0.17134
Windows 10 (1709)10.0.16299
Windows 10 (1703)10.0.15063
Windows 10 (1607)10.0.14393
Windows 10 (1511)10.0.10586
Windows 1010.0.10240
Windows 8Windows 8.1 (Update 1)6.3.9600
Windows 8.16.3.9200
Windows 86.2.9200
Windows 7Windows 7 SP16.1.7601
Windows 76.1.7600
Windows VistaWindows Vista SP26.0.6002
Windows Vista SP16.0.6001
Windows Vista6.0.6000

The various Windows server version overlap these as well.

You can get the latest version of masscan from GitHub. It doesn't have any dependencies to build it other than a compiler (gcc or clang). It does need libpcap installed to run. It also needs root privileges to run, like any other libpcap application, or you setuid it. Lastly, since masscan has it's own IP address, you need to either use --source-ip [ip] to use a different IP address on your local subnet, or use --source-port [port] to use a source port you've otherwise firewalled to prevent the local stack from using it. Otherwise, the local stack will generate RST packets, preventing a connection from being established to grab the banner.

$ sudo apt-get install build-essential git
$ git clone https://github.com/robertdavidgraham/masscan
$ cd masscan
$ make
$ sudo iptables -A INPUT -p tcp --dport 60000 -j DROP
$ sudo bin/masscan --source-port 60000 -p445 --banners ....

By default, masscan waits 10 seconds for any responses to come back after a scan is complete. I add the parameter "--wait 40" to extend that to 40 seconds. Connections longer than 30 seconds are killed anyway due to timeout, so it's not really worth it to wait much longer than 30 seconds.

There's a lot of junk out there on port 445. Among the interesting stuff is that there are a lot of honeypots out there looking for scanners and worms. When you do a scan on this port, you'll get a lot of scans coming back at you for a couple days from such honeypots. One of the healthy things about using a spoofed source IP address is that you'll avoid the noise caused by these scans. Since I always spoof the source address in my scans (--source ip [ip]) I'll also set --wait forever as a parameter, to keep masscan running even after it's transmitted all its packets. This keeps it responding to ARP requests from the local router, so that I can also run tcpdump to capture all the noise that happens after a scan, for a couple days. Otherwise, if a stack with that IP address doesn't exist, the router will drop the packets instead of forwarding them, so you can't tcpdump capture them.

So the full command line might be:

# masscan --banners -p445 0.0.0.0/0 --rate 100000 --source-port 60000 --wait 40 > scan.txt

Conclusion

I've added SMB version checking natively to masscan. While simple in theory, his actually gets a bit complex, as described above. SMB is a nasty protocol, so a custom implementation like in masscan will get different results, for various reasons, then you might get with the Samba tool smbclient or nmap.


No comments: