mirror of
https://github.com/fortra/impacket
synced 2026-06-08 14:15:13 +00:00
ae0ec300f8
* Standardized examples logger init method * Fixing missing import * Standardized examples init_ldap_session function #1 * Calling new shared function _ldap3_kerberos_login from example addcomputer * Removed considering -debug in each example. Now it's handled in utils. * Standardized EMPTY_LM_HASH in impacket.examples.utils * Unify parse_identity function (phase 1) * Standardized parse_identity * Renamed "_ldap3_kerberos_login" to "ldap3_kerberos_login" (as it is called from outside) * Standardize "logger" init in regsecrets.py * Standardized LDAP login into a single function in utils.py:ldap_login
176 lines
7.1 KiB
Python
176 lines
7.1 KiB
Python
#!/usr/bin/env python
|
|
# Impacket - Collection of Python classes for working with network protocols.
|
|
#
|
|
# Copyright Fortra, LLC and its affiliated companies
|
|
#
|
|
# 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:
|
|
# Through MS-DSSP, this script retrieves a host's role along with
|
|
# its primary domain details.
|
|
#
|
|
# This may be particularly useful when it is used in a script where
|
|
# further operations depend on knowing the role of its target,
|
|
# e.g. "I do not want to perform this on a DC".
|
|
#
|
|
# Author:
|
|
# Simon Decosse (@simondotsh)
|
|
#
|
|
|
|
import sys
|
|
import logging
|
|
import argparse
|
|
|
|
from impacket.examples import logger
|
|
from impacket.examples.utils import parse_target
|
|
from impacket import version
|
|
from impacket.uuid import bin_to_string
|
|
from impacket.dcerpc.v5 import transport, dssp
|
|
|
|
class MachineRole:
|
|
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dssp/09f0677f-52e5-454d-9a65-0e8d8ba6fdeb
|
|
MACHINE_ROLES = {
|
|
dssp.DSROLE_MACHINE_ROLE.DsRole_RoleStandaloneWorkstation:
|
|
'Standalone Workstation',
|
|
dssp.DSROLE_MACHINE_ROLE.DsRole_RoleMemberWorkstation:
|
|
'Domain-joined Workstation',
|
|
dssp.DSROLE_MACHINE_ROLE.DsRole_RoleStandaloneServer:
|
|
'Standalone Server',
|
|
dssp.DSROLE_MACHINE_ROLE.DsRole_RoleMemberServer:
|
|
'Domain-joined Server',
|
|
dssp.DSROLE_MACHINE_ROLE.DsRole_RoleBackupDomainController:
|
|
'Backup Domain Controller',
|
|
dssp.DSROLE_MACHINE_ROLE.DsRole_RolePrimaryDomainController:
|
|
'Primary Domain Controller'
|
|
}
|
|
|
|
def __init__(self, username='', password='', domain='', hashes=None,
|
|
aesKey=None, doKerberos=False, kdcHost=None, port=445):
|
|
self.__username = username
|
|
self.__password = password
|
|
self.__domain = domain
|
|
self.__lmhash = ''
|
|
self.__nthash = ''
|
|
self.__aesKey = aesKey
|
|
self.__doKerberos = doKerberos
|
|
self.__kdcHost = kdcHost
|
|
self.__port = port
|
|
|
|
if hashes is not None:
|
|
self.__lmhash, self.__nthash = hashes.split(':')
|
|
|
|
def print_info(self, remoteName, remoteHost):
|
|
try:
|
|
dce = self.__authenticate(remoteName, remoteHost)
|
|
except Exception as e:
|
|
self.__log_and_exit(str(e))
|
|
|
|
try:
|
|
output = self.__fetch(dce)
|
|
except Exception as e:
|
|
self.__log_and_exit(str(e))
|
|
|
|
for key, value in output.items():
|
|
print('%s: %s' % (key, value))
|
|
|
|
dce.disconnect()
|
|
|
|
def __authenticate(self, remoteName, remoteHost):
|
|
dce = self.__get_transport(remoteName, remoteHost)
|
|
|
|
dce.connect()
|
|
dce.bind(dssp.MSRPC_UUID_DSSP)
|
|
|
|
return dce
|
|
|
|
def __get_transport(self, remoteName, remoteHost):
|
|
stringbinding = r'ncacn_np:%s[\pipe\lsarpc]' % remoteName
|
|
logging.debug('StringBinding %s' % stringbinding)
|
|
rpctransport = transport.DCERPCTransportFactory(stringbinding)
|
|
rpctransport.set_dport(self.__port)
|
|
rpctransport.setRemoteHost(remoteHost)
|
|
|
|
if hasattr(rpctransport, 'set_credentials'):
|
|
# This method exists only for selected protocol sequences.
|
|
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
|
|
self.__nthash, self.__aesKey)
|
|
rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
|
|
|
|
return rpctransport.get_dce_rpc()
|
|
|
|
def __fetch(self, dce):
|
|
output = {}
|
|
domain_info = dssp.hDsRolerGetPrimaryDomainInformation(dce, 1)
|
|
|
|
output['Machine Role'] = self.MACHINE_ROLES[domain_info['DomainInfo']['DomainInfoBasic']['MachineRole']]
|
|
output['NetBIOS Domain Name'] = domain_info['DomainInfo']['DomainInfoBasic']['DomainNameFlat']
|
|
output['Domain Name'] = domain_info['DomainInfo']['DomainInfoBasic']['DomainNameDns']
|
|
output['Forest Name'] = domain_info['DomainInfo']['DomainInfoBasic']['DomainForestName']
|
|
output['Domain GUID'] = bin_to_string(domain_info['DomainInfo']['DomainInfoBasic']['DomainGuid'])
|
|
|
|
return output
|
|
|
|
def __log_and_exit(self, error):
|
|
logging.critical('Error while enumerating host: %s' % error)
|
|
sys.exit(1)
|
|
|
|
if __name__ == '__main__':
|
|
print(version.BANNER)
|
|
|
|
parser = argparse.ArgumentParser(add_help=True, description='Retrieve a host\'s role along with its primary domain details.')
|
|
|
|
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
|
|
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')
|
|
|
|
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 '
|
|
'ommited 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', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
|
|
help='Destination port to connect to SMB Server')
|
|
|
|
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)')
|
|
|
|
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, options.debug)
|
|
|
|
domain, username, password, remoteName = parse_target(options.target)
|
|
|
|
if domain is None:
|
|
domain = ''
|
|
|
|
if options.target_ip is None:
|
|
options.target_ip = remoteName
|
|
|
|
if options.aesKey is not None:
|
|
options.k = True
|
|
|
|
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:')
|
|
|
|
machine_role = MachineRole(username, password, domain, options.hashes, options.aesKey, options.k, options.dc_ip, int(options.port))
|
|
machine_role.print_info(remoteName, options.target_ip)
|
|
|