コード例 #1
0
 def parse_gmsa(self, user, entry):
     """
     Parse GMSA DACL which states which users can read the password
     """
     _, aces = parse_binary_acl(user, 'user', ADUtils.get_entry_property(entry, 'msDS-GroupMSAMembership', raw=True), self.addc.objecttype_guid_map)
     processed_aces = self.aceresolver.resolve_aces(aces)
     for ace in processed_aces:
         if ace['RightName'] == 'Owner':
             continue
         ace['RightName'] = 'ReadGMSAPassword'
         user['Aces'].append(ace)
コード例 #2
0
    def enumerate_gpos(self):
        gpos = []
        resolver = AceResolver(self.addomain, self.addomain.objectresolver)
        entries = self.addc.get_gpos()
        for entry in entries:
            gpo = {
                    "Properties": {
                        "highvalue": ADUtils.get_entry_property(entry, 'isCriticalSystemObject', default=False),
                        "name": ADUtils.get_entry_property(entry, 'displayName'),
                        "domain": '.'.join(str(ADUtils.get_entry_property(entry, 'distinguishedName')).split('DC')[1:]).translate({ord(c):'' for c in '=,'}),
                        "objectid": str(ADUtils.get_entry_property(entry, 'objectGUID')).translate({ord(c):'' for c in '}{'}),
                        "distinguishedname": ADUtils.get_entry_property(entry, 'distinguishedName'),
                        "description": None,
                        "gpcpath": ADUtils.get_entry_property(entry, 'gPCFileSysPath')
                    },
                    "ObjectIdentifier": str(ADUtils.get_entry_property(entry, 'objectGUID')).translate({ord(c):'' for c in '}{'}),
                    "Aces": []
            }

            _, aces = parse_binary_acl(gpo, 'gpo', ADUtils.get_entry_property(entry, 'nTSecurityDescriptor'), self.addc.objecttype_guid_map)
            gpo['Aces'] = resolver.resolve_aces(aces)
            gpos.append(gpo)
           
        self.dump_gpos(gpos)
コード例 #3
0
    def enumerate_computers_dconly(self, timestamp=""):
        '''
        Enumerate computer objects. This function is only used if no
        collection was requested that required connecting to computers anyway.
        '''
        filename = timestamp + 'computers.json'

        acl = 'acl' in self.collect
        entries = self.addc.ad.computers.values()

        logging.debug('Writing computers ACL to file: %s', filename)

        # Use a separate queue for processing the results
        self.result_q = queue.Queue()
        results_worker = threading.Thread(
            target=OutputWorker.membership_write_worker,
            args=(self.result_q, 'computers', filename))
        results_worker.daemon = True
        results_worker.start()

        if acl and not self.disable_pooling:
            self.aclenumerator.init_pool()

        # This loops over the cached entries
        for entry in entries:
            if not 'attributes' in entry:
                continue

            if 'dNSHostName' not in entry['attributes']:
                continue

            hostname = entry['attributes']['dNSHostName']
            if not hostname:
                continue
            samname = entry['attributes']['sAMAccountName']

            cobject = ADComputer(hostname=hostname,
                                 samname=samname,
                                 ad=self.addomain,
                                 addc=self.addc,
                                 objectsid=entry['attributes']['objectSid'])
            cobject.primarygroup = MembershipEnumerator.get_primary_membership(
                entry)
            computer = cobject.get_bloodhound_data(entry,
                                                   self.collect,
                                                   skip_acl=True)

            # If we are enumerating ACLs, we break out of the loop here
            # this is because parsing ACLs is computationally heavy and therefor is done in subprocesses
            if acl:
                if self.disable_pooling:
                    # Debug mode, don't run this pooled since it hides exceptions
                    self.process_acldata(
                        parse_binary_acl(
                            computer, 'computer',
                            ADUtils.get_entry_property(entry,
                                                       'nTSecurityDescriptor',
                                                       raw=True),
                            self.addc.objecttype_guid_map))
                else:
                    # Process ACLs in separate processes, then call the processing function to resolve entries and write them to file
                    self.aclenumerator.pool.apply_async(
                        parse_binary_acl,
                        args=(computer, 'computer',
                              ADUtils.get_entry_property(
                                  entry, 'nTSecurityDescriptor',
                                  raw=True), self.addc.objecttype_guid_map),
                        callback=self.process_acldata)
            else:
                # Write it to the queue -> write to file in separate thread
                # this is solely for consistency with acl parsing, the performance improvement is probably minimal
                self.result_q.put(computer)

        # If we are parsing ACLs, close the parsing pool first
        # then close the result queue and join it
        if acl and not self.disable_pooling:
            self.aclenumerator.pool.close()
            self.aclenumerator.pool.join()
            self.result_q.put(None)
        else:
            self.result_q.put(None)
        self.result_q.join()

        logging.debug('Finished writing computers')
