Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save emdnaia/70ac7f315aed0adb4d6c418923480ded to your computer and use it in GitHub Desktop.
Save emdnaia/70ac7f315aed0adb4d6c418923480ded 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) (AES version)
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)
# AES is used, so you need impacket #1848 (https://github.com/fortra/impacket/pull/1848)
# 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.ComputeSessionKeyAES(None, clientchall, serverchall, tdo_hash)
clientcred = nrpc.ComputeNetlogonCredentialAES(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")
# 0x613FFFFF to use AES (0x600FFFFF to use RC4)
# 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, 0x613FFFFF)
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)
# switch to AE within rpct.py
dce.set_aes(True)
# 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.ComputeNetlogonAuthenticatorAES(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)
# switch to AE within rpct.py
dce.set_aes(True)
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