Forked from ThePirateWhoSmellsOfSunflowers/lsarlookupsids3_aes.py
Created
February 6, 2025 22:56
-
-
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)
This file contains 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) | |
# 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