コード例 #4
0
    def enumerate_groups(self, timestamp=""):

        highvalue = [
            "S-1-5-32-544", "S-1-5-32-550", "S-1-5-32-549", "S-1-5-32-551",
            "S-1-5-32-548"
        ]

        def is_highvalue(sid):
            if sid.endswith("-512") or sid.endswith("-516") or sid.endswith(
                    "-519") or sid.endswith("-520"):
                return True
            if sid in highvalue:
                return True
            return False

        # Should we include extra properties in the query?
        with_properties = 'objectprops' in self.collect
        acl = 'acl' in self.collect

        filename = timestamp + 'groups.json'
        entries = self.addc.get_groups(include_properties=with_properties,
                                       acl=acl)

        logging.debug('Writing groups to file: %s', filename)

        # Use a separate queue for processing the results
        self.result_q = queue.Queue()
        results_worker = threading.Thread(
            target=OutputWorker.membership_write_worker,
            args=(self.result_q, 'groups', filename))
        results_worker.daemon = True
        results_worker.start()

        if acl and not self.disable_pooling:
            self.aclenumerator.init_pool()

        for entry in entries:
            resolved_entry = ADUtils.resolve_ad_entry(entry)
            self.addomain.groups[entry['dn']] = resolved_entry
            try:
                sid = entry['attributes']['objectSid']
            except KeyError:
                #Somehow we found a group without a sid?
                logging.warning('Could not determine SID for group %s',
                                entry['attributes']['distinguishedName'])
                continue
            group = {
                "ObjectIdentifier":
                sid,
                "Properties": {
                    "domain":
                    self.addomain.domain.upper(),
                    "domainsid":
                    self.addomain.domain_object.sid,
                    "name":
                    resolved_entry['principal'],
                    "distinguishedname":
                    ADUtils.get_entry_property(entry,
                                               'distinguishedName').upper()
                },
                "Members": [],
                "Aces": [],
                "IsDeleted":
                ADUtils.get_entry_property(entry, 'isDeleted', default=False)
            }
            if sid in ADUtils.WELLKNOWN_SIDS:
                # Prefix it with the domain
                group['ObjectIdentifier'] = '%s-%s' % (
                    self.addomain.domain.upper(), sid)
            if with_properties:
                group['Properties']['admincount'] = ADUtils.get_entry_property(
                    entry, 'adminCount', default=0) == 1
                group['Properties'][
                    'description'] = ADUtils.get_entry_property(
                        entry, 'description')
                whencreated = ADUtils.get_entry_property(entry,
                                                         'whencreated',
                                                         default=0)
                group['Properties']['whencreated'] = calendar.timegm(
                    whencreated.timetuple())

            for member in entry['attributes']['member']:
                resolved_member = self.get_membership(member)
                if resolved_member:
                    group['Members'].append(resolved_member)

            # If we are enumerating ACLs, we break out of the loop here
            # this is because parsing ACLs is computationally heavy and therefor is done in subprocesses
            if acl:
                if self.disable_pooling:
                    # Debug mode, don't run this pooled since it hides exceptions
                    self.process_acldata(
                        parse_binary_acl(
                            group, 'group',
                            ADUtils.get_entry_property(entry,
                                                       'nTSecurityDescriptor',
                                                       raw=True),
                            self.addc.objecttype_guid_map))
                else:
                    # Process ACLs in separate processes, then call the processing function to resolve entries and write them to file
                    self.aclenumerator.pool.apply_async(
                        parse_binary_acl,
                        args=(group, 'group',
                              ADUtils.get_entry_property(
                                  entry, 'nTSecurityDescriptor',
                                  raw=True), self.addc.objecttype_guid_map),
                        callback=self.process_acldata)
            else:
                # Write it to the queue -> write to file in separate thread
                # this is solely for consistency with acl parsing, the performance improvement is probably minimal
                self.result_q.put(group)

        self.write_default_groups()

        # If we are parsing ACLs, close the parsing pool first
        # then close the result queue and join it
        if acl and not self.disable_pooling:
            self.aclenumerator.pool.close()
            self.aclenumerator.pool.join()
            self.result_q.put(None)
        else:
            self.result_q.put(None)
        self.result_q.join()

        logging.debug('Finished writing groups')
