def rpc_resolve_sids(self): """ Resolve any remaining unknown SIDs for local administrator accounts. """ # If all sids were already cached, we can just return if len(self.admin_sids) == 0: return binding = r'ncacn_np:%s[\PIPE\lsarpc]' % self.addr dce = self.dce_rpc_connect(binding, lsat.MSRPC_UUID_LSAT) if dce is None: logging.warning('Connection failed') return try: resp = lsat.hLsarOpenPolicy2( dce, lsat.POLICY_LOOKUP_NAMES | MAXIMUM_ALLOWED) except Exception as e: if str(e).find('Broken pipe') >= 0: return else: raise policyHandle = resp['PolicyHandle'] # We could look up the SIDs all at once, but if not all SIDs are mapped, we don't know which # ones were resolved and which not, making it impossible to map them in the cache. # Therefor we use more SAMR calls at the start, but after a while most SIDs will be reliable # in our cache and this function doesn't even need to get called anymore. for sid_string in self.admin_sids: try: resp = lsat.hLsarLookupSids( dce, policyHandle, [sid_string], lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta) except DCERPCException as e: if str(e).find('STATUS_NONE_MAPPED') >= 0: logging.warning( 'SID %s lookup failed, return status: STATUS_NONE_MAPPED', sid_string) # Try next SID continue elif str(e).find('STATUS_SOME_NOT_MAPPED') >= 0: # Not all could be resolved, work with the ones that could resp = e.get_packet() else: raise domains = [] for entry in resp['ReferencedDomains']['Domains']: domains.append(entry['Name']) for entry in resp['TranslatedNames']['Names']: domain = domains[entry['DomainIndex']] domainEntry = self.ad.get_domain_by_name(domain) if domainEntry is not None: domain = ADUtils.ldap2domain( domainEntry['attributes']['distinguishedName']) if entry['Name'] != '': logging.debug('Resolved SID to name: %s@%s' % (entry['Name'], domain)) self.admins.append({ 'Name': u'%s@%s' % (unicode(entry['Name']).upper(), domain.upper()), 'Type': ADUtils.translateSidType(entry['Use']) }) # Add it to our cache self.ad.sidcache.put(sid_string, (entry, domain)) else: logging.warning('Resolved name is empty [%s]', entry) dce.disconnect()
def rpc_get_local_admins(self): binding = r'ncacn_np:%s[\PIPE\samr]' % self.addr dce = self.dce_rpc_connect(binding, samr.MSRPC_UUID_SAMR) if dce is None: logging.warning('Connection failed: %s', binding) return try: resp = samr.hSamrConnect(dce) serverHandle = resp['ServerHandle'] # Attempt to get the SID from this computer to filter local accounts later try: resp = samr.hSamrLookupDomainInSamServer( dce, serverHandle, self.samname[:-1]) self.sid = resp['DomainId'].formatCanonical() # This doesn't always work (for example on DCs) except DCERPCException as e: # Make it a string which is guaranteed not to match a SID self.sid = 'UNKNOWN' # Enumerate the domains known to this computer resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle) domains = resp['Buffer']['Buffer'] # Query the builtin domain (derived from this SID) sid = RPC_SID() sid.fromCanonical('S-1-5-32') logging.debug('Opening domain handle') # Open a handle to this domain resp = samr.hSamrOpenDomain(dce, serverHandle=serverHandle, desiredAccess=samr.DOMAIN_LOOKUP | MAXIMUM_ALLOWED, domainId=sid) domainHandle = resp['DomainHandle'] resp = samr.hSamrOpenAlias(dce, domainHandle, desiredAccess=samr.ALIAS_LIST_MEMBERS | MAXIMUM_ALLOWED, aliasId=544) resp = samr.hSamrGetMembersInAlias(dce, aliasHandle=resp['AliasHandle']) for member in resp['Members']['Sids']: sid_string = member['SidPointer'].formatCanonical() logging.debug('Found admin SID: %s', sid_string) if not sid_string.startswith(self.sid): # If the sid is known, we can add the admin value directly try: siddata, domain = self.ad.sidcache.get(sid_string) logging.debug('Sid is cached: %s@%s', siddata['Name'], domain) self.admins.append({ 'Name': u'%s@%s' % (unicode(siddata['Name']).upper(), domain.upper()), 'Type': ADUtils.translateSidType(siddata['Use']) }) except KeyError: # Append it to the list of unresolved SIDs self.admin_sids.append(sid_string) else: logging.debug('Ignoring local group %s', sid_string) except DCERPCException as e: logging.debug('Exception connecting to RPC: %s', e) except Exception as e: if 'connection reset' in str(e): logging.debug('Connection was reset: %s', e) else: raise e dce.disconnect()