Exemplo n.º 1
0
 def get_membership(self, member):
     # First assume it is a user
     try:
         resolved_entry = self.addomain.users[member]
     except KeyError:
         # Try if it is a group
         try:
             resolved_entry = self.addomain.groups[member]
         except KeyError:
             # Try if it is a computer
             try:
                 entry = self.addomain.computers[member]
                 # Computers are stored as raw entries
                 resolved_entry = ADUtils.resolve_ad_entry(entry)
             except KeyError:
                 use_gc = ADUtils.ldap2domain(
                     member) != self.addomain.domain
                 qobject = self.addomain.objectresolver.resolve_distinguishedname(
                     member, use_gc=use_gc)
                 if qobject is None:
                     return
                 resolved_entry = ADUtils.resolve_ad_entry(qobject)
                 # Store it in the cache
                 if resolved_entry['type'] == 'user':
                     self.addomain.users[member] = resolved_entry
                 if resolved_entry['type'] == 'group':
                     self.addomain.groups[member] = resolved_entry
                 # Computers are stored as raw entries
                 if resolved_entry['type'] == 'computer':
                     self.addomain.computers[member] = qobject
     return {
         "MemberName": resolved_entry['principal'],
         "MemberType": resolved_entry['type'].capitalize()
     }
Exemplo n.º 2
0
 def get_primary_membership(self, entry):
     """
     Looks up the primary membership based on RID. Resolves it if needed
     """
     try:
         primarygroupid = int(entry['attributes']['primaryGroupID'])
     except (TypeError, KeyError):
         # Doesn't have a primarygroupid, means it is probably a Group instead of a user
         return
     try:
         group = self.addomain.groups[
             self.addomain.groups_dnmap[primarygroupid]]
         return group['principal']
     except KeyError:
         # Look it up
         # Construct group sid by taking the domain sid, removing the user rid and appending the group rid
         groupsid = '%s-%d' % ('-'.join(
             entry['attributes']['objectSid'].split('-')[:-1]),
                               primarygroupid)
         group = self.addomain.objectresolver.resolve_sid(groupsid,
                                                          use_gc=False)
         if group is None:
             logging.warning('Warning: Unknown primarygroupid %d',
                             primarygroupid)
             return None
         resolved_entry = ADUtils.resolve_ad_entry(group)
         self.addomain.groups[group['attributes']
                              ['distinguishedName']] = resolved_entry
         self.addomain.groups_dnmap[primarygroupid] = group['attributes'][
             'distinguishedName']
         return resolved_entry['principal']
Exemplo n.º 3
0
    def enumerate_memberships(self, filename='group_membership.csv'):
        entries = self.addc.get_memberships()

        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

        logging.debug('Writing group memberships to file: %s' % filename)

        out.write('GroupName,AccountName,AccountType\n')
        entriesNum = 0
        for entry in entries:
            entriesNum += 1
            resolved_entry = ADUtils.resolve_ad_entry(entry)
            try:
                for m in entry['attributes']['memberOf']:
                    self.write_membership(resolved_entry, m, out)
            except (KeyError, LDAPKeyError):
                logging.debug(traceback.format_exc())
            self.write_primary_membership(resolved_entry, entry, out)

        logging.info('Found %d memberships', entriesNum)
        logging.debug('Finished writing membership')
        out.close()
Exemplo n.º 4
0
    def write_membership(self, resolved_entry, membership, out):
        if membership in self.addomain.groups:
            parent = self.addomain.groups[membership]
            pd = ADUtils.ldap2domain(membership)
            pr = ADUtils.resolve_ad_entry(parent)

            out.write(u'%s,%s,%s\n' %
                      (pr['principal'], resolved_entry['principal'],
                       resolved_entry['type']))
        else:
            # This could be a group in a different domain
            parent = self.addomain.objectresolver.resolve_group(membership)
            if not parent:
                logging.warning('Warning: Unknown group %s', membership)
                return
            self.addomain.groups[membership] = parent
            pd = ADUtils.ldap2domain(membership)
            pr = ADUtils.resolve_ad_entry(parent)

            out.write(u'%s,%s,%s\n' %
                      (pr['principal'], resolved_entry['principal'],
                       resolved_entry['type']))