コード例 #5
0
    def enumerate_users(self, timestamp=""):
        filename = timestamp + 'users.json'

        # Should we include extra properties in the query?
        with_properties = 'objectprops' in self.collect
        acl = 'acl' in self.collect
        entries = self.addc.get_users(include_properties=with_properties,
                                      acl=acl)

        logging.debug('Writing users to file: %s', filename)

        # Use a separate queue for processing the results
        self.result_q = queue.Queue()
        results_worker = threading.Thread(
            target=OutputWorker.membership_write_worker,
            args=(self.result_q, 'users', filename))
        results_worker.daemon = True
        results_worker.start()

        if acl and not self.disable_pooling:
            self.aclenumerator.init_pool()

        # This loops over a generator, results are fetched from LDAP on the go
        for entry in entries:
            resolved_entry = ADUtils.resolve_ad_entry(entry)
            # Skip trust objects
            if resolved_entry['type'] == 'trustaccount':
                continue
            user = {
                "AllowedToDelegate": [],
                "ObjectIdentifier":
                ADUtils.get_entry_property(entry, 'objectSid'),
                "PrimaryGroupSID":
                MembershipEnumerator.get_primary_membership(entry),
                "Properties": {
                    "name":
                    resolved_entry['principal'],
                    "domain":
                    self.addomain.domain.upper(),
                    "domainsid":
                    self.addomain.domain_object.sid,
                    "distinguishedname":
                    ADUtils.get_entry_property(entry,
                                               'distinguishedName').upper(),
                    "unconstraineddelegation":
                    ADUtils.get_entry_property(
                        entry, 'userAccountControl', default=0)
                    & 0x00080000 == 0x00080000,
                    "trustedtoauth":
                    ADUtils.get_entry_property(
                        entry, 'userAccountControl', default=0)
                    & 0x01000000 == 0x01000000,
                    "passwordnotreqd":
                    ADUtils.get_entry_property(
                        entry, 'userAccountControl', default=0)
                    & 0x00000020 == 0x00000020
                },
                "Aces": [],
                "SPNTargets": [],
                "HasSIDHistory": [],
                "IsDeleted":
                ADUtils.get_entry_property(entry, 'isDeleted', default=False)
            }

            if with_properties:
                MembershipEnumerator.add_user_properties(user, entry)
                if 'allowedtodelegate' in user['Properties']:
                    for host in user['Properties']['allowedtodelegate']:
                        try:
                            target = host.split('/')[1]
                        except IndexError:
                            logging.warning('Invalid delegation target: %s',
                                            host)
                            continue
                        try:
                            sid = self.addomain.computersidcache.get(
                                target.lower())
                            user['AllowedToDelegate'].append(sid)
                        except KeyError:
                            if '.' in target:
                                user['AllowedToDelegate'].append(
                                    target.upper())
                # Parse SID history
                if len(user['Properties']['sidhistory']) > 0:
                    for historysid in user['Properties']['sidhistory']:
                        user['HasSIDHistory'].append(
                            self.aceresolver.resolve_sid(historysid))

            # If this is a GMSA, process it's ACL. We don't bother with threads/processes here
            # since these accounts shouldn't be that common and neither should they have very complex
            # DACLs which control who can read their password
            if ADUtils.get_entry_property(
                    entry, 'msDS-GroupMSAMembership', default=b'',
                    raw=True) != b'':
                self.parse_gmsa(user, entry)

            self.addomain.users[entry['dn']] = resolved_entry
            # If we are enumerating ACLs, we break out of the loop here
            # this is because parsing ACLs is computationally heavy and therefor is done in subprocesses
            if acl:
                if self.disable_pooling:
                    # Debug mode, don't run this pooled since it hides exceptions
                    self.process_acldata(
                        parse_binary_acl(
                            user, 'user',
                            ADUtils.get_entry_property(entry,
                                                       'nTSecurityDescriptor',
                                                       raw=True),
                            self.addc.objecttype_guid_map))
                else:
                    # Process ACLs in separate processes, then call the processing function to resolve entries and write them to file
                    self.aclenumerator.pool.apply_async(
                        parse_binary_acl,
                        args=(user, 'user',
                              ADUtils.get_entry_property(
                                  entry, 'nTSecurityDescriptor',
                                  raw=True), self.addc.objecttype_guid_map),
                        callback=self.process_acldata)
            else:
                # Write it to the queue -> write to file in separate thread
                # this is solely for consistency with acl parsing, the performance improvement is probably minimal
                self.result_q.put(user)

        self.write_default_users()

        # If we are parsing ACLs, close the parsing pool first
        # then close the result queue and join it
        if acl and not self.disable_pooling:
            self.aclenumerator.pool.close()
            self.aclenumerator.pool.join()
            self.result_q.put(None)
        else:
            self.result_q.put(None)
        self.result_q.join()

        logging.debug('Finished writing users')
