def __fetchList(self, rpctransport): # UDP only works over DCE/RPC version 4. if isinstance(rpctransport, transport.UDPTransport): dce = dcerpc_v4.DCERPC_v4(rpctransport) else: dce = dcerpc.DCERPC_v5(rpctransport) entries = [] dce.connect() dce.bind(epm.MSRPC_UUID_PORTMAP) rpcepm = epm.DCERPCEpm(dce) resp = rpcepm.portmap_dump() while resp.get_entries_num() != 0: rpc_handle = resp.get_handle() ndrentry = resp.get_entry().get_entry() sb = transport.DCERPCStringBinding(ndrentry.get_string_binding()) entry = epm.EpmEntry( uuid.bin_to_string(ndrentry.get_uuid()), ndrentry.get_version(), ndrentry.get_annotation(), uuid.bin_to_string(ndrentry.get_objuuid()), sb.get_protocol_sequence(), sb.get_endpoint(), ) entries.append(entry) resp = rpcepm.portmap_dump(rpc_handle) dce.disconnect() return entries
def get_dynamic_endpoint(interface: bytes, target: str, timeout: int = 5) -> str: string_binding = r"ncacn_ip_tcp:%s[135]" % target rpctransport = transport.DCERPCTransportFactory(string_binding) rpctransport.set_connect_timeout(timeout) dce = rpctransport.get_dce_rpc() logging.debug("Trying to resolve dynamic endpoint %s" % repr(uuid.bin_to_string(interface))) try: dce.connect() except Exception as e: logging.warning("Failed to connect to endpoint mapper: %s" % e) return None try: endpoint = epm.hept_map(target, interface, protocol="ncacn_ip_tcp", dce=dce) logging.debug("Resolved dynamic endpoint %s to %s" % (repr(uuid.bin_to_string(interface)), repr(endpoint))) return endpoint except Exception: logging.debug("Failed to resolve dynamic endpoint %s" % repr(uuid.bin_to_string(interface))) return None
def __fetchList(self, rpctransport): # UDP only works over DCE/RPC version 4. if isinstance(rpctransport, transport.UDPTransport): dce = dcerpc_v4.DCERPC_v4(rpctransport) else: dce = dcerpc.DCERPC_v5(rpctransport) entries = [] dce.connect() dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) dce.bind(epm.MSRPC_UUID_PORTMAP) rpcepm = epm.DCERPCEpm(dce) resp = rpcepm.portmap_dump() while resp.get_entries_num() != 0: rpc_handle = resp.get_handle() ndrentry = resp.get_entry().get_entry() sb = transport.DCERPCStringBinding(ndrentry.get_string_binding()) entry = epm.EpmEntry(uuid.bin_to_string(ndrentry.get_uuid()), ndrentry.get_version(), ndrentry.get_annotation(), uuid.bin_to_string(ndrentry.get_objuuid()), sb.get_protocol_sequence(), sb.get_endpoint()) entries.append(entry) ## print str(entry) resp = rpcepm.portmap_dump(rpc_handle) dce.disconnect() return entries
def get_enrollment_principals(entry): # Mostly taken from github.com/ly4k/Certipy/certipy/security.py sd = ldaptypes.SR_SECURITY_DESCRIPTOR() sd.fromString(entry["raw_attributes"]["nTSecurityDescriptor"][0]) enrollment_uuids = [ "00000000-0000-0000-0000-000000000000", # All-Extended-Rights "0e10c968-78fb-11d2-90d4-00c04f79dc55", # Certificate-Enrollment "a05b8cc2-17bc-4802-a710-e7c15ab866a2", # Certificate-AutoEnrollment ] enrollment_principals = set() for ace in (a for a in sd["Dacl"]["Data"] if a["AceType"] == ldaptypes.ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE): sid = format_sid(ace["Ace"]["Sid"].getData()) if ace["Ace"]["ObjectTypeLen"] == 0: uuid = bin_to_string( ace["Ace"]["InheritedObjectType"]).lower() else: uuid = bin_to_string(ace["Ace"]["ObjectType"]).lower() if not uuid in enrollment_uuids: continue enrollment_principals.add(sid) return enrollment_principals
def dump(self): print("[VAULT_VPOL]") print("Version : %8x (%d)" % (self['Version'], self['Version'])) print("Guid : %s" % bin_to_string(self['Guid'])) print("Description : %s" % (self['Description'].decode('utf-16le'))) print("Size : 0x%.8x (%d)" % (self['Size'], self['Size'])) print("Guid2 : %s" % bin_to_string(self['Guid2'])) print("Guid3 : %s" % bin_to_string(self['Guid3'])) print("KeySize : 0x%.8x (%d)" % (self['KeySize'], self['KeySize'])) self['Blob'].dump() print()
def dump(self): print("[BLOB]") print("Version : %8x (%d)" % (self['Version'], self['Version'])) print("Guid Credential : %s" % bin_to_string(self['GuidCredential'])) print("MasterKeyVersion : %8x (%d)" % (self['MasterKeyVersion'], self['MasterKeyVersion'])) print("Guid MasterKey : %s" % bin_to_string(self['GuidMasterKey'])) print("Flags : %8x (%s)" % (self['Flags'], getFlags(FLAGS, self['Flags']))) print("Description : %s" % (self['Description'].decode('utf-16le'))) print("CryptAlgo : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name)) print("Salt : %s" % (hexlify(self['Salt']))) print("HMacKey : %s" % (hexlify(self['HMacKey']))) print("HashAlgo : %.8x (%d) (%s)" % (self['HashAlgo'], self['HashAlgo'], ALGORITHMS(self['HashAlgo']).name)) print("HMac : %s" % (hexlify(self['HMac']))) print("Data : %s" % (hexlify(self['Data']))) print("Sign : %s" % (hexlify(self['Sign']))) print()
def dump(self): print("[DOMAINKEY]") print("Version : %8x (%d)" % (self['Version'], self['Version'])) print("Guid : %s" % bin_to_string(self['Guid'])) print("SecretLen : %8x (%d)" % (self['SecretLen'], self['SecretLen'])) print("AccessCheckLen: %.8x (%d)" % (self['AccessCheckLen'], self['AccessCheckLen'])) print("SecretData : %s" % (hexlify(self['SecretData']))) print("AccessCheck : %s" % (hexlify(self['AccessCheck']))) print()
def can_add_member(ace): writeprivs = ace['Ace']['Mask'].hasPriv( ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_WRITE_PROP) if ace['AceType'] != ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE or ace['Ace'][ 'ObjectType'] == b'': return writeprivs userprivs = bin_to_string( ace['Ace'] ['ObjectType']).lower() == 'bf9679c0-0de6-11d0-a285-00aa003049e2' return writeprivs and userprivs
def can_create_users(ace): createprivs = ace['Ace']['Mask'].hasPriv( ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_CREATE_CHILD) if ace['AceType'] != ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE or ace['Ace'][ 'ObjectType'] == b'': return False userprivs = bin_to_string( ace['Ace'] ['ObjectType']).lower() == 'bf967aba-0de6-11d0-a285-00aa003049e2' return createprivs and userprivs
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 aceApplies(ace, objectClasses): ''' Checks if an ACE applies to this object (based on object classes). Note that this function assumes you already verified that InheritedObjectType is set (via the flag). If this is not set, the ACE applies to all object types. ''' objectTypeGuid = bin_to_string(ace['Ace']['InheritedObjectType']).lower() for objectType, guid in OBJECTTYPE_GUID_MAP.items(): if objectType in objectClasses and objectTypeGuid: return True # If none of these match, the ACE does not apply to this object return False
def dump(self): print("[VCRD]") print("SchemaGuid : %s" % bin_to_string(self['SchemaGuid'])) print("LastWritten : %s" % (datetime.utcfromtimestamp(getUnixTime(self['LastWritten'])))) print("FriendlyName: %s" % (self['FriendlyName'].decode('utf-16le'))) print() for i,entry in enumerate(self.mapEntries): entry.dump() self.attributes[i].dump() print() print("Remaining : %s" % (hexlify(self['Data']))) print()
def aceApplies(ace, objectClasses): ''' Checks if an ACE applies to this object (based on object classes). Note that this function assumes you already verified that InheritedObjectType is set (via the flag). If this is not set, the ACE applies to all object types. ''' objectTypeGuid = bin_to_string( ace['Ace']['InheritedObjectType']).lower() for objectType, guid in OBJECTTYPE_GUID_MAP.iteritems(): if objectType in objectClasses and objectTypeGuid: return True # If none of these match, the ACE does not apply to this object return False
def dump(self): print("[VCRD]") print("SchemaGuid : %s" % bin_to_string(self['SchemaGuid'])) print("LastWritten : %s" % (datetime.utcfromtimestamp(getUnixTime(self['LastWritten'])))) print("FriendlyName: %s" % (self['FriendlyName'].decode('utf-16le'))) print() for i, entry in enumerate(self.mapEntries): entry.dump() self.attributes[i].dump() print() print("Remaining : %s" % (hexlify(self['Data']))) print()
def dump(self): print("[CRED]") print("version : %8x (%d)" % (self['version'], self['version'])) print("GUID : %s" % bin_to_string(self['Guid'])) print("HashAlgo : %8x (%s)" % (self['HashAlgo'], ALGORITHMS(self['HashAlgo']).name)) print("rounds : %8x (%d)" % (self['rounds'], self['rounds'])) print("sidLen : %8x (%d)" % (self['sidLen'], self['sidLen'])) print("CryptAlgo : %8x (%s)" % (self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name)) print("shaHashLen : %8x (%d)" % (self['shaHashLen'], self['shaHashLen'])) print("ntHashLen : %8x (%d)" % (self['ntHashLen'], self['ntHashLen'])) print("Salt : %s" % hexlify(self['Salt'])) print("SID : %s" % (bin_to_sid(self['SID']))) print("data : %s" % hexlify(self['data'])) print()
def dump(self): print("[BLOB]") print("Version : %8x (%d)" % (self['Version'], self['Version'])) print("Guid Credential : %s" % bin_to_string(self['GuidCredential'])) print("MasterKeyVersion : %8x (%d)" % (self['MasterKeyVersion'], self['MasterKeyVersion'])) print("Guid MasterKey : %s" % bin_to_string(self['GuidMasterKey'])) print("Flags : %8x (%s)" % (self['Flags'], getFlags(FLAGS, self['Flags']))) print("Description : %s" % (self['Description'].decode('utf-16le'))) print("CryptAlgo : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name)) print("Salt : %s" % (hexlify(self['Salt']))) print("HMacKey : %s" % (hexlify(self['HMacKey']))) print("HashAlgo : %.8x (%d) (%s)" % (self['HashAlgo'], self['HashAlgo'], ALGORITHMS( self['HashAlgo']).name)) print("HMac : %s" % (hexlify(self['HMac']))) print("Data : %s" % (hexlify(self['Data']))) print("Sign : %s" % (hexlify(self['Sign']))) print()
def DiscoverDNSport(target): trans = transport.SMBTransport(target, 139, 'epmapper') trans.connect() dce = dcerpc.DCERPC_v5(trans) dce.bind(uuid.uuidtup_to_bin(('E1AF8308-5D1F-11C9-91A4-08002B14A0FA','3.0'))) pm = epm.DCERPCEpm(dce) handle = '\x00'*20 while 1: dump = pm.portmap_dump(handle) if not dump.get_entries_num(): break handle = dump.get_handle() entry = dump.get_entry().get_entry() if(uuid.bin_to_string(entry.get_uuid()) == '50ABC2A4-574D-40B3-9D66-EE4FD5FBA076'): port = entry.get_string_binding().split('[')[1][:-1] return int(port) print '[-] Could not locate DNS port; Target might not be running DNS'
def DiscoverDNSport(target): trans = transport.SMBTransport(target, 139, 'epmapper') trans.connect() dce = dcerpc.DCERPC_v5(trans) dce.bind( uuid.uuidtup_to_bin(('E1AF8308-5D1F-11C9-91A4-08002B14A0FA', '3.0'))) pm = epm.DCERPCEpm(dce) handle = '\x00' * 20 while 1: dump = pm.portmap_dump(handle) if not dump.get_entries_num(): break handle = dump.get_handle() entry = dump.get_entry().get_entry() if (uuid.bin_to_string( entry.get_uuid()) == '50ABC2A4-574D-40B3-9D66-EE4FD5FBA076'): port = entry.get_string_binding().split('[')[1][:-1] return int(port) print '[-] Could not locate DNS port; Target might not be running DNS'
def parse_EPM_Entries(entries): endpoints = {} for entry in entries: entry_Object = entry.get('object') entry_Tower_pointer = entry.get('tower') entry_Annotation = entry.get('annotation') entry_ObjectStr = uuid.bin_to_string(entry_Object) entry_Floors = (entry_Tower_pointer.fields).get('Floors') entry_NumberOfFloors = ( entry_Tower_pointer.fields).get('NumberOfFloors') entry_Tower_Floor_Info = parse_EPM_Entry_Floors(entry_Floors) from pprint import pprint pprint(entry_Tower_Floor_Info)
def parseRow(self, token, tuplemode=False): # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types # help would be appreciated ;) if len(token) == 1: return 0 row = [] if tuplemode else {} origDataLen = len(token['Data']) data = token['Data'] for col in self.colMeta: _type = col['Type'] if (_type == TDS_NVARCHARTYPE) |\ (_type == TDS_NCHARTYPE): #print "NVAR 0x%x" % _type charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen].decode('utf-16le') data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARCHRTYPE): charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_GUIDTYPE): uuidLen = ord(data[0]) data = data[1:] if uuidLen > 0: uu = data[:uuidLen] value = uuid.bin_to_string(uu) data = data[uuidLen:] else: value = 'NULL' elif (_type == TDS_NTEXTTYPE) |\ (_type == TDS_IMAGETYPE) : # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1 + charLen + 8:] charLen = struct.unpack('<L', data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: if _type == TDS_NTEXTTYPE: value = data[:charLen].decode('utf-16le') else: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_TEXTTYPE): # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1 + charLen + 8:] charLen = struct.unpack('<L', data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARBINTYPE) |\ (_type == TDS_BIGBINARYTYPE): charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_DATETIM4TYPE) |\ (_type == TDS_DATETIMNTYPE) |\ (_type == TDS_DATETIMETYPE): value = '' if _type == TDS_DATETIMNTYPE: # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data _types respectively. if ord(data[0]) == 4: _type = TDS_DATETIM4TYPE elif ord(data[0]) == 8: _type = TDS_DATETIMETYPE else: value = 'NULL' data = data[1:] if (_type == TDS_DATETIMETYPE): # datetime is represented in the following sequence: # * One 4-byte signed integer that represents the number of days since January 1, 1900. Negative # numbers are allowed to represents dates since January 1, 1753. # * One 4-byte unsigned integer that represents the number of one three-hundredths of a second # (300 counts per second) elapsed since 12 AM that day. dateValue = struct.unpack('<l', data[:4])[0] data = data[4:] if dateValue < 0: baseDate = datetime.date(1753, 1, 1) else: baseDate = datetime.date(1900, 1, 1) timeValue = struct.unpack('<L', data[:4])[0] data = data[4:] elif (_type == TDS_DATETIM4TYPE): # Small datetime # 2.2.5.5.1.8 # Date/Times # smalldatetime is represented in the following sequence: # * One 2-byte unsigned integer that represents the number of days since January 1, 1900. # * One 2-byte unsigned integer that represents the number of minutes elapsed since 12 AM that # day. dateValue = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] timeValue = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] baseDate = datetime.date(1900, 1, 1) if value != 'NULL': dateValue = datetime.date.fromordinal( baseDate.toordinal() + dateValue) hours, mod = divmod(timeValue / 300, 60 * 60) minutes, second = divmod(mod, 60) value = datetime.datetime(dateValue.year, dateValue.month, dateValue.day, hours, minutes, second) elif (_type == TDS_INT4TYPE) |\ (_type == TDS_MONEY4TYPE) |\ (_type == TDS_FLT4TYPE): #print "INT4" value = struct.unpack('<l', data[:struct.calcsize('<l')])[0] data = data[struct.calcsize('<l'):] elif (_type == TDS_FLTNTYPE): valueSize = ord(data[:1]) if valueSize == 4: fmt = '<f' elif valueSize == 8: fmt = '<d' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif _type == TDS_MONEYNTYPE: valueSize = ord(data[:1]) if valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] if valueSize == 4: value = float(value) / math.pow(10, 4) else: value = float(value >> 32) / math.pow(10, 4) data = data[valueSize:] else: value = 'NULL' elif _type == TDS_BIGCHARTYPE: #print "BIGC" charLen = struct.unpack('<H', data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] value = data[:charLen] data = data[charLen:] elif (_type == TDS_INT8TYPE) |\ (_type == TDS_FLT8TYPE) |\ (_type == TDS_MONEYTYPE): #print "DATETIME" value = struct.unpack('<q', data[:struct.calcsize('<q')])[0] data = data[struct.calcsize('<q'):] elif (_type == TDS_INT2TYPE): #print "INT2TYPE" value = struct.unpack('<H', (data[:2]))[0] data = data[2:] elif (_type == TDS_DATENTYPE): # date is represented as one 3-byte unsigned integer that represents the number of days since # January 1, year 1. valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: dateBytes = data[:valueSize] dateValue = struct.unpack('<L', '\x00' + dateBytes)[0] value = datetime.date.fromtimestamp(dateValue) data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_BITTYPE) |\ (_type == TDS_INT1TYPE): #print "BITTYPE" value = ord(data[:1]) data = data[1:] elif (_type == TDS_NUMERICNTYPE) |\ (_type == TDS_DECIMALNTYPE): valueLen = ord(data[:1]) data = data[1:] value = data[:valueLen] data = data[valueLen:] precision = ord(col['TypeData'][1]) scale = ord(col['TypeData'][2]) if valueLen > 0: isPositiveSign = ord(value[0]) if (valueLen - 1) == 2: fmt = '<H' elif (valueLen - 1) == 4: fmt = '<L' elif (valueLen - 1) == 8: fmt = '<Q' else: # Still don't know how to handle higher values value = "TODO: Interpret TDS_NUMERICNTYPE correctly" number = struct.unpack(fmt, value[1:])[0] number /= math.pow(precision, scale) if isPositiveSign == 0: number *= -1 value = number else: value = 'NULL' elif (_type == TDS_BITNTYPE): #print "BITNTYPE" valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: if valueSize == 1: value = ord(data[:valueSize]) else: value = data[:valueSize] else: value = 'NULL' data = data[valueSize:] elif (_type == TDS_INTNTYPE): valueSize = ord(data[:1]) if valueSize == 1: fmt = '<B' elif valueSize == 2: fmt = '<h' elif valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' else: fmt = '' data = data[1:] if valueSize > 0: value = struct.unpack(fmt, data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_SSVARIANTTYPE): self.__logger.logMessage( "ParseRow: SQL Variant type not yet supported :(") raise else: self.__logger.logMessage( "ParseROW: Unsupported data type: 0%x" % _type) raise if tuplemode: row.append(value) else: row[col['Name']] = value self.rows.append(row) return (origDataLen - len(data))
def nspi_dump_tables(self, options): self.exch.set_output_type(options.output_type) if options.lookup_type == None or options.lookup_type == 'MINIMAL': propTags = NSPIAttacks.PROPS_MINUMAL elif options.lookup_type == 'EXTENDED': propTags = NSPIAttacks.PROPS_EXTENDED elif options.lookup_type == 'GUIDS': propTags = NSPIAttacks.PROPS_GUID else: # FULL propTags = [] if options.name != None and options.name.lower() in ['gal', 'default global address list', 'global address list']: logging.info("Lookuping Global Address List") table_MId = 0 else: # 2.2.8 # The client obtains Minimal Entry IDs for STAT ContainerID # from the server's address book hierarchy table # # We cannot convert the GUID to a MId via NspiDNToMId or similar operations because it # may not work in Multi-Tenant environments self.exch.load_htable() if options.guid != None: logging.info("Search for an address book with objectGUID = %s" % options.guid) guid = uuid.string_to_bin(options.guid) name = None else: guid = None name = options.name table_MId = 0 for MId in self.exch.htable: if MId == 0: # GAL continue if guid is not None: # -guid if self.exch.htable[MId]['guid'] == guid: logging.debug("MId %d is assigned for %s object" % (MId, options.guid)) logging.info("Lookuping %s" % self.exch.htable[MId]['name']) table_MId = MId break else: # -name if self.exch.htable[MId]['name'] == name: guid = uuid.bin_to_string(self.exch.htable[MId]['guid']) logging.debug("MId %d is assigned for %s object" % (MId, guid)) logging.info("Lookuping address book with objectGUID = %s" % guid) table_MId = MId break if table_MId == 0: logging.error("Specified address book not found!") sys.exit(1) self.exch.req_print_table_rows(table_MId, propTags, options.rows_per_request)
def dump(self): print("[CAPI]") print("Version : %8x (%d)" % (self['Version'], self['Version'])) print("UniqueNameLen : %8x (%d)" % (self['UniqueNameLen'], self['UniqueNameLen'])) print("SiPublicKeyLen : %8x (%d)" % (self['SiPublicKeyLen'], self['SiPublicKeyLen'])) print("SiPrivateKeyLen : %8x (%d)" % (self['SiPrivateKeyLen'], self['SiPrivateKeyLen'])) print("Unique Name : (%s)" % self['UniqueName'].decode('utf-8')) print("Hash : (%s)" % ((self['Hash']))) print("PublicKey : (%s)" % (hexlify(self['PublicKey']))) print() print("pkVersion : (%s)" % (self['pkVersion'])) print("guidProvider : (%s)" % (bin_to_string(self['guidProvider']))) print("MasterKeyVersion : %8x (%d)" % (self['MasterKeyVersion_'], self['MasterKeyVersion_'])) print("guidMasterKey : (%s)" % (bin_to_string(self['guidMasterKey']))) print("Flags : %8x (%s)" % (self['Flags'], getFlags(FLAGS, self['Flags']))) print("DescriptionLen : %8x (%d)" % (self['DescriptionLen'], self['DescriptionLen'])) print("Description : %s" % (self['Description'].decode('utf-16le'))) print("CryptAlgo : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name)) print("CryptAlgoLen : %8x (%d)" % (self['CryptAlgoLen'], self['CryptAlgoLen'])) print("SaltLen : %8x (%d)" % (self['SaltLen'], self['SaltLen'])) print("Salt : %s" % (hexlify(self['Salt']))) print("HMacKeyLen : %8x (%d)" % (self['HMacKeyLen'], self['HMacKeyLen'])) print("HMacKey : %s" % (hexlify(self['HMacKey']))) #print("HashAlgoLen : %s " % bytes(hexlify(self['HashAlgoLen']))) print("HashAlgo : %.8x (%d) (%s)" % (self['HashAlgo'], self['HashAlgo'], ALGORITHMS(self['HashAlgo']).name)) print("HMac : %s" % (hexlify(self['HMac']))) print("DataLen : %8x (%d)" % (self['DataLen'], self['DataLen'])) print("Data : %s" % (hexlify(self['Data']))) print("SignLen : %8x (%d)" % (self['SignLen'], self['SignLen'])) print("Sign : %s" % (hexlify(self['Sign']))) print() print("expkVersion : (%s)" % (self['expkVersion'])) print("exguidProvider : (%s)" % (bin_to_string(self['exguidProvider']))) print("exMasterKeyVersion : %8x (%d)" % (self['exMasterKeyVersion_'], self['exMasterKeyVersion_'])) print("exguidMasterKey : (%s)" % (bin_to_string(self['exguidMasterKey']))) print("exFlags : %8x (%s)" % (self['exFlags'], getFlags(FLAGS, self['exFlags']))) print("exDescriptionLen : %8x (%d)" % (self['exDescriptionLen'], self['exDescriptionLen'])) print("exDescription : %s" % (self['exDescription'].decode('utf-16le'))) print("exCryptAlgo : %.8x (%d) (%s)" % (self['exCryptAlgo'], self['exCryptAlgo'], ALGORITHMS(self['exCryptAlgo']).name)) print("exCryptAlgoLen : %8x (%d)" % (self['exCryptAlgoLen'], self['exCryptAlgoLen'])) print("exSaltLen : %8x (%d)" % (self['exSaltLen'], self['exSaltLen'])) print("exSalt : %s" % (hexlify(self['exSalt']))) print("exHMacKeyLen : %8x (%d)" % (self['exHMacKeyLen'], self['exHMacKeyLen'])) print("exHMacKey : %s" % (hexlify(self['exHMacKey']))) #print("exHashAlgoLen : %s " % bytes(hexlify(self['exHashAlgoLen']))) print("exHashAlgo : %.8x (%d) (%s)" % (self['exHashAlgo'], self['exHashAlgo'], ALGORITHMS(self['exHashAlgo']).name)) print("exHMac2Key : %s" % (hexlify(self['exHMac2Key']))) print("exDataLen : %8x (%d)" % (self['exDataLen'], self['exDataLen'])) print("exData : %s" % (hexlify(self['exData']))) print("exSignLen : %8x (%d)" % (self['exSignLen'], self['exSignLen'])) print("exSign : %s" % (hexlify(self['exSign']))) print()
def checkSecurityDescriptors(self, entries, privs, membersids, sidmapping, domainDumper): standardrights = [ self.GENERIC_ALL, self.GENERIC_WRITE, self.GENERIC_READ, ACCESS_MASK.WRITE_DACL ] for entry in entries: if entry['type'] != 'searchResEntry': continue dn = entry['dn'] try: sdData = entry['raw_attributes']['nTSecurityDescriptor'][0] except IndexError: # We don't have the privileges to read this security descriptor continue hasFullControl = False secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR() secDesc.fromString(sdData) if secDesc['OwnerSid'] != '' and secDesc['OwnerSid'].formatCanonical() in membersids: sid = secDesc['OwnerSid'].formatCanonical() LOG.debug('Permission found: Full Control on %s; Reason: Owner via %s' % (dn, sidmapping[sid])) hasFullControl = True # Iterate over all the ACEs for ace in secDesc['Dacl'].aces: sid = ace['Ace']['Sid'].formatCanonical() if ace['AceType'] != ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE and ace['AceType'] != ACCESS_ALLOWED_ACE.ACE_TYPE: continue if not ace.hasFlag(ACE.INHERITED_ACE) and ace.hasFlag(ACE.INHERIT_ONLY_ACE): # ACE is set on this object, but only inherited, so not applicable to us continue # Check if the ACE has restrictions on object type if ace['AceType'] == ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE \ and ace.hasFlag(ACE.INHERITED_ACE) \ and ace['Ace'].hasFlag(ACCESS_ALLOWED_OBJECT_ACE.ACE_INHERITED_OBJECT_TYPE_PRESENT): # Verify if the ACE applies to this object type inheritedObjectType = bin_to_string(ace['Ace']['InheritedObjectType']).lower() if not self.aceApplies(inheritedObjectType, entry['raw_attributes']['objectClass'][-1]): continue # Check for non-extended rights that may not apply to us if ace['Ace']['Mask']['Mask'] in standardrights or ace['Ace']['Mask'].hasPriv(ACCESS_MASK.WRITE_DACL): # Check if this applies to our objecttype if ace['AceType'] == ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE and ace['Ace'].hasFlag(ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT): objectType = bin_to_string(ace['Ace']['ObjectType']).lower() if not self.aceApplies(objectType, entry['raw_attributes']['objectClass'][-1]): # LOG.debug('ACE does not apply, only to %s', objectType) continue if sid in membersids: # Generic all if ace['Ace']['Mask'].hasPriv(self.GENERIC_ALL): ace.dump() LOG.debug('Permission found: Full Control on %s; Reason: GENERIC_ALL via %s' % (dn, sidmapping[sid])) hasFullControl = True if can_create_users(ace) or hasFullControl: if not hasFullControl: LOG.debug('Permission found: Create users in %s; Reason: Granted to %s' % (dn, sidmapping[sid])) if dn == 'CN=Users,%s' % domainDumper.root: # We can create users in the default container, this is preferred privs['create'] = True privs['createIn'] = dn else: # Could be a different OU where we have access # store it until we find a better place if privs['createIn'] != 'CN=Users,%s' % domainDumper.root and 'organizationalUnit' in entry['raw_attributes']['objectClass']: privs['create'] = True privs['createIn'] = dn if can_add_member(ace) or hasFullControl: if 'group' in entry['raw_attributes']['objectClass']: # We can add members to a group if not hasFullControl: LOG.debug('Permission found: Add member to %s; Reason: Granted to %s' % (dn, sidmapping[sid])) privs['escalateViaGroup'] = True privs['escalateGroup'] = dn if ace['Ace']['Mask'].hasPriv(ACCESS_MASK.WRITE_DACL) or hasFullControl: if not hasFullControl: LOG.debug('Permission found: Write Dacl of %s; Reason: Granted to %s' % (dn, sidmapping[sid])) # We can modify the domain Dacl if 'domain' in entry['raw_attributes']['objectClass']: privs['aclEscalate'] = True privs['aclEscalateIn'] = dn
def parse_uuid(_uuid): return uuid.bin_to_string(_uuid)
def get_object_type(self): if self.has_flag(self.ACE_OBJECT_TYPE_PRESENT): return bin_to_string(self.data.ObjectType) return None
def get_inherited_object_type(self): if self.has_flag(self.ACE_INHERITED_OBJECT_TYPE_PRESENT): return bin_to_string(self.data.InheritedObjectType) return None
def dump(self): print("[CREDHIST]") print("Version : %8x (%d)" % (self['Version'], self['Version'])) print("Guid : %s" % bin_to_string(self['Guid'])) print()
def parse_EPM_Entry_Floors(entry_Floors): entry_Tower_Floor_Info = { 'UUID': [], 'NDR_UUID': [], 'IP': '', 'PIPE': [], 'NETBIOS': [], 'APPLICATION': '', 'PROTOCOL': '' } for floor in entry_Floors: protocol = floor.getData()[2] protocol = int(ord(protocol)) if protocol == PROTO_ID_UUID: floor_fields = floor.fields # import ipdb; ipdb.set_trace() floor_MajorVersion = floor_fields.get('MajorVersion') floor_MinorVersion = floor_fields.get('MinorVersion') floor_InterfaceIdent = floor_fields.get('InterfaceIdent') floor_RHSByteCount = floor_fields.get('RHSByteCount') # different UUID definition in EPMFloor Structure from (impacket/dcerpc/v5/epm.py) floor_InterfaceUUID = floor_fields.get('InterfaceUUID') floor_DataRepUuid = floor_fields.get('DataRepUuid') floor_LHSByteCount = floor_fields.get('LHSByteCount') floor_UUID = floor_InterfaceUUID if floor_InterfaceUUID else floor_DataRepUuid floor_UUIDStr = uuid.bin_to_string(floor_UUID) floor_Version = float("%s.%s" % (floor_MajorVersion, floor_MinorVersion)) if not floor_UUIDStr: continue if floor_UUIDStr.upper() in (NDR64_UUID, NDR32_UUID): entry_Tower_Floor_Info['NDR_UUID'] = (floor_UUIDStr, floor_Version) else: entry_Tower_Floor_Info['UUID'] = (floor_UUIDStr, floor_Version) elif protocol == PROTO_ID_IP: floor_fields = floor.fields entry_Tower_Floor_Info['IP'] = socket.inet_ntoa( floor_fields['RelatedData']) elif protocol in (PROTO_ID_NAMED_PIPES, PROTO_ID_NAMED_PIPES_2): floor_fields = floor.fields named_pipe = floor_fields.get('RelatedData') if not named_pipe: continue if named_pipe not in entry_Tower_Floor_Info['PIPE']: entry_Tower_Floor_Info['PIPE'].append(named_pipe) elif protocol == PROTO_ID_NETBIOS: floor_fields = floor.fields netbios_name = floor_fields.get('RelatedData') if not netbios_name: continue if netbios_name not in entry_Tower_Floor_Info['NETBIOS']: entry_Tower_Floor_Info['NETBIOS'].append(netbios_name) entry_UUID = entry_Tower_Floor_Info['UUID'] if entry_UUID: # Add APPLICATION Info into entry_Tower_Floor_Info _uuid, _ver = entry_UUID key = uuid.uuidtup_to_bin(uuid.string_to_uuidtup(_uuid))[:18] application = epm.KNOWN_UUIDS[key] if key in epm.KNOWN_UUIDS else 'N/A' entry_Tower_Floor_Info['APPLICATION'] = application # Add POTOCOL Info into entry_Tower_Floor_Info key = _uuid[:36] protocol = epm.KNOWN_PROTOCOLS[ key] if key in epm.KNOWN_PROTOCOLS else 'N/A' entry_Tower_Floor_Info['PROTOCOL'] = protocol return entry_Tower_Floor_Info
def simplifyPropertyRow(rowSetElem): row = {} for prop in rowSetElem['lpProps']: prop_name_in_union = prop['Value'].structure[0][0] prop_value = prop['Value'].fields[prop_name_in_union] PropTag = prop['ulPropTag'] if isinstance(prop_value, SHORT) or \ isinstance(prop_value, USHORT) or \ isinstance(prop_value, LONG) or \ isinstance(prop_value, ULONG): row[PropTag] = int(prop_value['Data']) elif isinstance(prop_value, LPWSTR): if PropTag in [0x8c38001f]: # What is this field for? row[PropTag] = ExchBinaryObject( prop_value['Data'].encode("utf-16le")[:-2]) else: row[PropTag] = prop_value['Data'][:-1] elif isinstance(prop_value, LPSTR): row[PropTag] = prop_value['Data'][:-1] elif isinstance(prop_value, Binary_r): value = b''.join(prop_value['lpb']) if PropTag in [0x80270102, 0x8c750102]: value = EXCH_SID(value) elif PropTag == 0x300b0102: value = value[:-1].decode("utf-8") elif value[4:20] == GUID_NSPI and value[ 20: 24] == b'\x01\x00\x00\x00' and value[: 4] == b'\x00\x00\x00\x00': value = PermanentEntryID(value) elif value[:4] == b'\x87\x00\x00\x00' and value[ 20:24] == b'\x01\x00\x00\x00' and len(value) == 32: value = EphemeralEntryID(value) elif PropTag in [0x8c6d0102, 0x68c40102, 0x8c730102, 0x0ff80102]: value = uuid.bin_to_string(value).lower() elif PropTag == 0x0ff60102: value = unpack('<l', value)[0] else: value = ExchBinaryObject(value) row[PropTag] = value elif isinstance(prop_value, BinaryArray_r): array = [] for value in prop_value['lpbin']: array.append(b''.join(value['lpb'])) row[PropTag] = array elif isinstance(prop_value, StringArray_r): array = [] for value in prop_value['lppszA']: array.append(value['Data'][:-1]) row[PropTag] = array elif isinstance(prop_value, WStringArray_r): array = [] for value in prop_value['lppszW']: array.append(value['Data'][:-1]) row[PropTag] = array elif isinstance(prop_value, FILETIME): row[PropTag] = datetime.fromtimestamp( \ getUnixTime(unpack('<Q', prop_value.getData())[0])) else: row[PropTag] = prop_value return row
def parseRow(self,token,tuplemode=False): # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types # help would be appreciated ;) if len(token) == 1: return 0 row = [] if tuplemode else {} origDataLen = len(token['Data']) data = token['Data'] for col in self.colMeta: _type = col['Type'] if (_type == TDS_NVARCHARTYPE) |\ (_type == TDS_NCHARTYPE): #print "NVAR 0x%x" % _type charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen].decode('utf-16le') data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARCHRTYPE): charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_GUIDTYPE): uuidLen = ord(data[0]) data = data[1:] if uuidLen > 0: uu = data[:uuidLen] value = uuid.bin_to_string(uu) data = data[uuidLen:] else: value = 'NULL' elif (_type == TDS_NTEXTTYPE) |\ (_type == TDS_IMAGETYPE) : # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1+charLen+8:] charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: if _type == TDS_NTEXTTYPE: value = data[:charLen].decode('utf-16le') else: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_TEXTTYPE): # Skip the pointer data charLen = ord(data[0]) if charLen == 0: value = 'NULL' data = data[1:] else: data = data[1+charLen+8:] charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0] data = data[struct.calcsize('<L'):] if charLen != 0xFFFF: value = data[:charLen] data = data[charLen:] else: value = 'NULL' elif (_type == TDS_BIGVARBINTYPE) |\ (_type == TDS_BIGBINARYTYPE): charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] if charLen != 0xFFFF: value = binascii.b2a_hex(data[:charLen]) data = data[charLen:] else: value = 'NULL' elif (_type == TDS_DATETIM4TYPE) |\ (_type == TDS_DATETIMNTYPE) |\ (_type == TDS_DATETIMETYPE): value = '' if _type == TDS_DATETIMNTYPE: # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and # datetime SQL data types respectively. if ord(data[0]) == 4: _type = TDS_DATETIM4TYPE elif ord(data[0]) == 8: _type = TDS_DATETIMETYPE else: value = 'NULL' data = data[1:] if (_type == TDS_DATETIMETYPE): # datetime is represented in the following sequence: # * One 4-byte signed integer that represents the number of days since January 1, 1900. Negative # numbers are allowed to represents dates since January 1, 1753. # * One 4-byte unsigned integer that represents the number of one three-hundredths of a second # (300 counts per second) elapsed since 12 AM that day. dateValue = struct.unpack('<l',data[:4])[0] data = data[4:] if dateValue < 0: baseDate = datetime.date(1753,1,1) else: baseDate = datetime.date(1900,1,1) timeValue = struct.unpack('<L',data[:4])[0] data = data[4:] elif (_type == TDS_DATETIM4TYPE): # Small datetime # 2.2.5.5.1.8 # Date/Times # smalldatetime is represented in the following sequence: # * One 2-byte unsigned integer that represents the number of days since January 1, 1900. # * One 2-byte unsigned integer that represents the number of minutes elapsed since 12 AM that # day. dateValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] timeValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] baseDate = datetime.date(1900,1,1) if value != 'NULL': dateValue = datetime.date.fromordinal(baseDate.toordinal() + dateValue) hours, mod = divmod(timeValue/300, 60*60) minutes, second = divmod(mod, 60) value = datetime.datetime(dateValue.year, dateValue.month, dateValue.day, hours, minutes, second) elif (_type == TDS_INT4TYPE) |\ (_type == TDS_MONEY4TYPE) |\ (_type == TDS_FLT4TYPE): #print "INT4" value = struct.unpack('<l',data[:struct.calcsize('<l')])[0] data = data[struct.calcsize('<l'):] elif (_type == TDS_FLTNTYPE): valueSize = ord(data[:1]) if valueSize == 4: fmt = '<f' elif valueSize == 8: fmt = '<d' data = data[1:] if valueSize > 0: value = struct.unpack(fmt,data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif _type == TDS_MONEYNTYPE: valueSize = ord(data[:1]) if valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' data = data[1:] if valueSize > 0: value = struct.unpack(fmt,data[:valueSize])[0] if valueSize == 4: value = float(value) / math.pow(10,4) else: value = float(value >> 32) / math.pow(10,4) data = data[valueSize:] else: value = 'NULL' elif _type == TDS_BIGCHARTYPE: #print "BIGC" charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] data = data[struct.calcsize('<H'):] value = data[:charLen] data = data[charLen:] elif (_type == TDS_INT8TYPE) |\ (_type == TDS_FLT8TYPE) |\ (_type == TDS_MONEYTYPE): #print "DATETIME" value = struct.unpack('<q',data[:struct.calcsize('<q')])[0] data = data[struct.calcsize('<q'):] elif (_type == TDS_INT2TYPE): #print "INT2TYPE" value = struct.unpack('<H',(data[:2]))[0] data = data[2:] elif (_type == TDS_DATENTYPE): # date is represented as one 3-byte unsigned integer that represents the number of days since # January 1, year 1. valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: dateBytes = data[:valueSize] dateValue = struct.unpack('<L','\x00'+dateBytes)[0] value = datetime.date.fromtimestamp(dateValue) data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_BITTYPE) |\ (_type == TDS_INT1TYPE): #print "BITTYPE" value = ord(data[:1]) data = data[1:] elif (_type == TDS_NUMERICNTYPE) |\ (_type == TDS_DECIMALNTYPE): valueLen = ord(data[:1]) data = data[1:] value = data[:valueLen] data = data[valueLen:] precision = ord(col['TypeData'][1]) scale = ord(col['TypeData'][2]) if valueLen > 0: isPositiveSign = ord(value[0]) if (valueLen-1) == 2: fmt = '<H' elif (valueLen-1) == 4: fmt = '<L' elif (valueLen-1) == 8: fmt = '<Q' else: # Still don't know how to handle higher values value = "TODO: Interpret TDS_NUMERICNTYPE correctly" number = struct.unpack(fmt, value[1:])[0] number /= math.pow(precision, scale) if isPositiveSign == 0: number *= -1 value = number else: value = 'NULL' elif (_type == TDS_BITNTYPE): #print "BITNTYPE" valueSize = ord(data[:1]) data = data[1:] if valueSize > 0: if valueSize == 1: value = ord(data[:valueSize]) else: value = data[:valueSize] else: value = 'NULL' data = data[valueSize:] elif (_type == TDS_INTNTYPE): valueSize = ord(data[:1]) if valueSize == 1: fmt = '<B' elif valueSize == 2: fmt = '<h' elif valueSize == 4: fmt = '<l' elif valueSize == 8: fmt = '<q' else: fmt = '' data = data[1:] if valueSize > 0: value = struct.unpack(fmt,data[:valueSize])[0] data = data[valueSize:] else: value = 'NULL' elif (_type == TDS_SSVARIANTTYPE): print "ParseRow: SQL Variant type not yet supported :(" raise else: print "ParseROW: Unsupported data type: 0%x" % _type raise if tuplemode: row.append(value) else: row[col['Name']] = value self.rows.append(row) return (origDataLen - len(data))
def run(self): if self.options.action.upper() == 'MASTERKEY': fp = open(options.file, 'rb') data = fp.read() mkf= MasterKeyFile(data) mkf.dump() data = data[len(mkf):] if mkf['MasterKeyLen'] > 0: mk = MasterKey(data[:mkf['MasterKeyLen']]) data = data[len(mk):] if mkf['BackupKeyLen'] > 0: bkmk = MasterKey(data[:mkf['BackupKeyLen']]) data = data[len(bkmk):] if mkf['CredHistLen'] > 0: ch = CredHist(data[:mkf['CredHistLen']]) data = data[len(ch):] if mkf['DomainKeyLen'] > 0: dk = DomainKey(data[:mkf['DomainKeyLen']]) data = data[len(dk):] if self.options.system and self.options.security: # We have hives, let's try to decrypt with them self.getLSA() decryptedKey = mk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted Backup key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted Backup key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.key: key = unhexlify(self.options.key[2:]) decryptedKey = mk.decrypt(key) if decryptedKey: print('Decrypted key with key provided') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.pvk and dk: pvkfile = open(self.options.pvk, 'rb').read() key = PRIVATE_KEY_BLOB(pvkfile[len(PVK_FILE_HDR()):]) private = privatekeyblob_to_pkcs1(key) cipher = PKCS1_v1_5.new(private) decryptedKey = cipher.decrypt(dk['SecretData'][::-1], None) if decryptedKey: domain_master_key = DPAPI_DOMAIN_RSA_MASTER_KEY(decryptedKey) key = domain_master_key['buffer'][:domain_master_key['cbMasterKey']] print('Decrypted key with domain backup key provided') print('Decrypted key: 0x%s' % hexlify(key).decode('latin-1')) return elif self.options.sid and self.options.key is None: # Do we have a password? if self.options.password is None: # Nope let's ask it from getpass import getpass password = getpass("Password:"******"Password:"******"G$BCKUPKEY_PREFERRED", "G$BCKUPKEY_P"): buffer = crypto.decryptSecret(connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], keyname)) guid = bin_to_string(buffer) name = "G$BCKUPKEY_{}".format(guid) secret = crypto.decryptSecret(connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], name)) keyVersion = struct.unpack('<L', secret[:4])[0] if keyVersion == 1: # legacy key backup_key = P_BACKUP_KEY(secret) backupkey = backup_key['Data'] if self.options.export: logging.debug("Exporting key to file {}".format(name + ".key")) open(name + ".key", 'wb').write(backupkey) else: print("Legacy key:") print("0x%s" % hexlify(backupkey)) print("\n") elif keyVersion == 2: # preferred key backup_key = PREFERRED_BACKUP_KEY(secret) pvk = backup_key['Data'][:backup_key['KeyLength']] cert = backup_key['Data'][backup_key['KeyLength']:backup_key['KeyLength'] + backup_key['CertificateLength']] # build pvk header (PVK_MAGIC, PVK_FILE_VERSION_0, KeySpec, PVK_NO_ENCRYPT, 0, cbPvk) header = PVK_FILE_HDR() header['dwMagic'] = 0xb0b5f11e header['dwVersion'] = 0 header['dwKeySpec'] = 1 header['dwEncryptType'] = 0 header['cbEncryptData'] = 0 header['cbPvk'] = backup_key['KeyLength'] backupkey_pvk = header.getData() + pvk # pvk blob backupkey = backupkey_pvk if self.options.export: logging.debug("Exporting certificate to file {}".format(name + ".der")) open(name + ".der", 'wb').write(cert) logging.debug("Exporting private key to file {}".format(name + ".pvk")) open(name + ".pvk", 'wb').write(backupkey) else: print("Preferred key:") header.dump() print("PRIVATEKEYBLOB:{%s}" % (hexlify(backupkey))) print("\n") return elif self.options.action.upper() == 'CREDENTIAL': fp = open(options.file, 'rb') data = fp.read() cred = CredentialFile(data) blob = DPAPI_BLOB(cred['Data']) if self.options.key is not None: key = unhexlify(self.options.key[2:]) decrypted = blob.decrypt(key) if decrypted is not None: creds = CREDENTIAL_BLOB(decrypted) creds.dump() return else: # Just print the data blob.dump() elif self.options.action.upper() == 'VAULT': if options.vcrd is None and options.vpol is None: print('You must specify either -vcrd or -vpol parameter. Type --help for more info') return if options.vcrd is not None: fp = open(options.vcrd, 'rb') data = fp.read() blob = VAULT_VCRD(data) if self.options.key is not None: key = unhexlify(self.options.key[2:]) cleartext = None for i, entry in enumerate(blob.attributesLen): if entry > 28: attribute = blob.attributes[i] if 'IV' in attribute.fields and len(attribute['IV']) == 16: cipher = AES.new(key, AES.MODE_CBC, iv=attribute['IV']) else: cipher = AES.new(key, AES.MODE_CBC) cleartext = cipher.decrypt(attribute['Data']) if cleartext is not None: # Lookup schema Friendly Name and print if we find one if blob['FriendlyName'].decode('utf-16le')[:-1] in VAULT_KNOWN_SCHEMAS: # Found one. Cast it and print vault = VAULT_KNOWN_SCHEMAS[blob['FriendlyName'].decode('utf-16le')[:-1]](cleartext) vault.dump() else: # otherwise hexdump(cleartext) return else: blob.dump() elif options.vpol is not None: fp = open(options.vpol, 'rb') data = fp.read() vpol = VAULT_VPOL(data) vpol.dump() if self.options.key is not None: key = unhexlify(self.options.key[2:]) blob = vpol['Blob'] data = blob.decrypt(key) if data is not None: keys = VAULT_VPOL_KEYS(data) keys.dump() return print('Cannot decrypt (specify -key or -sid whenever applicable) ')
def run(self): if self.options.action.upper() == 'MASTERKEY': fp = open(options.file, 'rb') data = fp.read() mkf = MasterKeyFile(data) mkf.dump() data = data[len(mkf):] if mkf['MasterKeyLen'] > 0: mk = MasterKey(data[:mkf['MasterKeyLen']]) data = data[len(mk):] if mkf['BackupKeyLen'] > 0: bkmk = MasterKey(data[:mkf['BackupKeyLen']]) data = data[len(bkmk):] if mkf['CredHistLen'] > 0: ch = CredHist(data[:mkf['CredHistLen']]) data = data[len(ch):] if mkf['DomainKeyLen'] > 0: dk = DomainKey(data[:mkf['DomainKeyLen']]) data = data[len(dk):] if self.options.system and self.options.security and self.options.sid is None: # We have hives, let's try to decrypt with them self.getLSA() decryptedKey = mk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted Backup key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted Backup key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.system and self.options.security: # Use SID + hash # We have hives, let's try to decrypt with them self.getLSA() key1, key2 = self.deriveKeysFromUserkey( self.options.sid, self.dpapiSystem['UserKey']) decryptedKey = mk.decrypt(key1) if decryptedKey: print('Decrypted key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(key1) if decryptedKey: print('Decrypted Backup key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(key2) if decryptedKey: print('Decrypted key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(key2) if decryptedKey: print('Decrypted Backup key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.key and self.options.sid: key = unhexlify(self.options.key[2:]) key1, key2 = self.deriveKeysFromUserkey(self.options.sid, key) decryptedKey = mk.decrypt(key1) if decryptedKey: print('Decrypted key with key provided + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(key2) if decryptedKey: print('Decrypted key with key provided + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.key: key = unhexlify(self.options.key[2:]) decryptedKey = mk.decrypt(key) if decryptedKey: print('Decrypted key with key provided') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.pvk and dk: pvkfile = open(self.options.pvk, 'rb').read() key = PRIVATE_KEY_BLOB(pvkfile[len(PVK_FILE_HDR()):]) private = privatekeyblob_to_pkcs1(key) cipher = PKCS1_v1_5.new(private) decryptedKey = cipher.decrypt(dk['SecretData'][::-1], None) if decryptedKey: domain_master_key = DPAPI_DOMAIN_RSA_MASTER_KEY( decryptedKey) key = domain_master_key[ 'buffer'][:domain_master_key['cbMasterKey']] print('Decrypted key with domain backup key provided') print('Decrypted key: 0x%s' % hexlify(key).decode('latin-1')) return elif self.options.sid and self.options.key is None: # Do we have a password? if self.options.password is None: # Nope let's ask it from getpass import getpass password = getpass("Password:"******"Password:"******"Password:"******"G$BCKUPKEY_PREFERRED", "G$BCKUPKEY_P"): buffer = crypto.decryptSecret( connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], keyname)) guid = bin_to_string(buffer) name = "G$BCKUPKEY_{}".format(guid) secret = crypto.decryptSecret( connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], name)) keyVersion = struct.unpack('<L', secret[:4])[0] if keyVersion == 1: # legacy key backup_key = P_BACKUP_KEY(secret) backupkey = backup_key['Data'] if self.options.export: logging.debug( "Exporting key to file {}".format(name + ".key")) open(name + ".key", 'wb').write(backupkey) else: print("Legacy key:") print("0x%s" % hexlify(backupkey).decode('latin-1')) print("\n") elif keyVersion == 2: # preferred key backup_key = PREFERRED_BACKUP_KEY(secret) pvk = backup_key['Data'][:backup_key['KeyLength']] cert = backup_key['Data'][ backup_key['KeyLength']:backup_key['KeyLength'] + backup_key['CertificateLength']] # build pvk header (PVK_MAGIC, PVK_FILE_VERSION_0, KeySpec, PVK_NO_ENCRYPT, 0, cbPvk) header = PVK_FILE_HDR() header['dwMagic'] = 0xb0b5f11e header['dwVersion'] = 0 header['dwKeySpec'] = 1 header['dwEncryptType'] = 0 header['cbEncryptData'] = 0 header['cbPvk'] = backup_key['KeyLength'] backupkey_pvk = header.getData() + pvk # pvk blob backupkey = backupkey_pvk if self.options.export: logging.debug( "Exporting certificate to file {}".format(name + ".der")) open(name + ".der", 'wb').write(cert) logging.debug( "Exporting private key to file {}".format(name + ".pvk")) open(name + ".pvk", 'wb').write(backupkey) else: print("Preferred key:") header.dump() print("PRIVATEKEYBLOB:{%s}" % (hexlify(backupkey).decode('latin-1'))) print("\n") return elif self.options.action.upper() == 'CREDENTIAL': fp = open(options.file, 'rb') data = fp.read() cred = CredentialFile(data) blob = DPAPI_BLOB(cred['Data']) if self.options.key is not None: key = unhexlify(self.options.key[2:]) decrypted = blob.decrypt(key) if decrypted is not None: creds = CREDENTIAL_BLOB(decrypted) creds.dump() return else: # Just print the data blob.dump() elif self.options.action.upper() == 'VAULT': if options.vcrd is None and options.vpol is None: print( 'You must specify either -vcrd or -vpol parameter. Type --help for more info' ) return if options.vcrd is not None: fp = open(options.vcrd, 'rb') data = fp.read() blob = VAULT_VCRD(data) if self.options.key is not None: key = unhexlify(self.options.key[2:]) cleartext = None for i, entry in enumerate(blob.attributesLen): if entry > 28: attribute = blob.attributes[i] if 'IV' in attribute.fields and len( attribute['IV']) == 16: cipher = AES.new(key, AES.MODE_CBC, iv=attribute['IV']) else: cipher = AES.new(key, AES.MODE_CBC) cleartext = cipher.decrypt(attribute['Data']) if cleartext is not None: # Lookup schema Friendly Name and print if we find one if blob['FriendlyName'].decode( 'utf-16le')[:-1] in VAULT_KNOWN_SCHEMAS: # Found one. Cast it and print vault = VAULT_KNOWN_SCHEMAS[ blob['FriendlyName'].decode('utf-16le')[:-1]]( cleartext) vault.dump() else: # otherwise hexdump(cleartext) return else: blob.dump() elif options.vpol is not None: fp = open(options.vpol, 'rb') data = fp.read() vpol = VAULT_VPOL(data) vpol.dump() if self.options.key is not None: key = unhexlify(self.options.key[2:]) blob = vpol['Blob'] data = blob.decrypt(key) if data is not None: keys = VAULT_VPOL_KEYS(data) keys.dump() return elif self.options.action.upper() == 'UNPROTECT': fp = open(options.file, 'rb') data = fp.read() blob = DPAPI_BLOB(data) if self.options.key is not None: key = unhexlify(self.options.key[2:]) if self.options.entropy_file is not None: fp2 = open(self.options.entropy_file, 'rb') entropy = fp2.read() fp2.close() elif self.options.entropy is not None: entropy = b(self.options.entropy) else: entropy = None decrypted = blob.decrypt(key, entropy) if decrypted is not None: print('Successfully decrypted data') hexdump(decrypted) return else: # Just print the data blob.dump() print('Cannot decrypt (specify -key or -sid whenever applicable) ')
def can_create_users(ace): createprivs = ace['Ace']['Mask'].hasPriv(ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_CREATE_CHILD) if ace['AceType'] != ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE or ace['Ace']['ObjectType'] == '': return False userprivs = bin_to_string(ace['Ace']['ObjectType']).lower() == 'bf967aba-0de6-11d0-a285-00aa003049e2' return createprivs and userprivs
def checkSecurityDescriptors(self, entries, privs, membersids, sidmapping, domainDumper): standardrights = [ self.GENERIC_ALL, self.GENERIC_WRITE, self.GENERIC_READ, ACCESS_MASK.WRITE_DACL ] for entry in entries: if entry['type'] != 'searchResEntry': continue dn = entry['dn'] try: sdData = entry['raw_attributes']['nTSecurityDescriptor'][0] except IndexError: # We don't have the privileges to read this security descriptor LOG.debug('Access to security descriptor was denied for DN %s', dn) continue hasFullControl = False secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR() secDesc.fromString(sdData) if secDesc['OwnerSid'] != '' and secDesc[ 'OwnerSid'].formatCanonical() in membersids: sid = secDesc['OwnerSid'].formatCanonical() LOG.debug( 'Permission found: Full Control on %s; Reason: Owner via %s' % (dn, sidmapping[sid])) hasFullControl = True # Iterate over all the ACEs for ace in secDesc['Dacl'].aces: sid = ace['Ace']['Sid'].formatCanonical() if ace['AceType'] != ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE and ace[ 'AceType'] != ACCESS_ALLOWED_ACE.ACE_TYPE: continue if not ace.hasFlag(ACE.INHERITED_ACE) and ace.hasFlag( ACE.INHERIT_ONLY_ACE): # ACE is set on this object, but only inherited, so not applicable to us continue # Check if the ACE has restrictions on object type (inherited case) if ace['AceType'] == ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE \ and ace.hasFlag(ACE.INHERITED_ACE) \ and ace['Ace'].hasFlag(ACCESS_ALLOWED_OBJECT_ACE.ACE_INHERITED_OBJECT_TYPE_PRESENT): # Verify if the ACE applies to this object type inheritedObjectType = bin_to_string( ace['Ace']['InheritedObjectType']).lower() if not self.aceApplies( inheritedObjectType, entry['raw_attributes']['objectClass'][-1]): continue # Check for non-extended rights that may not apply to us if ace['Ace']['Mask']['Mask'] in standardrights or ace['Ace'][ 'Mask'].hasPriv(ACCESS_MASK.WRITE_DACL): # Check if this applies to our objecttype if ace['AceType'] == ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE and ace[ 'Ace'].hasFlag(ACCESS_ALLOWED_OBJECT_ACE. ACE_OBJECT_TYPE_PRESENT): objectType = bin_to_string( ace['Ace']['ObjectType']).lower() if not self.aceApplies( objectType, entry['raw_attributes']['objectClass'][-1]): # LOG.debug('ACE does not apply, only to %s', objectType) continue if sid in membersids: # Generic all if ace['Ace']['Mask'].hasPriv(self.GENERIC_ALL): ace.dump() LOG.debug( 'Permission found: Full Control on %s; Reason: GENERIC_ALL via %s' % (dn, sidmapping[sid])) hasFullControl = True if can_create_users(ace) or hasFullControl: if not hasFullControl: LOG.debug( 'Permission found: Create users in %s; Reason: Granted to %s' % (dn, sidmapping[sid])) if dn == 'CN=Users,%s' % domainDumper.root: # We can create users in the default container, this is preferred privs['create'] = True privs['createIn'] = dn else: # Could be a different OU where we have access # store it until we find a better place if privs[ 'createIn'] != 'CN=Users,%s' % domainDumper.root and b'organizationalUnit' in entry[ 'raw_attributes']['objectClass']: privs['create'] = True privs['createIn'] = dn if can_add_member(ace) or hasFullControl: if b'group' in entry['raw_attributes']['objectClass']: # We can add members to a group if not hasFullControl: LOG.debug( 'Permission found: Add member to %s; Reason: Granted to %s' % (dn, sidmapping[sid])) privs['escalateViaGroup'] = True privs['escalateGroup'] = dn if ace['Ace']['Mask'].hasPriv( ACCESS_MASK.WRITE_DACL) or hasFullControl: # Check if the ACE is an OBJECT ACE, if so the WRITE_DACL is applied to # a property, which is both weird and useless, so we skip it if ace['AceType'] == ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE \ and ace['Ace'].hasFlag(ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT): # LOG.debug('Skipping WRITE_DACL since it has an ObjectType set') continue if not hasFullControl: LOG.debug( 'Permission found: Write Dacl of %s; Reason: Granted to %s' % (dn, sidmapping[sid])) # We can modify the domain Dacl if b'domain' in entry['raw_attributes']['objectClass']: privs['aclEscalate'] = True privs['aclEscalateIn'] = dn
def can_add_member(ace): writeprivs = ace['Ace']['Mask'].hasPriv(ACCESS_ALLOWED_OBJECT_ACE.ADS_RIGHT_DS_WRITE_PROP) if ace['AceType'] != ACCESS_ALLOWED_OBJECT_ACE.ACE_TYPE or ace['Ace']['ObjectType'] == '': return writeprivs userprivs = bin_to_string(ace['Ace']['ObjectType']).lower() == 'bf9679c0-0de6-11d0-a285-00aa003049e2' return writeprivs and userprivs
def run(self): if self.options.action.upper() == 'MASTERKEY': fp = open(options.file, 'rb') data = fp.read() mkf= MasterKeyFile(data) mkf.dump() data = data[len(mkf):] if mkf['MasterKeyLen'] > 0: mk = MasterKey(data[:mkf['MasterKeyLen']]) data = data[len(mk):] if mkf['BackupKeyLen'] > 0: bkmk = MasterKey(data[:mkf['BackupKeyLen']]) data = data[len(bkmk):] if mkf['CredHistLen'] > 0: ch = CredHist(data[:mkf['CredHistLen']]) data = data[len(ch):] if mkf['DomainKeyLen'] > 0: dk = DomainKey(data[:mkf['DomainKeyLen']]) data = data[len(dk):] if self.options.system and self.options.security: # We have hives, let's try to decrypt with them self.getLSA() decryptedKey = mk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted Backup key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted Backup key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.key: key = unhexlify(self.options.key[2:]) decryptedKey = mk.decrypt(key) if decryptedKey: print('Decrypted key with key provided') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.pvk and dk: pvkfile = open(self.options.pvk, 'rb').read() key = PRIVATE_KEY_BLOB(pvkfile[len(PVK_FILE_HDR()):]) private = privatekeyblob_to_pkcs1(key) cipher = PKCS1_v1_5.new(private) decryptedKey = cipher.decrypt(dk['SecretData'][::-1], None) if decryptedKey: domain_master_key = DPAPI_DOMAIN_RSA_MASTER_KEY(decryptedKey) key = domain_master_key['buffer'][:domain_master_key['cbMasterKey']] print('Decrypted key with domain backup key provided') print('Decrypted key: 0x%s' % hexlify(key).decode('latin-1')) return elif self.options.sid and self.options.key is None: # Do we have a password? if self.options.password is None: # Nope let's ask it from getpass import getpass password = getpass("Password:"******"Password:"******"G$BCKUPKEY_PREFERRED", "G$BCKUPKEY_P"): buffer = crypto.decryptSecret(connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], keyname)) guid = bin_to_string(buffer) name = "G$BCKUPKEY_{}".format(guid) secret = crypto.decryptSecret(connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], name)) keyVersion = struct.unpack('<L', secret[:4])[0] if keyVersion == 1: # legacy key backup_key = P_BACKUP_KEY(secret) backupkey = backup_key['Data'] if self.options.export: logging.debug("Exporting key to file {}".format(name + ".key")) open(name + ".key", 'wb').write(backupkey) else: print("Legacy key:") print("0x%s" % hexlify(backupkey).decode('latin-1')) print("\n") elif keyVersion == 2: # preferred key backup_key = PREFERRED_BACKUP_KEY(secret) pvk = backup_key['Data'][:backup_key['KeyLength']] cert = backup_key['Data'][backup_key['KeyLength']:backup_key['KeyLength'] + backup_key['CertificateLength']] # build pvk header (PVK_MAGIC, PVK_FILE_VERSION_0, KeySpec, PVK_NO_ENCRYPT, 0, cbPvk) header = PVK_FILE_HDR() header['dwMagic'] = 0xb0b5f11e header['dwVersion'] = 0 header['dwKeySpec'] = 1 header['dwEncryptType'] = 0 header['cbEncryptData'] = 0 header['cbPvk'] = backup_key['KeyLength'] backupkey_pvk = header.getData() + pvk # pvk blob backupkey = backupkey_pvk if self.options.export: logging.debug("Exporting certificate to file {}".format(name + ".der")) open(name + ".der", 'wb').write(cert) logging.debug("Exporting private key to file {}".format(name + ".pvk")) open(name + ".pvk", 'wb').write(backupkey) else: print("Preferred key:") header.dump() print("PRIVATEKEYBLOB:{%s}" % (hexlify(backupkey).decode('latin-1'))) print("\n") return elif self.options.action.upper() == 'CREDENTIAL': fp = open(options.file, 'rb') data = fp.read() cred = CredentialFile(data) blob = DPAPI_BLOB(cred['Data']) if self.options.key is not None: key = unhexlify(self.options.key[2:]) decrypted = blob.decrypt(key) if decrypted is not None: creds = CREDENTIAL_BLOB(decrypted) creds.dump() return else: # Just print the data blob.dump() elif self.options.action.upper() == 'VAULT': if options.vcrd is None and options.vpol is None: print('You must specify either -vcrd or -vpol parameter. Type --help for more info') return if options.vcrd is not None: fp = open(options.vcrd, 'rb') data = fp.read() blob = VAULT_VCRD(data) if self.options.key is not None: key = unhexlify(self.options.key[2:]) cleartext = None for i, entry in enumerate(blob.attributesLen): if entry > 28: attribute = blob.attributes[i] if 'IV' in attribute.fields and len(attribute['IV']) == 16: cipher = AES.new(key, AES.MODE_CBC, iv=attribute['IV']) else: cipher = AES.new(key, AES.MODE_CBC) cleartext = cipher.decrypt(attribute['Data']) if cleartext is not None: # Lookup schema Friendly Name and print if we find one if blob['FriendlyName'].decode('utf-16le')[:-1] in VAULT_KNOWN_SCHEMAS: # Found one. Cast it and print vault = VAULT_KNOWN_SCHEMAS[blob['FriendlyName'].decode('utf-16le')[:-1]](cleartext) vault.dump() else: # otherwise hexdump(cleartext) return else: blob.dump() elif options.vpol is not None: fp = open(options.vpol, 'rb') data = fp.read() vpol = VAULT_VPOL(data) vpol.dump() if self.options.key is not None: key = unhexlify(self.options.key[2:]) blob = vpol['Blob'] data = blob.decrypt(key) if data is not None: keys = VAULT_VPOL_KEYS(data) keys.dump() return print('Cannot decrypt (specify -key or -sid whenever applicable) ')
def print_htable(self, parent_guid=None): MIds_print = [] for MId in self.htable: if parent_guid == None and 'parent_guid' not in self.htable[MId]: MIds_print.append(MId) elif parent_guid != None and 'parent_guid' in self.htable[MId] and self.htable[MId]['parent_guid'] == parent_guid: MIds_print.append(MId) for MId in MIds_print: ab = self.htable[MId] ab['printed'] = True indent = ' ' * ab['depth'] # Table name print("%s%s" % (indent, ab['name'])) # Count if 'count' in ab: print("%sTotalRecs: %d" % (indent, ab['count'])) # Table params if MId != 0: guid = uuid.bin_to_string(ab['guid']).lower() print("%sGuid: %s" % (indent, guid)) else: print("%sGuid: None" % indent) if ab['is_master'] != 0: print("%sPR_EMS_AB_IS_MASTER attribute is set!" % indent) if self._extended_output: dword = NSPIAttacks._int_to_dword(MId) print("%sAssigned MId: 0x%.08X (%d)" % (indent, dword, MId)) if 'start_mid' in ab: dword = NSPIAttacks._int_to_dword(ab['start_mid']) if dword == 2: print("%sAssigned first record MId: 0x00000002 (MID_END_OF_TABLE)" % indent) else: print("%sAssigned first record MId: 0x%.08X (%d)" % (indent, dword, ab['start_mid'])) flags = parse_bitmask(PR_CONTAINER_FLAGS_VALUES, ab['flags']) print("%sFlags: %s" % (indent, flags)) print() if MId != 0: self.print_htable(parent_guid=ab['guid']) if parent_guid == None: for MId in self.htable: if self.htable[MId]['printed'] == False: print("Found parentless object!") print("Name: %s" % self.htable[MId]['name']) print("Guid: %s" % uuid.bin_to_string(self.htable[MId]['guid']).lower()) print("Parent guid: %s" % uuid.bin_to_string(self.htable[MId]['parent_guid']).lower()) dword = NSPIAttacks._int_to_dword(MId) if MId < 0 else MId print("Assigned MId: 0x%.08X (%d)" % (dword, MId)) flags = parse_bitmask(PR_CONTAINER_FLAGS_VALUES, self.htable[MId]['flags']) print("Flags: %s" % flags) if self.htable[MId]['is_master'] != 0: print("%sPR_EMS_AB_IS_MASTER attribute is set!" % indent) print()