Exemplo n.º 5
0
    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)

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

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

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

        # Initialize json header
        out.write('{"users":[')

        num_entries = 0
        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,
                    "objectsid": entry['attributes']['objectSid'],
                    "highvalue": False
                }
            }
            if with_properties:
                MembershipEnumerator.add_user_properties(user, entry)
            self.addomain.users[entry['dn']] = resolved_entry
            if num_entries != 0:
                out.write(',')
            json.dump(user, out, indent=indent_level)
            num_entries += 1

        logging.info('Found %d users', num_entries)
        out.write('],"meta":{"type":"users","count":%d}}' % num_entries)

        logging.debug('Finished writing users')
        out.close()
Exemplo n.º 6
0
 def get_membership(self, member):
     """
     Attempt to resolve the membership (DN) of a group to an object
     """
     # First assume it is a user
     try:
         resolved_entry = self.addomain.users[member]
     except KeyError:
         # Try if it is a group
         try:
             resolved_entry = self.addomain.groups[member]
         except KeyError:
             # Try if it is a computer
             try:
                 entry = self.addomain.computers[member]
                 # Computers are stored as raw entries
                 resolved_entry = ADUtils.resolve_ad_entry(entry)
             except KeyError:
                 use_gc = ADUtils.ldap2domain(
                     member) != self.addomain.domain
                 qobject = self.addomain.objectresolver.resolve_distinguishedname(
                     member, use_gc=use_gc)
                 if qobject is None:
                     return None
                 resolved_entry = ADUtils.resolve_ad_entry(qobject)
                 # Store it in the cache
                 if resolved_entry['type'] == 'User':
                     self.addomain.users[member] = resolved_entry
                 if resolved_entry['type'] == 'Group':
                     self.addomain.groups[member] = resolved_entry
                 # Computers are stored as raw entries
                 if resolved_entry['type'] == 'Computer':
                     self.addomain.computers[member] = qobject
     return {
         "ObjectIdentifier": resolved_entry['objectid'],
         "ObjectType": resolved_entry['type'].capitalize()
     }
Exemplo n.º 7
0
 def write_primary_membership(self, resolved_entry, entry, out):
     try:
         primarygroupid = int(entry['attributes']['primaryGroupID'])
     except (TypeError, KeyError):
         # Doesn't have a primarygroupid, means it is probably a Group instead of a user
         return
     try:
         group = self.addomain.groups[
             self.addomain.groups_dnmap[primarygroupid]]
         pr = ADUtils.resolve_ad_entry(group)
         out.write('%s,%s,%s\n' %
                   (pr['principal'], resolved_entry['principal'],
                    resolved_entry['type']))
     except KeyError:
         logging.warning('Warning: Unknown primarygroupid %d',
                         primarygroupid)