コード例 #6
0
    def get_bloodhound_data(self, entry, collect, skip_acl=False):
        data = {
            'ObjectIdentifier':
            self.objectsid,
            'AllowedToAct': [],
            'PrimaryGroupSID':
            self.primarygroup,
            'LocalAdmins': {
                'Collected': 'localadmin' in collect
                and not self.permanentfailure,
                'FailureReason': None,
                'Results': self.admins,
            },
            'PSRemoteUsers': {
                'Collected': 'psremote' in collect
                and not self.permanentfailure,
                'FailureReason': None,
                'Results': self.psremote
            },
            'Properties': {
                'name':
                self.hostname.upper(),
                'domainsid':
                self.ad.domain_object.sid,
                'domain':
                self.ad.domain.upper(),
                'distinguishedname':
                ADUtils.get_entry_property(entry, 'distinguishedName').upper()
            },
            'RemoteDesktopUsers': {
                'Collected': 'rdp' in collect and not self.permanentfailure,
                'FailureReason': None,
                'Results': self.rdp
            },
            'DcomUsers': {
                'Collected': 'dcom' in collect and not self.permanentfailure,
                'FailureReason': None,
                'Results': self.dcom
            },
            'AllowedToDelegate': [],
            'Sessions': {
                'Collected': 'session' in collect
                and not self.permanentfailure,
                'FailureReason': None,
                'Results': self.sessions
            },
            'PrivilegedSessions': {
                'Collected': 'loggedon' in collect
                and not self.permanentfailure,
                'FailureReason': None,
                'Results': self.loggedon
            },
            # Unsupported for now
            'RegistrySessions': {
                'Collected': False,
                'FailureReason': None,
                'Results': []
            },
            'Aces': [],
            'HasSIDHistory': [],
            'IsDeleted':
            ADUtils.get_entry_property(entry, 'isDeleted', default=False),
            'Status':
            None
        }
        props = data['Properties']
        # via the TRUSTED_FOR_DELEGATION (0x00080000) flag in UAC
        props['unconstraineddelegation'] = ADUtils.get_entry_property(
            entry, 'userAccountControl', default=0) & 0x00080000 == 0x00080000
        props['enabled'] = ADUtils.get_entry_property(
            entry, 'userAccountControl', default=0) & 2 == 0
        props['trustedtoauth'] = ADUtils.get_entry_property(
            entry, 'userAccountControl', default=0) & 0x01000000 == 0x01000000

        if 'objectprops' in collect or 'acl' in collect:
            props['haslaps'] = ADUtils.get_entry_property(
                entry, 'ms-mcs-admpwdexpirationtime', 0) != 0

        if 'objectprops' in collect:
            props['lastlogon'] = ADUtils.win_timestamp_to_unix(
                ADUtils.get_entry_property(entry,
                                           'lastlogon',
                                           default=0,
                                           raw=True))
            props['lastlogontimestamp'] = ADUtils.win_timestamp_to_unix(
                ADUtils.get_entry_property(entry,
                                           'lastlogontimestamp',
                                           default=0,
                                           raw=True))
            if props['lastlogontimestamp'] == 0:
                props['lastlogontimestamp'] = -1
            props['pwdlastset'] = ADUtils.win_timestamp_to_unix(
                ADUtils.get_entry_property(entry,
                                           'pwdLastSet',
                                           default=0,
                                           raw=True))
            whencreated = ADUtils.get_entry_property(entry,
                                                     'whencreated',
                                                     default=0)
            if not isinstance(whencreated, int):
                whencreated = calendar.timegm(whencreated.timetuple())
            props['whencreated'] = whencreated
            props['serviceprincipalnames'] = ADUtils.get_entry_property(
                entry, 'servicePrincipalName', [])
            props['description'] = ADUtils.get_entry_property(
                entry, 'description')
            props['operatingsystem'] = ADUtils.get_entry_property(
                entry, 'operatingSystem')
            # Add SP to OS if specified
            servicepack = ADUtils.get_entry_property(
                entry, 'operatingSystemServicePack')
            if servicepack:
                props['operatingsystem'] = '%s %s' % (props['operatingsystem'],
                                                      servicepack)
            props['sidhistory'] = [
                LDAP_SID(bsid).formatCanonical()
                for bsid in ADUtils.get_entry_property(entry, 'sIDHistory', [])
            ]
            delegatehosts = ADUtils.get_entry_property(
                entry, 'msDS-AllowedToDelegateTo', [])
            for host in delegatehosts:
                try:
                    target = host.split('/')[1]
                except IndexError:
                    logging.warning('Invalid delegation target: %s', host)
                    continue
                try:
                    sid = self.ad.computersidcache.get(target.lower())
                    data['AllowedToDelegate'].append(sid)
                except KeyError:
                    if '.' in target:
                        data['AllowedToDelegate'].append(target.upper())
            if len(delegatehosts) > 0:
                props['allowedtodelegate'] = delegatehosts

            # Process resource-based constrained delegation
            _, aces = parse_binary_acl(
                data, 'computer',
                ADUtils.get_entry_property(
                    entry,
                    'msDS-AllowedToActOnBehalfOfOtherIdentity',
                    raw=True), self.addc.objecttype_guid_map)
            outdata = self.aceresolver.resolve_aces(aces)
            for delegated in outdata:
                if delegated['RightName'] == 'Owner':
                    continue
                if delegated['RightName'] == 'GenericAll':
                    data['AllowedToAct'].append({
                        'ObjectIdentifier':
                        delegated['PrincipalSID'],
                        'ObjectType':
                        delegated['PrincipalType']
                    })

        # Run ACL collection if this was not already done centrally
        if 'acl' in collect and not skip_acl:
            _, aces = parse_binary_acl(
                data, 'computer',
                ADUtils.get_entry_property(entry,
                                           'nTSecurityDescriptor',
                                           raw=True),
                self.addc.objecttype_guid_map)
            # Parse aces
            data['Aces'] = self.aceresolver.resolve_aces(aces)

        return data
