Netlogon Protocol security
The highlighted items are the
ones we look at in this article.
Getting started with
A client computer that
wants to communicate with a Netlogon server such as a Windows domain
controller starts by sending eight random bytes (what’s often called a
nonce, short for number used once) to the server.
The server replies with 8
random bytes of its own, as explained in section 220.127.116.11,
REQUEST --> ClientChallenge (8 random bytes, e.g. 452fdbfd2e38b9e0) -->
REPLY <-- ServerChallenge (8 random bytes, e.g. 7696398fe5417372) <--
As shown above, Microsoft
refers to these nonces as
ClientChallenge (CC) and
ServerChallenge (SC) respectively, if you want to match up this
discussion with the protocol documentataion.
Both sides then scramble
up the two random strings together with a shared secret to concoct a
one-off encryption key, known as the
On a Windows network, the
secret component is the domain password of the computer you’re
On the client, this is
stored securely by Windows in the registry; on the domain controller,
it’s stored in the Active Directory database.
scrambling is done using the keyed cryptographic hash called
The algorithm is
specified in section 18.104.22.168.1, AES Session-Key, and in
pseudocode it looks like this:
Assuming that the client
requesting access has the same password stored locally as the Netlogon
server has on record centrally, and given that each side has already
told the other its 8-byte random challenge, both sides should now have
arrived at the same, one-off
SessionKey value (SK) to
secure the rest of their communication.
This session key setup
avoids using the secret password directly in encrypting Netlogon
traffic, and ensures a unique key for each session, into which both
parties inject their own randomness. (This is a common approach: setting
up a WPA-2 wireless connection using a pre-shared key follows a
similar process .)
In theory, the server
could blindly assume that the client knows the real password by simply
accepting encrypted function calls immediately; if the client had
cheated so far by using a made-up password, the requests wouldn’t
decrypt properly and the ruse would fail.
Good practice, however,
says that each end should verify the other first, for example by
encrypting a known test string that the other end can validate, and
that’s what happens next.
Obviously, the client
can’t share the session key directly because that would let anyone else
on the network sniff it out and hijack the session.
Instead, the client
proves that it knows the session key by encrypting the
ClientChallenge that it committed to at the start, using the
SessionKey it just calculated.
Microsoft calls this the
Netlogon Credential Computation, detailed in section 22.214.171.124.1:
At the other end, the
server does the same thing in reverse, and verifies that the original
ClientChallenge comes out correctly when the ciphertext is
decrypted with the session key.
At this point, it looks
as though an imposter client is stuck.
Without the right secret
password, which you can only get by already having administrator-level
access to a trusted computer on the network, you won’t get the same
session key as the server.
Without the right session
key, you won’t produce an encrypted version of your original 8-byte
random string that the server will accept to authenticate you.
A chink in the armour
At this point, if you’re
interested in cryptography, you’re probably wondering, “What on earth is
the encryption algorithm dubbed
AES-128-CFB8 in the
AES, short for
Advanced Encryption Standard, sounds like a good choice because
it’s currently accepted as a strong algorithm with no known security
Also, a key size of 128
bits is currently regarded as satisfactory because it would take too
long to try all possible 2 128 keys, even if you harnessed all
of the world’s computing power for yourself.
For the record: AES doesn’t
use any internal calculations that could be sped up with so-called
quantum algorithms ,
so it is considered
post-quantum secure .
Even if a truly powerful quantum computer were built tomorrow, it
wouldn’t be of any special use, so far as we know, in cracking AES
faster than we can with regular computers today.
But algorithms like AES
can be used in many different modes, and not all of them are suitable
for all purposes.
The best-known mode,
which you can think of as “straight encryption”, is called
AES-128-ECB, and it scrambles 16 bytes of input at a time,
directly producing 16 bytes of output.
Note that we’ve
simplified these diagrams by pretending that AES-128 works on 4 bytes
(32 bits) at a time instead of 16 bytes (128 bits), but the underlying
principles are still perfectly clear:
ECB stands for
Electronic Code Book, because the cipher in this mode does indeed
work like an unimaginably large codebook.
The codebook moniker is
entirely theoretical. In practice, you would need a different codebook
for every one of the 2 128 different keys, with each book
listing every one of the encryption values for each of the 2 128
different 16-byte input strings. And you would need a further 2 128
(that’s 300 million million million million million million) codebooks
to list all the possible decryptions, too, if you ever had the space or
time to unscramble what you had previously encrypted.
simplicity of codebook mode is also a weakness, because any time there
is repeated text in one of the input blocks, you’ll know because you’ll
get a repeat in the ciphertext, too:
At best, ECB leaks
whether there are any patterns in the input, something that an
encryption algorithm should conceal.
At worst, it means that
if ever you figure out what the plaintext was in one part of the input –
a chapter heading, for example, or part of a Bitcoin address – you will
automatically be able to decrypt that text everywhere else it appears.
Various solutions exist
to use block-based encryption algorithms so they don’t reveal repeated
patterns, and one of them is Cipher Feedback (CFB) mode, which
works like this:
Instead of encrypting the
plaintext blocks with AES each time, you encrypt the last block of
ciphertext instead, and then XOR that “keystream” with the next block of
That way, even if you get
two identical plaintext blocks in a row, the ciphertext won’t repeat.
Of course, there is no
ciphertext block to use at the outset, so
requires not only a key of 16 bytes for the encryption engine, but also
initialisation vector (IV) of 16 bytes as an up-front input
to get the keystream started.
Note that the IV can, and
usually is, shared along with the ciphertext – the IV doesn’t need to be
kept secret, because the secrecy of the encryption is provided by the
key that controls the AES encryption engine.
Nevertheless, a CFB
initialisation vector should be chosen randomly, and should never be
re-used, especially with the same AES key.
One disadvantage that
AES-ECB and AES-CFB have in common is that until you have a full 16-byte
block of input, you can’t produce any output, because they can’t work on
partial blocks – AES is designed to mix-and-swap-and-mince-and-munge
chunks of 128 bits at a time.
That also means you are
stuck if you have any leftover bytes at the end, for example if you have
67 bytes to encrypt, which is 4×16 + 3.
You need to figure out a
way to pad out the last block to the right size, and then reliably work
out whether there were any extra bytes added on that need to be stripped
off when you decrypt the data.
One solution to this is
AES-CFB8, a mode that we have never heard of anyone using in real life
before, but that is designed to use a full 128-bit AES mixing cycle for
every byte of input, so you can encrypt even just a single
character without any padding.
Instead of encrypting the
last full block of ciphertext to create the next full block of keystream
data, you use just the first byte of the keystream each time and XOR it
with one plaintext byte rather than a 16-byte plaintext block.
Then you chop off the
keystream byte you just used and add the new ciphertext byte on at the
end of the keystream, giving you a full block of data to encrypt to
generate the next keystream byte:
Netlogon CFB8 considered
Sadly, the way that
AES-128-CFB8 is significantly less secure
than it should be.
spotted the problem very quickly when perusing the Microsoft
documentation, where the algorithm is not defined generically (as we
listed it above), but given in a dangerously simplified form.
specifies the AES Credential [Computation] process as follows:
If AES support is negotiated between the client and the server,
credentials are computed using the AES-128 encryption
algorithm in 8-bit CFB
mode with a zero initialization vector.
[Sk below is short for SessionKey]
ComputeNetlogonCredential(Input, Sk, Output)
SET IV = 0
CALL AesEncrypt(Input, Sk, IV, Output)
You probably spotted the
cryptographic blunder already: “the credentials are computed […] with
a zero initialization vector.”
As we already mentioned,
IVs are supposed to be randomly chosen, and used only once with any key
– indeed, that’s why they are often referred to as nonces, for
numbers used once.
But there’s an even
bigger problem with an all-zero IV in CFB8 mode, as Secura discovered.
You can visualise the
problem if you use an all-zero IV plus an all-zero block of plaintext
Because AES is a
high-quality cipher with no known statistical biases, you can put in any
input and encrypt it with any key, and the chance of each individual bit
in the output being zero (or one) is 50%.
Every output bit’s value
is like a digital coin toss.
So the chance of the
first output byte being zero is the same as the chance that the first 8
output bits are all zero, which is 50% × 50% × 50% … eight times over
(50% is just another way of writing 0.50, which is the same as 1/2).
50% 8 is 2 -8,
In the diagram above
we’ve assumed that the first output byte did indeed come out as zero,
and you can see that if that happens, the entire encryption process
essentially gets “locked into” an all-zero state.
The keystream byte
(black) comes out as 00, so when you XOR it with the first plaintext
byte (pink) of 00 you get a ciphertext byte (red) of 00.
Then, when you chop the
first 00 off the left hand end of the IV (white) and append the new
ciphertext 00 at the end, you are right back where you started, with
another all-zero IV and a remaining plaintext buffer of all zeros.
When you encrypt the
“new” IV with the key, you get exactly the same result as before,
because all your inputs are the same again, and out comes another
keystream byte of 00, which XORs with the next plaintext 00 to produce
another ciphertext byte of 00, which feeds back into the IV to make it
all zero again.
How to trick Netlogon
quickly realised what would happen if they tried to authenticate to a
Netlogon server over and over again using a
nonce consisting of 8 zeros.
Roughly once in every 256
times the server would randomly concoct a session key for which the
correctly-encrypted version of their all-zero
…would itself be all
We tried an al-zero IV
with an all-zero ClientChallenge 2560 times.
One in 256 times the key chosen gave all-zero output too.
In other words, by
ClientChallenge of 0000000000000000 and then
blindly also submitting a
Netlogon Credential Computation (see
above) of 0000000000000000, they’d get the credential computation
correct by chance 1/256 of the time, even though they had no idea what
SessionKey value should be because they had no
idea what secret password to use.
Simply put, 1/256 of the
time, they ended up in a situation where they could always produce
correctly-encrypted data to transmit to the server, without having a
clue what the password or session key was, as long as they only ever
needed to encrypt zeros!
Better yet, the server
would automatically notify them when they hit the jackpot by accepting
their credential submission.
Surely that’s not
By now you are probably
thinking, “What’s the chance that every time they needed to submit an
encrypted authentication token or to supply encrypted password data,
they’d only ever need to encrypt zeros?”
We wondered that too, but
our intrepid researchers found a way.
One of the Netlogon
126.96.36.199.5), can be called remotely from a Netlogon session that has
already got past the
Netlogon Credential Computation check.
This function, which does
what its name suggests and changes the server password, requires the
caller to correctly encrypt two chunks of data:
ClientChallenge, treated as a 64-bit number, with the current
time (in what’s known as “Posix seconds” or Unix epoch form) added
to it. This data is used as an authentication check to ensure it’s
still the same client program trying to do the password change.
A buffer of 516
bytes that specifies the new password, formatted as (512-N) bytes of
random data, followed by N bytes specifying the password, followed
by the password length N expressed as a 4-byte number.
is all zeros, because that was needed to get the exploit started in the
first place, but the current time in Posix seconds is something close to
$ date --utc +%s
Posix time denotes the
number of seconds since the start of the Unix epoch, which began, by
definition, at 1970-01-01T00:00:00Z, a date now more than 50 years in
The researchers found
themselves on the horns of a dilemma: the
was zero, but the time was not, so the sum of those two numbers couldn’t
be zero, and therefore wouldn’t encrypt to zero…
…and therefore the
attackers would need the original session key after all, and to get the
session key they would need to know a valid password for a suitable
computer on the network.
What to do?
Well, the researchers
just pretended it was 1970 all over again, used a timestamp of zero
added to a
ClientChallenge of zero…
…and the server didn’t
mind – there was apparently no check to see if the timestamp was decades
in the past.
Of course, the 516
all-zero bytes that the researchers now needed to supply in the
encrypted password buffer forced them to specify a password length of
zero, which you might think would be disallowed by the server.
But the researchers tried
…and the server didn’t
mind that either, setting its own Active Directory password to <no
password at all>.
Happily – or perhaps
slightly less unhappily – the password change that they were able make
didn’t reset the server’s actual login password, so the researchers
couldn’t simply login directly and take over the server via a
conventional Windows desktop.
However, they did report
that by changing the Active Directory password of the domain controller
were able to :
extract all user
hashes from the domain through the Domain Replication Service (DRS)
protocol. This includes domain administrator hashes (including the
‘krbtgt’ key, which can used to create golden tickets) that could
then be used to login to the Domain Controller (using a standard
pass-the-hash attack) and update the computer password stored in the
Domain Controllers’s local registry.
In other words, complete
All because of an
over-simplified cryptographic specification that involved the cardinal
sin of an all-zero initialisation vector every time.
Of course, that flaw was
compounded by several other programmatic oversights where stricter
attention to security and correctness could have prevented this attack,
in the first
place. We’d assume that the most likely cause of an
all-zero buffer at the start of the Netlogon process would be an
incorrectly initialised or buggy client program, so we’d reject it
as a precaution anyway.
zero-length password. Given that Windows already has a
secure mechanism for storing shared secrets, and relies on it
heavily anyway, it seems unnecessary to allow blank passwords at
all, even for accounts where no humans are ever expected to log on.
date-based authentication field in which the timestamp could not
possibly be correct. We’d be inclined to treat this as a
warning of a buggy client or an attempt to pull off a security
What to do?
This bug opens a serious
security hole to anyone already inside your organisation, and perhaps
even to outsiders, depending on the topology of your network.
If you haven’t applied
the August 2020 patch yet, you need to do so – you aren’t just letting
yourself down, you’re letting everyone else down too by making your
network an easier target for crooks, and therefore making it more likely
that you will be the source of security problems for other people.
cryptographic shortcuts such as choosing an encryption mode
that’s convenient for your application, but then taking liberties
with how you use it because that’s convenient for your programmers.
defensively whenever you are accepting untrusted data,
especially if the data can easily be checked for obviously forged or
incorrect values such as timestamps 50 years in the past.
parts of your products or specifications as soon as you can after
better ones are available. Although the exploit in this
case relied on updated parts of the Netlogon protocol, such as using
AES instead of falling back to older algorithms, you can argue that
this bug might have been found far sooner if the protocol
specification were not encumbered with so many alternative ways of
doing all sort of security-related checks. But the big thing to
remember here is: patch early, patch often.