Exemplo n.º 8
0
    def write_default_groups(self):
        """
        Put default groups in the groups.json file
        """

        # Domain controllers
        rootdomain = self.addc.get_root_domain().upper()
        entries = self.addc.get_domain_controllers()

        group = {
            "IsDeleted": False,
            "IsACLProtected": False,
            "ObjectIdentifier": "%s-S-1-5-9" % rootdomain,
            "Properties": {
                "domain": rootdomain.upper(),
                "name": "ENTERPRISE DOMAIN CONTROLLERS@%s" % rootdomain,
            },
            "Members": [],
            "Aces": []
        }
        for entry in entries:
            resolved_entry = ADUtils.resolve_ad_entry(entry)
            memberdata = {
                "ObjectIdentifier": resolved_entry['objectid'],
                "ObjectType": resolved_entry['type'].capitalize()
            }
            group["Members"].append(memberdata)
        self.result_q.put(group)

        domainsid = self.addomain.domain_object.sid
        domainname = self.addomain.domain.upper()

        # Everyone
        evgroup = {
            "IsDeleted": False,
            "IsACLProtected": False,
            "ObjectIdentifier": "%s-S-1-1-0" % domainname,
            "Properties": {
                "domain": domainname,
                "domainsid": self.addomain.domain_object.sid,
                "name": "EVERYONE@%s" % domainname,
            },
            "Members": [],
            "Aces": []
        }
        self.result_q.put(evgroup)

        # Authenticated users
        augroup = {
            "IsDeleted": False,
            "IsACLProtected": False,
            "ObjectIdentifier": "%s-S-1-5-11" % domainname,
            "Properties": {
                "domain": domainname,
                "domainsid": self.addomain.domain_object.sid,
                "name": "AUTHENTICATED USERS@%s" % domainname,
            },
            "Members": [],
            "Aces": []
        }
        self.result_q.put(augroup)

        # Interactive
        iugroup = {
            "IsDeleted": False,
            "IsACLProtected": False,
            "ObjectIdentifier": "%s-S-1-5-4" % domainname,
            "Properties": {
                "domain": domainname,
                "domainsid": self.addomain.domain_object.sid,
                "name": "INTERACTIVE@%s" % domainname,
            },
            "Members": [],
            "Aces": []
        }
        self.result_q.put(iugroup)
Exemplo n.º 9
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')
Exemplo n.º 10
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')
Exemplo n.º 11
0
    def enumerate_groups(self):

        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 = 'groups.json'
        entries = self.addc.get_groups(include_properties=with_properties,
                                       acl=acl)

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

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

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

        # Initialize json header
        out.write('{"groups":[')

        num_entries = 0
        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 = {
                "Name": resolved_entry['principal'],
                "Properties": {
                    "domain": self.addomain.domain,
                    "objectsid": sid,
                    "highvalue": is_highvalue(sid)
                },
                "Members": []
            }
            if with_properties:
                group['Properties']['admincount'] = ADUtils.get_entry_property(
                    entry, 'adminCount', default=0) == 1
                group['Properties'][
                    'description'] = ADUtils.get_entry_property(
                        entry, 'description')

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

            if num_entries != 0:
                out.write(',')
            json.dump(group, out, indent=indent_level)
            num_entries += 1

        logging.info('Found %d groups', num_entries)
        out.write('],"meta":{"type":"groups","count":%d}}' % num_entries)
        logging.debug('Finished writing groups')
        out.close()