コード例 #7
0
    def get_bloodhound_data(self, entry, collect, skip_acl=False):
        data = {
            'ObjectIdentifier': self.objectsid,
            'AllowedToAct': [],
            'PrimaryGroupSid': self.primarygroup,
            'LocalAdmins': self.admins,
            'PSRemoteUsers': self.psremote,
            'Properties': {
                'name': self.hostname.upper(),
                'objectid': self.objectsid,
                'domain': self.ad.domain.upper(),
                'highvalue': False,
                'distinguishedname': ADUtils.get_entry_property(entry, 'distinguishedName')
            },
            'RemoteDesktopUsers': self.rdp,
            'DcomUsers': self.dcom,
            'AllowedToDelegate': [],
            'Sessions': self.sessions,
            'Aces': []
        }
        props = data['Properties']
        # via the TRUSTED_FOR_DELEGATION (0x00080000) flag in UAC
        props['unconstraineddelegation'] = ADUtils.get_entry_property(entry, 'userAccountControl', default=0) & 0x00080000 == 0x00080000
        props['enabled'] = ADUtils.get_entry_property(entry, 'userAccountControl', default=0) & 2 == 0

        if 'objectprops' in collect or 'acl' in collect:
            props['haslaps'] = ADUtils.get_entry_property(entry, 'ms-mcs-admpwdexpirationtime', 0) != 0

        if 'objectprops' in collect:
            props['lastlogontimestamp'] = ADUtils.win_timestamp_to_unix(
                ADUtils.get_entry_property(entry, 'lastlogontimestamp', default=0, raw=True)
            )
            props['pwdlastset'] = ADUtils.win_timestamp_to_unix(
                ADUtils.get_entry_property(entry, 'pwdLastSet', default=0, raw=True)
            )
            props['serviceprincipalnames'] = ADUtils.get_entry_property(entry, 'servicePrincipalName', [])
            props['description'] = ADUtils.get_entry_property(entry, 'description')
            props['operatingsystem'] = ADUtils.get_entry_property(entry, 'operatingSystem')
            # Add SP to OS if specified
            servicepack = ADUtils.get_entry_property(entry, 'operatingSystemServicePack')
            if servicepack:
                props['operatingsystem'] = '%s %s' % (props['operatingsystem'], servicepack)

            delegatehosts = ADUtils.get_entry_property(entry, 'msDS-AllowedToDelegateTo', [])
            for host in delegatehosts:
                try:
                    target = host.split('/')[1]
                except IndexError:
                    logging.warning('Invalid delegation target: %s', host)
                    continue
                try:
                    sid = self.ad.computersidcache.get(target.lower())
                    data['AllowedToDelegate'].append(sid)
                except KeyError:
                    if '.' in target:
                        data['AllowedToDelegate'].append(target.upper())
            if len(delegatehosts) > 0:
                props['allowedtodelegate'] = delegatehosts

            # Process resource-based constrained delegation
            _, aces = parse_binary_acl(data,
                                       'computer',
                                       ADUtils.get_entry_property(entry,
                                                                  'msDS-AllowedToActOnBehalfOfOtherIdentity',
                                                                  raw=True),
                                       self.addc.objecttype_guid_map)
            outdata = self.aceresolver.resolve_aces(aces)
            for delegated in outdata:
                if delegated['RightName'] == 'Owner':
                    continue
                if delegated['RightName'] == 'GenericAll':
                    data['AllowedToAct'].append({'MemberId': delegated['PrincipalSID'], 'MemberType': delegated['PrincipalType']})

        # Run ACL collection if this was not already done centrally
        if 'acl' in collect and not skip_acl:
            _, aces = parse_binary_acl(data,
                                       'computer',
                                       ADUtils.get_entry_property(entry,
                                                                  'nTSecurityDescriptor',
                                                                  raw=True),
                                       self.addc.objecttype_guid_map)
            # Parse aces
            data['Aces'] = self.aceresolver.resolve_aces(aces)

        return data
