zerologon packet analysis
2022-07-17
Background
Vulnerabilities are discovered on a daily basis but there are a couple that stand out due to their severity level and how easy it is to execute. Since one of the best ways to learn is to actually do the thing instead of reading an article or blog post about it, thought I'd experiment solo to try and understand how these vulnerabilities and exploits work. The importance here is to leave no stone unturned, learn everything and anything about them no matter how indepth and 'difficult' it gets. From what I remember, ZeroLogon was a vulnerability found by the organization Secura although I have no idea what that org. does. It's a vulnerability that can go from no account to domain level access somehow, and thus relates to AD. Active Directory has become a stable in information security lately, so soon they'll include them in syllabus of university classrooms and degrees for cybersecurity is what I believe... Perfect chance for an indepth look at it.Initial Research
Requirements: High level understanding of ZeroLogon
NetrServer[ReqChallenge, Authenticate3, SetPassword2]
Packet Analysis
Secura PoC teardown
The best method of understanding how this vulnerability works is not just
reading a whitepaper released by the guys who found it, but to really understand
the inner workings of all the components and be able to pull it off myself. This
is what the requirements enable us to do; I'd like to note that initial research
has been done via a TryHackMe room named 'ZeroLogon' under the Cyber Defense
pathway. TryHackMe is a great way to learn that 'x' information exists but not
the best method to learn it, which is what this project hopefully will do.
TryHackMe\Cyber Defense\ZeroLogon\Secura Whitepaper
A python PoC is also one of the requirements which will be a PoC provided by
Secura. I'm not a programmer nor do I care to learn programming too indepth, but
one of the foundations of information security is to be able to read code! Which
is why review of a python PoC is perfect in this instance. Secura Python PoC
Secura ZeroLogon Whitepaper
MS-NRPC & ZeroLogon
The vulnerability exists due to poor implementation of cryptography regarding the use of a hard-coded IV. Typically if the IV is hard-coded or can be guessed then that reveals an attack surface for the bad guys, which is exactly what this case is. MS-NRPC is a protocol that authenticates user & machine accounts for AD (Active Directory), so authentication between client and user is important. It's important to note that there is no limit to account logon attempts here because we try to log into a machine account which has a really long password and thus the guys at Microsoft thought noone would bruteforce it anyways so why put a limit to it.
# ---[ aes_cfb8.py ]---
iv = b"\x00" * 16
def encrypt(bs: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_ECB)
res = list(iv + bs)
iv_len = len(iv)
for i in range(len(bs)):
res[iv_len + i] = cipher.encrypt(
bytes(res[i:i+iv_len]))[0] ^ res[iv_len + i]
return bytes(res[iv_len:])
gh\kurenaif\aes_cfb8wikipedia\aes_cfb8
The encryption algorithm used by Microsoft for MS-NRPC is AES-CFB8 which is not a bad encryption algorithm by any means. But if you notice the first line, the IV (Init. Vector) is hardcoded and all set to 0's, exactly done by Microsoft.
Steps: NetrServerReqChallenge [Client -> Server]
Server Challenge [Server -> Client]
NetrServerAuthenticate3 [Client -> Server]
Compute+Compare Values [Server -> Client]
The steps provided are those that will test for whether ZeroLogon is exploitable
or not in your case. We don't have a full kill chain as this only checks that
the vulnerability exists, not tries to change any passwords and log in. That
will be shown later. Steps 3 & 4 is where the loop happens and on average 1/256
chance you will get both client computed and server computed values to be the
same, thus authenticating you to the domain. We'll first look at both the Netr
Auth and Challenge functions to understand what really is going on, this will
also help in understanding AD - perfect for OSCP down the road I hope?
NetrServerReqChallenge
NTSTATUS NetrServerReqChallenge(
[in, unique, string] LOGONSRV_HANDLE PrimaryName,
[in, string] wchar_t* ComputerName,
[in] PNETLOGON_CREDENTIAL ClientChallenge,
[out] PNETLOGON_CREDENTIAL ServerChallenge
);
Similar to many login methodologies systems exist to check whether the client
is who they say they are, also confirming they hold the correct password. The
function above tests for this by sending the parameters labeled [in]:
PrimaryName, ComputerName and ClientChallenge. The primary name is the handle
used during binding (mentioned later), the computer name is the name of the
client computer (spoofed name in this case DC01), and finally the challenge we
input is all zero's.Wireshark ServerReqChallenge Client

Wireshark ServerReqChallenge Server

Sidenote: CVE-2019-1424 [Netlogon MiTM]
After comm' explained above, the same author who discovered ZeroLogon also had discovered another vulnerability (less harsh) prior to Zerologon, this is CVE 2019-1424 which is gaining access to windows systems via Man-in-The-Middle attack. This CVE is not the importance of this project, but I looked into it a bit and this occurs when the attacker sends a RST packet right after the session has been authenticated and is starting to take shape. This causes comm' to take place over SMB instead of EPM and Netlogon default port, resulting in dropping of encryption between the client and server. Attackers could modify comm' from now on even though there is an authenticator field in the first message it is not used in concurrent messages so leaving it alone is sufficient.CVE-2019-1424 by Tom Tervoort, Secura
Sidenote: CVE-2019-1424 [Netlogon MiTM]
NTSTATUS NetrServerAuthenticate3(
[in, unique, string] LOGONSRV_HANDLE PrimaryName,
[in, string] wchar_t* AccountName,
[in] NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
[in, string] wchar_t* ComputerName,
[in] PNETLOGON_CREDENTIAL ClientCredential,
[out] PNETLOGON_CREDENTIAL ServerCredential,
[in, out] ULONG * NegotiateFlags,
[out] ULONG * AccountRid
);
Quickly going over through the parameters of this authenticate function, similar
to the previous request challenge function we have PrimaryName, ComputerName.
AccountName refers to the account we're trying to log into, SecureChannelType
is the channel type we use (e.g Backup domain controller). Both client and
server credentials are computed values that take in the previous challenges that
were generated in the previous function (NetrServerReqChallenge). The negotiate
flags contain flags that set encryption types, supported function calls and more
things that can be done. AccountRid is the identifier the account belongs in,
think somewhat similar to Unix groups and users, it is also unique.