Exemplo n.º 12
0
    def process_computer(self, hostname, samname, objectsid, entry, results_q):
        """
            Processes a single computer, pushes the results of the computer to the given queue.
        """
        logging.debug('Querying computer: %s', hostname)
        c = ADComputer(hostname=hostname,
                       samname=samname,
                       ad=self.addomain,
                       objectsid=objectsid)
        c.primarygroup = self.get_primary_membership(entry)
        if c.try_connect() == True:
            try:

                if 'session' in self.collect:
                    sessions = c.rpc_get_sessions()
                else:
                    sessions = []
                if 'localadmin' in self.collect:
                    unresolved = c.rpc_get_group_members(544, c.admins)
                    c.rpc_resolve_sids(unresolved, c.admins)
                if 'rdp' in self.collect:
                    unresolved = c.rpc_get_group_members(555, c.rdp)
                    c.rpc_resolve_sids(unresolved, c.rdp)
                if 'dcom' in self.collect:
                    unresolved = c.rpc_get_group_members(562, c.dcom)
                    c.rpc_resolve_sids(unresolved, c.dcom)
                if 'loggedon' in self.collect:
                    loggedon = c.rpc_get_loggedon()
                else:
                    loggedon = []
                if 'experimental' in self.collect:
                    services = c.rpc_get_services()
                    tasks = c.rpc_get_schtasks()
                else:
                    services = []
                    tasks = []

                c.rpc_close()
                # c.rpc_get_domain_trusts()

                results_q.put(
                    ('computer', c.get_bloodhound_data(entry, self.collect)))

                if sessions is None:
                    sessions = []

                # Process found sessions
                for ses in sessions:
                    # For every session, resolve the SAM name in the GC if needed
                    domain = self.addomain.domain
                    if self.addomain.num_domains > 1 and self.do_gc_lookup:
                        try:
                            users = self.addomain.samcache.get(samname)
                        except KeyError:
                            # Look up the SAM name in the GC
                            users = self.addomain.objectresolver.gc_sam_lookup(
                                ses['user'])
                            if users is None:
                                # Unknown user
                                continue
                            self.addomain.samcache.put(samname, users)
                    else:
                        users = [((u'%s@%s' % (ses['user'], domain)).upper(),
                                  2)]

                    # Resolve the IP to obtain the host the session is from
                    try:
                        target = self.addomain.dnscache.get(ses['source'])
                    except KeyError:
                        target = ADUtils.ip2host(ses['source'],
                                                 self.addomain.dnsresolver,
                                                 self.addomain.dns_tcp)
                        # Even if the result is the IP (aka could not resolve PTR) we still cache
                        # it since this result is unlikely to change during this run
                        self.addomain.dnscache.put_single(
                            ses['source'], target)
                    if ':' in target:
                        # IPv6 address, not very useful
                        continue
                    if '.' not in target:
                        logging.debug(
                            'Resolved target does not look like an IP or domain. Assuming hostname: %s',
                            target)
                        target = '%s.%s' % (target, domain)
                    # Put the result on the results queue.
                    for user in users:
                        results_q.put(('session', {
                            'UserName': user[0].upper(),
                            'ComputerName': target.upper(),
                            'Weight': user[1]
                        }))
                if loggedon is None:
                    loggedon = []

                # Put the logged on users on the queue too
                for user in loggedon:
                    results_q.put(('session', {
                        'UserName': ('%s@%s' % user).upper(),
                        'ComputerName': hostname.upper(),
                        'Weight': 1
                    }))

                # Process Tasks
                for taskuser in tasks:
                    try:
                        user = self.addomain.sidcache.get(taskuser)
                    except KeyError:
                        # Resolve SID in GC
                        userentry = self.addomain.objectresolver.resolve_sid(
                            taskuser)
                        # Resolve it to an entry and store in the cache
                        user = ADUtils.resolve_ad_entry(userentry)
                        self.addomain.sidcache.put(taskuser, user)
                    logging.debug('Resolved TASK SID to username: %s',
                                  user['principal'])
                    # Use sessions for now
                    results_q.put(('session', {
                        'UserName': user['principal'].upper(),
                        'ComputerName': hostname.upper(),
                        'Weight': 2
                    }))

                # Process Services
                for serviceuser in services:
                    # Todo: use own cache
                    try:
                        user = self.addomain.sidcache.get(serviceuser)
                    except KeyError:
                        # Resolve UPN in GC
                        userentry = self.addomain.objectresolver.resolve_upn(
                            serviceuser)
                        # Resolve it to an entry and store in the cache
                        user = ADUtils.resolve_ad_entry(userentry)
                        self.addomain.sidcache.put(serviceuser, user)
                    logging.debug('Resolved Service UPN to username: %s',
                                  user['principal'])
                    # Use sessions for now
                    results_q.put(('session', {
                        'UserName': user['principal'].upper(),
                        'ComputerName': hostname.upper(),
                        'Weight': 2
                    }))

            except DCERPCException:
                logging.warning('Querying computer failed: %s' % hostname)
            except Exception as e:
                logging.error(
                    'Unhandled exception in computer %s processing: %s',
                    hostname, str(e))
                logging.info(traceback.format_exc())
        else:
            # Write the info we have to the file regardless
            try:
                results_q.put(
                    ('computer', c.get_bloodhound_data(entry, self.collect)))
            except Exception as e:
                logging.error(
                    'Unhandled exception in computer %s processing: %s',
                    hostname, str(e))
                logging.info(traceback.format_exc())
Exemplo n.º 13
0
    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')