コード例 #8
0
    def dump_domain(self, collect, filename='domains.json'):
        """
        Dump trusts. This is currently the only domain info we support, so
        this function handles the entire domain dumping.
        """
        if 'trusts' in collect:
            entries = self.addc.get_trusts()
        else:
            entries = []

        try:
            logging.debug('Opening file for writing: %s' % filename)
            out = codecs.open(filename, 'w', 'utf-8')
        except:
            logging.warning('Could not write file: %s' % filename)
            return

        # If the logging level is DEBUG, we ident the objects
        if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
            indent_level = 1
        else:
            indent_level = None

        # Todo: fix this properly. Current code is quick fix to work with domains
        # that have custom casing in their DN
        domain_object = None
        for domain in self.addomain.domains.keys():
            if domain.lower() == self.addomain.baseDN.lower():
                domain_object = self.addomain.domains[domain]
                break

        if not domain_object:
            logging.error(
                'Could not find domain object. Aborting domain enumeration')
            return

        # Initialize json structure
        datastruct = {
            "domains": [],
            "meta": {
                "type": "domains",
                "count": 0,
                "version": 3
            }
        }
        # Get functional level
        level_id = ADUtils.get_entry_property(domain_object,
                                              'msds-behavior-version')
        try:
            functional_level = ADUtils.FUNCTIONAL_LEVELS[int(level_id)]
        except KeyError:
            functional_level = 'Unknown'

        domain = {
            "ObjectIdentifier": domain_object['attributes']['objectSid'],
            "Properties": {
                "name":
                self.addomain.domain.upper(),
                "domain":
                self.addomain.domain.upper(),
                "highvalue":
                True,
                "objectid":
                ADUtils.get_entry_property(domain_object, 'objectSid'),
                "distinguishedname":
                ADUtils.get_entry_property(domain_object, 'distinguishedName'),
                "description":
                ADUtils.get_entry_property(domain_object, 'description'),
                "functionallevel":
                functional_level
            },
            "Trusts": [],
            "Aces": [],
            # The below is all for GPO collection, unsupported as of now.
            "Links": [],
            "Users": [],
            "Computers": [],
            "ChildOus": []
        }

        if 'acl' in collect:
            resolver = AceResolver(self.addomain, self.addomain.objectresolver)
            _, aces = parse_binary_acl(
                domain, 'domain',
                ADUtils.get_entry_property(domain_object,
                                           'nTSecurityDescriptor'),
                self.addc.objecttype_guid_map)
            domain['Aces'] = resolver.resolve_aces(aces)

        if 'trusts' in collect:
            num_entries = 0
            for entry in entries:
                num_entries += 1
                trust = ADDomainTrust(
                    ADUtils.get_entry_property(entry, 'name'),
                    ADUtils.get_entry_property(entry, 'trustDirection'),
                    ADUtils.get_entry_property(entry, 'trustType'),
                    ADUtils.get_entry_property(entry, 'trustAttributes'),
                    ADUtils.get_entry_property(entry, 'securityIdentifier'))
                domain['Trusts'].append(trust.to_output())

            logging.info('Found %u trusts', num_entries)

        # Single domain only
        datastruct['meta']['count'] = 1
        datastruct['domains'].append(domain)
        json.dump(datastruct, out, indent=indent_level)

        logging.debug('Finished writing domain info')
        out.close()
