Our goal at Rumble is to help customers identify everything on their networks, quickly, and without authentication. This process is driven by research, which often leads to dead ends, but sometimes we learn interesting things along the way. This post explores recent research into remote fingerprinting of Windows build numbers and some of the surprising results.
1803, 1909, 2004, 20H2, and 21H1 are maybe not the most memorable names, but knowing the specifics of these Windows 10 and Server 2019 releases is an important part of managing a Windows environment, especially as it applies to end-of-life (support) dates. Windows 10 version 2004 reaches end of support on December 13, 2021--not too far away! By contrast, the most recent release build (21H1) is supported through December 14, 2022. The problem is that both versions report the same build number in network scans.
Rumble uses a bunch of tricks to determine operating system versions, but one of the most useful is the build number provided through the NTLMSSP authentication sequence. The build number on recent releases looks something like 10.0.19041; this can refer to either the workstation OS (Windows 10) or the server OS (Server 2019), and telling those apart is a challenge on its own. The NTLMSSP response is available through any NTLM-enabled service: SMB, RDP, and MSRPC, and sometimes HTTP servers.
Will the real 19041 please stand up? #
Windows 10 2004, 20H2, 21H1, and 21H2 preview builds all report the same build number of 10.0.19041, instead of the expected sequence of 10.0.19041 through 10.0.19044. This makes end-of-life reporting tricky, since we can't provide an accurate date without knowing exactly which OS version we are looking at. This issue also applies to version 1903 and 1909 (they both report 10.0.18362).
Figuring out the difference sounds like a job for science!
We started off installing one of everything in our test lab and went down the list of default services. Are there any differences in the TCP/IP stacks? How about the TCP window size? Does the EPM list any differences in RPC services? Is anything different with RDP or SMB?
Nope. No changes at all between any of the four different Windows 10 versions that report build number 10.0.19041. Maybe we need more data.
We reviewed the DCERPC EPM services for a little over 200,000 Windows systems and built a table of minimum observed build number versus UUID. This work was useful in that it can flag the oldest possible version associated with a build, but it didn't help with two specific cases: differentiating between versions 1903 and 1909 and fingerprinting specific builds between the versions 2004 and 21H2. There was still no obvious way to tell these apart. A partial extract of this UUID table is shown below.
| DCERPC UUID | First Build ||-------------------------------------","------------|| b77ee4ec-c5b0-4b5f-8768-76d756e5d647 | 5.2.3790 || b8007262-7c16-492a-928b-d3689992ef05 | 6.1.7601 || b8cadbaf-e84b-46b9-84f2-6f71c03f9e55 | 6.1.0 || b8ff9393-2cdf-427b-9bb8-441226b71ed6 | 6.1.7601 || b91eaff4-050c-2b49-4b50-525250524f50 | 10.0.18362 || b91eaff4-10a4-09c1-4b50-525250494453 | 10.0.22000 || b91eaff4-131b-2c2f-4b50-525250494453 | 10.0.19041 || b91eaff4-1441-c93d-4b50-525250524f50 | 10.0.18362 |
On a lark, we also looked into the WinRM service (5985/5986). WinRM (over WS-MAN) provides a path to query the build number, but this normally requires authentication. After poking around with the API, we found that sending a magic HTTP header allowed access to this endpoint without authentication, but doing so causes Windows to report back an all-zero version number. This ended up being useful for detecting the OMI WS-MAN services (they report the real version in this case), but left us at square one for Windows build fingerprinting.
Reading the docs #
A wise person once said: "when in doubt, turn to the documentation". We dove into the Windows release notes to see if there were any advertised changes we might be able to measure from the network. This paragraph stopped us cold (the emphasis is ours):
Windows 10, versions 21H1, 20H2, and 2004 share a common core operating system and an identical set of system files. As a result, the release notes for Windows 10, versions 21H1, 20H2, and 2004 will share an update history page. Each release page will contain a list of addressed issues for 21H1, 20H2, and 2004 versions. Note that the 21H1 and 20H2 versions will always contain the fixes for 2004; however, 2004 will not contain the fixes for 21H1 and 20H2.
Well, that answers that. Systems reporting build 10.0.19041 for Windows versions 2004 through 21H2 are the same operating system with identical system files. The only difference is how long a specific build gets updates from the same channel. If you install and then fully update versions 2004 and 21H1, you end up with the same operating system (outside of what WinVer tells you). This was not the expected outcome and we needed to change how Rumble reports these versions and their end-of-life system.
A range of potential versions is better than nothing, but it means that our end-of-life tracking has to assume the newest possible version for the reported build number in order to avoid false positives. Starting with Rumble v2.7.0, Windows OS versions are now reported and tracked for EOL status using these ranges:
The good news is that sometimes we can do better. In a few cases, specific DCERPC services are available that help us ratchet up the minimum version, and any connections to CrowdStrike or Miradore will pull in the exact build number every time. Rumble will use anything you give it to provide the most accurate fingerprint, but this still wasn't a satisfying outcome.
Digging into the specifications #
When the normal documentation falls short, it's time to dig into the specifications. One underappreciated outcome from the Microsoft anti-trust settlement is that the company was required to make protocol specifications open to competitors. Once they started down this path, the company seemed to embrace the open approach, and now many parts of Windows have an amazing level of protocol documentation. What makes this even more amazing is these documents have detailed diffs and a beautiful appendix named "Product Behavior". This documentation provides granular details on what various Windows versions do at the protocol level and how it has changed over time. Finally, some hope!
A quick search for specific Windows versions (1809, 2004, 21H1, etc) turned up quite a few results where significant protocol changes occurred. Sure, there are hundreds of pages of results, but this is finite!
The next few hours were spent skimming through the product behavior appendices and mapping these changes back to unauthenticated functionality of the related protocol. There are a ton of changes, but most of them are not things a network scanner can measure easily. Finally, one of our old friends showed up on the list: MS-SMB2.
We love this document. That may sound weird, but prior to Microsoft publishing these specifications, most work on the SMB protocol was a mix of binary analysis and packet captures. It still mostly is, but now we have something to compare them against, often with names and descriptions. The diffs and product behavior sections looks promising, especially the sections that reference the Negotiation Context processing.
In SMB v3 parlance, the Negotiation Context is a set of Type-Length-Value fields appended to the end of the Negotiate Request. Clients are supposed to send any contexts they want to use and servers are supposed to ignore unknown context types and reply to the ones they support. This process happens before the authentication phase and is perfect for our fingerprinting needs.
Parsing product behavior #
A few items in the product behavior notes stood out.
Windows 10 v1809 operating system operating system and prior and Windows Server v1809 operating system operating system and prior do not send or process SMB2_COMPRESSION_CAPABILITIES.
This dovetails with an item further down:
Windows 10 v1903 operating system and later and Windows Server v1903 operating system and later set IsCompressionSupported to TRUE.
Excellent, so if the SMB service supports compression, it must be version 1903 or newer.
Windows 10 v2004 and later and Windows Server v2004 and later operating systems set IsChainedCompressionSupported to TRUE.
Even better, if the flags indicated chained compression, this must be 2004 or newer.
Windows 10 v1903, Windows 10 v1909, Windows Server v1903 and Windows Server v1909 operating systems initialize with LZ77(0x0002) followed by LZ77+Huffman(0x0003) followed by LZNT1(0x0001).
This matches up with:
Windows 10 v2004 and Windows Server v2004 operating systems initialize with Pattern_V1(0x0004) followed by LZ77(0x0002) followed by LZ77+Huffman(0x0003) followed by LZNT1(0x0001).
In addition to the compression and chaining flag, we can also use the selected compression algorithms to determine the minimum version.
Windows 10 v1809 operating system and prior and Windows Server v1809 operating system and prior do not send or process SMB2_NETNAME_NEGOTIATE_CONTEXT_ID.
This context is used to provide a name to in-line load balancers and acts kind of like TLS's Server Name Indicator (SNI). Unfortunately, SMB servers do not acknowledge or reply to this context, and it isn't useful as a result.
Windows 10 v1909 operating system and prior and Windows Server v1909 operating system and prior do not send or process SMB2_TRANSPORT_CAPABILITIES.
This claims that 2004 and newer will process the transport capabilities context, lets look into this.
Windows 10 v2004 operating system and prior and Windows Server v2004 operating system and prior do not send or process SMB2_RDMA_TRANSFORM_CAPABILITIES.
This aligns with:
Windows 10 v20H2 and later and Windows Server v20H2 and later set IsRDMATransformSupported to TRUE.
This sounds like a way to differentiate 2004 from newer builds, like 20H2 and 21H1. Exciting!
Windows 10 v20H2 operating system and prior and Windows Server v20H2 operating system and prior do not send or process SMB2_SIGNING_CAPABILITIES.
Another one, even better!
Windows 10 v21H1 and Windows Server 2022 initialize with AES-GMAC(0x0002) followed by AES-CMAC(0x0001) followed by HMAC-SHA256(0x0000).
Hurray! The signing capabilities cipher ID should indicate if a system is 21H1 or newer.
Windows 10 v21H1 and Windows Server 2022 initialize with AES-128-GCM(0x0002), followed by AES-128-CCM(0x0001), followed by AES-256-GCM(0x0004), followed by AES-256-CCM(0x0003).
Great! If the system supports AES-256-GCM, this must be 21H1 or newer.
Creating a ruleset #
Digesting the above, it sounds like we can send a specific set of Negotiate Context sequences and determine some useful version information from the response.
- If there is no compression support, this is 1809 or older.
- If compression is supported, but chaining is not, this is 1903 or 1909.
- If compression chaining or transport capabilities are supported, this is 2004 or newer.
- If RDMA transforms or signing capabilities are supported, this is 20H2 or newer.
- If signing capabilities support AES-GMAC or encryption supports AES-256-GCM, this is 21H1 or newer.
Theoretically, these rules cover the versions we are trying to differentiate between, except for the case of 1903 vs 1909. Time to write up a SMB v3 Negotiate Context implementation for Rumble and see how things look in the real world.
Time passes and ... crud.
Version | Real | NTLMSSP | Encryption | Comp | Ch | RDMA | Sig ||-------------","-----","---------","-----------","----------","--","--------","---------|| Win 10 1903 | 18362 | 18362 | aes-128-gcm | lz77 | 0 | | || Win 10 1909 | 18363 | 18362 | aes-128-gcm | lz77 | 0 | | || Win 10 2004 | 19041 | 19041 | aes-128-gcm | lz77,patv1 | 1 | | || Win 10 20H2 | 19042 | 19041 | aes-128-gcm | lz77,patv1 | 1 | | || Win 10 21H1 | 19043 | 19041 | aes-128-gcm | lz77,patv1 | 1 | | || Win 10 21H2 | 19044 | 19041 | aes-128-gcm | lz77,patv1 | 1 | | || Win 10 21390 | 21390 | 21390 | aes-256-gcm | lz77,patv1 | 1 | enc,sign | aes-gmac || Win 11 22000 | 22000 | 22000 | aes-256-gcm | lz77,patv1 | 1 | enc,sign | aes-gmac || Server 2022 | 20348 | 20348 | aes-256-gcm | lz77,patv1 | 1 | enc,sign | aes-gmac |
Lies. Mostly! #
It is still not possible to differentiate between 1903 and 1909 or any of the build 19041 variants (2004-21H2).The Windows 10 release notes were correct, but the MS-SMB2 documentation is wrong. Any reference to a Windows 10 or Server 2019 build newer than 2004 in the MS-SMB2 product behavior notes actually applies to Windows 11 and Server 2022 instead. This would be useful, except the version reported by those systems via the NTLMSSP build number is actually correct, and we don't need to do more precise fingerprinting (yet).
Unlike the MS-SMB2 notes, the Azure File System documentation addresses the fact that no Windows 10 build will actually negotiate AES-GCM-256 SMB encryption, but is also wrong about the reason:
Azure Files supports AES-256-GCM with SMB 3.1.1 when used with Windows Server 2022 or Windows 11. SMB 3.1.1 also supports AES-128-GCM and SMB 3.0 supports AES-128-CCM. AES-128-GCM is negotiated by default on Windows 10, version 21H1 for performance reasons.
In reality, this is not negotiated because the functionality never shipped with Windows 10 and instead was first implemented in Windows 11 and Server 2022. Technically the Windows 10 Cobalt builds (21390) supported this, but those are also known as the Windows 11 previews, and they never shipped as a stable Windows 10 release.
We are right back to square one again.
Summary #
We spent far too much time reading inaccurate documentation and implementing the latest bits of SMB v3, but still failed to significantly improve our fingerprinting of Windows 10 1903/1909 or builds 2004 through 21H2. With any luck, this ~~rant~~article will be helpful to other folks who are interested in Windows fingerprinting, and the SMB v3 implementation in Rumble will become more useful with future Windows versions. If you made it this far and would like the stuff above to be your job, drop us an email at careers[at]rumble.run (remote, but US only).
Big thanks to my colleague and our principal security researcher, Pearce Barry, who drove the DCERPC UUID analysis and put up with me ranting all weekend about protocol documentation that lies.
The new SMB v3 probe, OS reporting changes, and EOL tracking updates will be available in the Rumble 2.7 release going out next week.