コード例 #9
0
ファイル: memberships.py プロジェクト: yusabas/BloodHound
    def enumerate_users(self):
        filename = 'users.json'

        # Should we include extra properties in the query?
        with_properties = 'objectprops' in self.collect
        acl = 'acl' in self.collect
        entries = self.addc.get_users(include_properties=with_properties,
                                      acl=acl)

        logging.debug('Writing users to file: %s', filename)

        # Use a separate queue for processing the results
        self.result_q = queue.Queue()
        results_worker = threading.Thread(
            target=OutputWorker.membership_write_worker,
            args=(self.result_q, 'users', filename))
        results_worker.daemon = True
        results_worker.start()

        if acl and not self.disable_pooling:
            self.aclenumerator.init_pool()

        # This loops over a generator, results are fetched from LDAP on the go
        for entry in entries:
            resolved_entry = ADUtils.resolve_ad_entry(entry)
            user = {
                "Name": resolved_entry['principal'],
                "PrimaryGroup": self.get_primary_membership(entry),
                "Properties": {
                    "domain":
                    self.addomain.domain.upper(),
                    "objectsid":
                    entry['attributes']['objectSid'],
                    "highvalue":
                    False,
                    "unconstraineddelegation":
                    ADUtils.get_entry_property(
                        entry, 'userAccountControl', default=0)
                    & 0x00080000 == 0x00080000
                },
                "Aces": []
            }

            if with_properties:
                MembershipEnumerator.add_user_properties(user, entry)
            self.addomain.users[entry['dn']] = resolved_entry
            # If we are enumerating ACLs, we break out of the loop here
            # this is because parsing ACLs is computationally heavy and therefor is done in subprocesses
            if acl:
                if self.disable_pooling:
                    # Debug mode, don't run this pooled since it hides exceptions
                    self.process_stuff(
                        parse_binary_acl(
                            user, 'user',
                            ADUtils.get_entry_property(entry,
                                                       'nTSecurityDescriptor',
                                                       raw=True)))
                else:
                    # Process ACLs in separate processes, then call the processing function to resolve entries and write them to file
                    self.aclenumerator.pool.apply_async(
                        parse_binary_acl,
                        args=(user, 'user',
                              ADUtils.get_entry_property(
                                  entry, 'nTSecurityDescriptor', raw=True)),
                        callback=self.process_stuff)
            else:
                # Write it to the queue -> write to file in separate thread
                # this is solely for consistency with acl parsing, the performance improvement is probably minimal
                self.result_q.put(user)

        # If we are parsing ACLs, close the parsing pool first
        # then close the result queue and join it
        if acl and not self.disable_pooling:
            self.aclenumerator.pool.close()
            self.aclenumerator.pool.join()
            self.result_q.put(None)
        else:
            self.result_q.put(None)
        self.result_q.join()

        logging.debug('Finished writing users')