def get_resources(pe): resources = [] if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'): count = 1 for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries: try: resource = {} if resource_type.name is not None: name = str(resource_type.name) else: name = str(pefile.RESOURCE_TYPE.get(resource_type.struct.Id)) if name is None: name = str(resource_type.struct.Id) if hasattr(resource_type, 'directory'): for resource_id in resource_type.directory.entries: if hasattr(resource_id, 'directory'): for resource_lang in resource_id.directory.entries: data = pe.get_data(resource_lang.data.struct.OffsetToData, resource_lang.data.struct.Size) filetype = get_type(data) md5 = get_md5(data) language = pefile.LANG.get(resource_lang.data.lang, None) sublanguage = pefile.get_sublang_name_for_lang( resource_lang.data.lang, resource_lang.data.sublang) offset = ('%-8s' % hex(resource_lang.data.struct.OffsetToData)).strip() size = ('%-8s' % hex(resource_lang.data.struct.Size)).strip() resource = [count, name, offset, md5, size, filetype, language, sublanguage] # Dump resources if requested to and if the file currently being # processed is the opened session file. # This is to avoid that during a --scan all the resources being # scanned are dumped as well. if (self.args.open or self.args.dump) and pe == self.pe: if self.args.dump: folder = self.args.dump else: folder = tempfile.mkdtemp() resource_path = os.path.join( folder, '{0}_{1}_{2}'.format(__sessions__.current.file.md5, offset, name)) resource.append(resource_path) with open(resource_path, 'wb') as resource_handle: resource_handle.write(data) resources.append(resource) count += 1 except Exception as e: self.log('error', e) continue return resources
def get_signed_samples(current=None, cert_filter=None): db = Database() samples = db.find(key='all') results = [] for sample in samples: # Skip if it's the same file. if current: if sample.sha256 == current: continue # Obtain path to the binary. sample_path = get_sample_path(sample.sha256) if not os.path.exists(sample_path): continue # Open PE instance. try: cur_pe = pefile.PE(sample_path) except: continue cur_cert_data = get_certificate(cur_pe) if not cur_cert_data: continue cur_cert_md5 = get_md5(cur_cert_data) if cert_filter: if cur_cert_md5 == cert_filter: results.append([sample.name, sample.md5]) else: results.append([sample.name, sample.md5, cur_cert_md5]) return results
def decompress(self, dump_dir): # Check if the file type is right. # TODO: this might be a bit hacky, need to verify whether malformed # Flash exploit would get a different file type. if 'Flash' not in __sessions__.current.file.type: self.log( 'error', "The opened file doesn't appear to be a valid SWF object") return # Retrieve key information from the opened SWF file. header, version, size, data = self.parse_swf() # Decompressed data. decompressed = None # Check if the file is already a decompressed Flash object. if header == 'FWS': self.log('info', "The opened file doesn't appear to be compressed") return # Check if the file is compressed with zlib. elif header == 'CWS': self.log('info', "The opened file appears to be compressed with Zlib") # Open an handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress and reconstruct the Flash object. decompressed = 'FWS' + compressed.read(5) + zlib.decompress( compressed.read()) # Check if the file is compressed with lzma. elif header == 'ZWS': self.log('info', "The opened file appears to be compressed with Lzma") # We need an third party library to decompress this. if not HAVE_PYLZMA: self.log( 'error', "Missing dependency, please install pylzma (`pip install pylzma`)" ) return # Open and handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress with pylzma and reconstruct the Flash object. ## ZWS(LZMA) ## | 4 bytes | 4 bytes | 4 bytes | 5 bytes | n bytes | 6 bytes | ## | 'ZWS'+version | scriptLen | compressedLen | LZMA props | LZMA data | LZMA end marker | decompressed = 'FWS' + compressed.read(5) compressed.read(4) # skip compressedLen decompressed += pylzma.decompress(compressed.read()) # If we obtained some decompressed data, we print it and eventually # dump it to file. if decompressed: # Print the decompressed data # TODO: this prints too much, need to find a better wayto display # this. Paginate? self.log('', cyan(hexdump(decompressed))) if dump_dir: # Dump the decompressed SWF file to the specified directory # or to the default temporary one. dump_path = os.path.join( dump_dir, '{0}.swf'.format(get_md5(decompressed))) with open(dump_path, 'wb') as handle: handle.write(decompressed) self.log('info', "Flash object dumped at {0}".format(dump_path)) # Directly open a session on the dumped Flash object. __sessions__.new(dump_path)
def security(self): def get_certificate(pe): # TODO: this only extract the raw list of certificate data. # I need to parse them, extract single certificates and perhaps return # the PEM data of the first certificate only. pe_security_dir = pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY'] address = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pe_security_dir].VirtualAddress # size = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pe_security_dir].Size if address: return pe.write()[address + 8:] else: return None def get_signed_samples(current=None, cert_filter=None): db = Database() samples = db.find(key='all') results = [] for sample in samples: # Skip if it's the same file. if current: if sample.sha256 == current: continue # Obtain path to the binary. sample_path = get_sample_path(sample.sha256) if not os.path.exists(sample_path): continue # Open PE instance. try: cur_pe = pefile.PE(sample_path) except: continue cur_cert_data = get_certificate(cur_pe) if not cur_cert_data: continue cur_cert_md5 = get_md5(cur_cert_data) if cert_filter: if cur_cert_md5 == cert_filter: results.append([sample.name, sample.md5]) else: results.append([sample.name, sample.md5, cur_cert_md5]) return results if self.args.all: self.log('info', "Scanning the repository for all signed samples...") all_of_them = get_signed_samples() self.log('info', "{0} signed samples found".format(bold(len(all_of_them)))) if len(all_of_them) > 0: self.log('table', dict(header=['Name', 'MD5', 'Cert MD5'], rows=all_of_them)) return if not self.__check_session(): return cert_data = get_certificate(self.pe) if not cert_data: self.log('warning', "No certificate found") return cert_md5 = get_md5(cert_data) self.log('info', "Found certificate with MD5 {0}".format(bold(cert_md5))) if self.args.dump: cert_path = os.path.join(self.args.dump, '{0}.crt'.format(__sessions__.current.file.sha256)) with open(cert_path, 'wb+') as cert_handle: cert_handle.write(cert_data) self.log('info', "Dumped certificate to {0}".format(cert_path)) self.log('info', "You can parse it using the following command:\n\t" + bold("openssl pkcs7 -inform DER -print_certs -text -in {0}".format(cert_path))) # TODO: do scan for certificate's serial number. if self.args.scan: self.log('info', "Scanning the repository for matching signed samples...") matches = get_signed_samples(current=__sessions__.current.file.sha256, cert_filter=cert_md5) self.log('info', "{0} relevant matches found".format(bold(len(matches)))) if len(matches) > 0: self.log('table', dict(header=['Name', 'SHA256'], rows=matches)) # TODO: this function needs to be better integrated with the rest of the command. # TODO: need to add more error handling and figure out why so many samples are failing. if self.args.check: if not HAVE_VERIFYSIGS: self.log('error', "Dependencies missing for authenticode validation. Please install M2Crypto and pyasn1 (`pip install pyasn1 M2Crypto`)") return try: auth, computed_content_hash = get_auth_data(__sessions__.current.file.path) except Exception as e: self.log('error', "Unable to parse PE certificate: {0}".format(str(e))) return try: auth.ValidateAsn1() auth.ValidateHashes(computed_content_hash) auth.ValidateSignatures() auth.ValidateCertChains(time.gmtime()) except Exception, e: self.log('error', "Unable to validate PE certificate: {0}".format(str(e))) return self.log('info', bold('Signature metadata:')) self.log('info', 'Program name: {0}'.format(auth.program_name)) self.log('info', 'URL: {0}'.format(auth.program_url)) if auth.has_countersignature: self.log('info', bold('Countersignature is present. Timestamp: {0} UTC'.format( time.asctime(time.gmtime(auth.counter_timestamp))))) else: self.log('info', bold('Countersignature is not present.')) self.log('info', bold('Binary is signed with cert issued by:')) self.log('info', '{0}'.format(auth.signing_cert_id[0])) self.log('info', '{0}'.format(auth.cert_chain_head[2][0])) self.log('info', 'Chain not before: {0} UTC'.format( time.asctime(time.gmtime(auth.cert_chain_head[0])))) self.log('info', 'Chain not after: {0} UTC'.format( time.asctime(time.gmtime(auth.cert_chain_head[1])))) if auth.has_countersignature: self.log('info', bold('Countersig chain head issued by:')) self.log('info', '{0}'.format(auth.counter_chain_head[2])) self.log('info', 'Countersig not before: {0} UTC'.format( time.asctime(time.gmtime(auth.counter_chain_head[0])))) self.log('info', 'Countersig not after: {0} UTC'.format( time.asctime(time.gmtime(auth.counter_chain_head[1])))) self.log('info', bold('Certificates:')) for (issuer, serial), cert in auth.certificates.items(): self.log('info', 'Issuer: {0}'.format(issuer)) self.log('info', 'Serial: {0}'.format(serial)) subject = cert[0][0]['subject'] subject_dn = str(dn.DistinguishedName.TraverseRdn(subject[0])) self.log('info', 'Subject: {0}'.format(subject_dn)) not_before = cert[0][0]['validity']['notBefore'] not_after = cert[0][0]['validity']['notAfter'] not_before_time = not_before.ToPythonEpochTime() not_after_time = not_after.ToPythonEpochTime() self.log('info', 'Not Before: {0} UTC ({1})'.format( time.asctime(time.gmtime(not_before_time)), not_before[0])) self.log('info', 'Not After: {0} UTC ({1})'.format( time.asctime(time.gmtime(not_after_time)), not_after[0])) if auth.trailing_data: self.log('info', 'Signature Blob had trailing (unvalidated) data ({0} bytes): {1}'.format( len(auth.trailing_data), auth.trailing_data.encode('hex')))
def security(self): def usage(): print("usage: pe security [-d=folder] [-s]") def help(): usage() print("") print("Options:") print("\t--help (-h)\tShow this help message") print("\t--dump (-d)\tDestination directory to store digital signature in") print("\t--scan (-s)\tScan the repository for common certificates") print("") def get_certificate(pe): # TODO: this only extract the raw list of certificate data. # I need to parse them, extract single certificates and perhaps return # the PEM data of the first certificate only. pe_security_dir = pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY'] address = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pe_security_dir].VirtualAddress size = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pe_security_dir].Size if address: return pe.write()[address+8:] else: return None try: opts, argv = getopt.getopt(self.args[1:], 'hd:s', ['help', 'dump=', 'scan']) except getopt.GetoptError as e: print(e) usage() return arg_folder = None arg_scan = False for opt, value in opts: if opt in ('-h', '--help'): help() return elif opt in ('-d', '--dump'): arg_folder = value elif opt in ('-s', '--scan'): arg_scan = True if not self.__check_session(): return cert_data = get_certificate(self.pe) if not cert_data: print_warning("No certificate found") return cert_md5 = get_md5(cert_data) print_info("Found certificate with MD5 {0}".format(bold(cert_md5))) if arg_folder: cert_path = os.path.join(arg_folder, '{0}.crt'.format(__sessions__.current.file.sha256)) with open(cert_path, 'wb+') as cert_handle: cert_handle.write(cert_data) print_info("Dumped certificate to {0}".format(cert_path)) print_info("You can parse it using the following command:\n\t" + bold("openssl pkcs7 -inform DER -print_certs -text -in {0}".format(cert_path))) # TODO: do scan for certificate's serial number. if arg_scan: print_info("Scanning the repository for matching samples...") db = Database() samples = db.find(key='all') matches = [] for sample in samples: # Skip if it's the same file. if sample.sha256 == __sessions__.current.file.sha256: continue # Obtain path to the binary. sample_path = get_sample_path(sample.sha256) if not os.path.exists(sample_path): continue # Open PE instance. try: cur_pe = pefile.PE(sample_path) except: continue cur_cert_data = get_certificate(cur_pe) if not cur_cert_data: continue cur_cert_md5 = get_md5(cur_cert_data) if cur_cert_md5 == cert_md5: matches.append([sample.name, sample.sha256]) print_info("{0} relevant matches found".format(bold(len(matches)))) if len(matches) > 0: print(table(header=['Name', 'SHA256'], rows=matches))
def security(self): def get_certificate(pe): # TODO: this only extract the raw list of certificate data. # I need to parse them, extract single certificates and perhaps return # the PEM data of the first certificate only. pe_security_dir = pefile.DIRECTORY_ENTRY[ 'IMAGE_DIRECTORY_ENTRY_SECURITY'] address = pe.OPTIONAL_HEADER.DATA_DIRECTORY[ pe_security_dir].VirtualAddress # size = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pe_security_dir].Size if address: return pe.write()[address + 8:] else: return None def get_signed_samples(current=None, cert_filter=None): db = Database() samples = db.find(key='all') results = [] for sample in samples: # Skip if it's the same file. if current: if sample.sha256 == current: continue # Obtain path to the binary. sample_path = get_sample_path(sample.sha256) if not os.path.exists(sample_path): continue # Open PE instance. try: cur_pe = pefile.PE(sample_path) except: continue cur_cert_data = get_certificate(cur_pe) if not cur_cert_data: continue cur_cert_md5 = get_md5(cur_cert_data) if cert_filter: if cur_cert_md5 == cert_filter: results.append([sample.name, sample.md5]) else: results.append([sample.name, sample.md5, cur_cert_md5]) return results if self.args.all: self.log('info', "Scanning the repository for all signed samples...") all_of_them = get_signed_samples() self.log('info', "{0} signed samples found".format(bold(len(all_of_them)))) if len(all_of_them) > 0: self.log( 'table', dict(header=['Name', 'MD5', 'Cert MD5'], rows=all_of_them)) return if not self.__check_session(): return cert_data = get_certificate(self.pe) if not cert_data: self.log('warning', "No certificate found") return cert_md5 = get_md5(cert_data) self.log('info', "Found certificate with MD5 {0}".format(bold(cert_md5))) if self.args.dump: cert_path = os.path.join( self.args.dump, '{0}.crt'.format(__sessions__.current.file.sha256)) with open(cert_path, 'wb+') as cert_handle: cert_handle.write(cert_data) self.log('info', "Dumped certificate to {0}".format(cert_path)) self.log( 'info', "You can parse it using the following command:\n\t" + bold("openssl pkcs7 -inform DER -print_certs -text -in {0}". format(cert_path))) # TODO: do scan for certificate's serial number. if self.args.scan: self.log('info', "Scanning the repository for matching signed samples...") matches = get_signed_samples( current=__sessions__.current.file.sha256, cert_filter=cert_md5) self.log('info', "{0} relevant matches found".format(bold(len(matches)))) if len(matches) > 0: self.log('table', dict(header=['Name', 'SHA256'], rows=matches)) # TODO: this function needs to be better integrated with the rest of the command. # TODO: need to add more error handling and figure out why so many samples are failing. if self.args.check: if not HAVE_VERIFYSIGS: self.log( 'error', "Dependencies missing for authenticode validation. Please install M2Crypto and pyasn1 (`pip install pyasn1 M2Crypto`)" ) return try: auth, computed_content_hash = get_auth_data( __sessions__.current.file.path) except Exception as e: self.log('error', "Unable to parse PE certificate: {0}".format(str(e))) return try: auth.ValidateAsn1() auth.ValidateHashes(computed_content_hash) auth.ValidateSignatures() auth.ValidateCertChains(time.gmtime()) except Exception as e: self.log( 'error', "Unable to validate PE certificate: {0}".format(str(e))) return self.log('info', bold('Signature metadata:')) self.log('info', 'Program name: {0}'.format(auth.program_name)) self.log('info', 'URL: {0}'.format(auth.program_url)) if auth.has_countersignature: self.log( 'info', bold('Countersignature is present. Timestamp: {0} UTC'. format( time.asctime(time.gmtime( auth.counter_timestamp))))) else: self.log('info', bold('Countersignature is not present.')) self.log('info', bold('Binary is signed with cert issued by:')) self.log('info', '{0}'.format(auth.signing_cert_id[0])) self.log('info', '{0}'.format(auth.cert_chain_head[2][0])) self.log( 'info', 'Chain not before: {0} UTC'.format( time.asctime(time.gmtime(auth.cert_chain_head[0])))) self.log( 'info', 'Chain not after: {0} UTC'.format( time.asctime(time.gmtime(auth.cert_chain_head[1])))) if auth.has_countersignature: self.log('info', bold('Countersig chain head issued by:')) self.log('info', '{0}'.format(auth.counter_chain_head[2])) self.log( 'info', 'Countersig not before: {0} UTC'.format( time.asctime(time.gmtime(auth.counter_chain_head[0])))) self.log( 'info', 'Countersig not after: {0} UTC'.format( time.asctime(time.gmtime(auth.counter_chain_head[1])))) self.log('info', bold('Certificates:')) for (issuer, serial), cert in auth.certificates.items(): self.log('info', 'Issuer: {0}'.format(issuer)) self.log('info', 'Serial: {0}'.format(serial)) subject = cert[0][0]['subject'] subject_dn = str(dn.DistinguishedName.TraverseRdn(subject[0])) self.log('info', 'Subject: {0}'.format(subject_dn)) not_before = cert[0][0]['validity']['notBefore'] not_after = cert[0][0]['validity']['notAfter'] not_before_time = not_before.ToPythonEpochTime() not_after_time = not_after.ToPythonEpochTime() self.log( 'info', 'Not Before: {0} UTC ({1})'.format( time.asctime(time.gmtime(not_before_time)), not_before[0])) self.log( 'info', 'Not After: {0} UTC ({1})'.format( time.asctime(time.gmtime(not_after_time)), not_after[0])) if auth.trailing_data: self.log( 'info', 'Signature Blob had trailing (unvalidated) data ({0} bytes): {1}' .format(len(auth.trailing_data), auth.trailing_data.encode('hex')))
def decompress(self, dump_dir): # Check if the file type is right. # TODO: this might be a bit hacky, need to verify whether malformed # Flash exploit would get a different file type. if 'Flash' not in __sessions__.current.file.type: self.log('error', "The opened file doesn't appear to be a valid SWF object") return # Retrieve key information from the opened SWF file. header, version, size, data = self.parse_swf() # Decompressed data. decompressed = None # Check if the file is already a decompressed Flash object. if header == 'FWS': self.log('info', "The opened file doesn't appear to be compressed") return # Check if the file is compressed with zlib. elif header == 'CWS': self.log('info', "The opened file appears to be compressed with Zlib") # Open an handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress and reconstruct the Flash object. decompressed = 'FWS' + compressed.read(5) + zlib.decompress(compressed.read()) # Check if the file is compressed with lzma. elif header == 'ZWS': self.log('info', "The opened file appears to be compressed with Lzma") # We need an third party library to decompress this. if not HAVE_PYLZMA: self.log('error', "Missing dependency, please install pylzma (`pip install pylzma`)") return # Open and handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress with pylzma and reconstruct the Flash object. decompressed = 'FWS' + compressed.read(5) + pylzma.decompress(compressed.read()) # If we obtained some decompressed data, we print it and eventually # dump it to file. if decompressed: # Print the decompressed data # TODO: this prints too much, need to find a better wayto display # this. Paginate? self.log('', cyan(hexdump(decompressed))) if dump_dir: # Dump the decompressed SWF file to the specified directory # or to the default temporary one. dump_path = os.path.join(dump_dir, '{0}.swf'.format(get_md5(decompressed))) with open(dump_path, 'wb') as handle: handle.write(decompressed) self.log('info', "Flash object dumped at {0}".format(dump_path)) # Directly open a session on the dumped Flash object. __sessions__.new(dump_path)
def security(self): def usage(): print("usage: pe security [-d=folder] [-s]") def help(): usage() print("") print("Options:") print("\t--help (-h)\tShow this help message") print( "\t--dump (-d)\tDestination directory to store digital signature in" ) print("\t--all (-a)\tFind all samples with a digital signature") print("\t--scan (-s)\tScan the repository for common certificates") print("") def get_certificate(pe): # TODO: this only extract the raw list of certificate data. # I need to parse them, extract single certificates and perhaps return # the PEM data of the first certificate only. pe_security_dir = pefile.DIRECTORY_ENTRY[ 'IMAGE_DIRECTORY_ENTRY_SECURITY'] address = pe.OPTIONAL_HEADER.DATA_DIRECTORY[ pe_security_dir].VirtualAddress size = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pe_security_dir].Size if address: return pe.write()[address + 8:] else: return None def get_signed_samples(current=None, cert_filter=None): db = Database() samples = db.find(key='all') results = [] for sample in samples: # Skip if it's the same file. if current: if sample.sha256 == current: continue # Obtain path to the binary. sample_path = get_sample_path(sample.sha256) if not os.path.exists(sample_path): continue # Open PE instance. try: cur_pe = pefile.PE(sample_path) except: continue cur_cert_data = get_certificate(cur_pe) if not cur_cert_data: continue cur_cert_md5 = get_md5(cur_cert_data) if cert_filter: if cur_cert_md5 == cert_filter: results.append([sample.name, sample.md5]) else: results.append([sample.name, sample.md5, cur_cert_md5]) return results try: opts, argv = getopt.getopt(self.args[1:], 'hd:as', ['help', 'dump=', 'all', 'scan']) except getopt.GetoptError as e: print(e) usage() return arg_folder = None arg_all = False arg_scan = False for opt, value in opts: if opt in ('-h', '--help'): help() return elif opt in ('-d', '--dump'): arg_folder = value elif opt in ('-a', '--all'): arg_all = True elif opt in ('-s', '--scan'): arg_scan = True if arg_all: print_info("Scanning the repository for all signed samples...") all_of_them = get_signed_samples() print_info("{0} signed samples found".format(bold( len(all_of_them)))) if len(all_of_them) > 0: print( table(header=['Name', 'MD5', 'Cert MD5'], rows=all_of_them)) return if not self.__check_session(): return cert_data = get_certificate(self.pe) if not cert_data: print_warning("No certificate found") return cert_md5 = get_md5(cert_data) print_info("Found certificate with MD5 {0}".format(bold(cert_md5))) if arg_folder: cert_path = os.path.join( arg_folder, '{0}.crt'.format(__sessions__.current.file.sha256)) with open(cert_path, 'wb+') as cert_handle: cert_handle.write(cert_data) print_info("Dumped certificate to {0}".format(cert_path)) print_info( "You can parse it using the following command:\n\t" + bold("openssl pkcs7 -inform DER -print_certs -text -in {0}". format(cert_path))) # TODO: do scan for certificate's serial number. if arg_scan: print_info( "Scanning the repository for matching signed samples...") matches = get_signed_samples( current=__sessions__.current.file.sha256, cert_filter=cert_md5) print_info("{0} relevant matches found".format(bold(len(matches)))) if len(matches) > 0: print(table(header=['Name', 'SHA256'], rows=matches))
def decompress(self): def usage(): print("usage: swf decompress [-d=folder]") def help(): usage() print("") print("Options:") print("\t--help (-h)\tShow this help message") print("\t--dump (-d)\tDump the SWF object to the destination folder (default is /tmp)") print("") try: opts, argv = getopt.getopt(self.args[1:], 'hd', ['help', 'dump']) except getopt.GetoptError as e: print(e) usage() return arg_dump = None for opt, value in opts: if opt in ('-h', '--help'): help() return elif opt in ('-d', '--dump'): if value: arg_dump = value else: arg_dump = tempfile.gettempdir() if not __sessions__.is_set(): print_error("No session opened") return # Check if the file type is right. # TODO: this might be a bit hacky, need to verify whether malformed # Flash exploit would get a different file type. if not 'Flash' in __sessions__.current.file.type: print_error("The opened file doesn't appear to be a valid SWF object") return # Retrieve key information from the opened SWF file. header, version, size, data = self.parse_swf() # Decompressed data. decompressed = None # Check if the file is already a decompressed Flash object. if header == 'FWS': print_info("The opened file doesn't appear to be compressed") return # Check if the file is compressed with zlib. elif header == 'CWS': print_info("The opened file appears to be compressed with Zlib") # Open an handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress and reconstruct the Flash object. decompressed = 'FWS' + compressed.read(5) + zlib.decompress(compressed.read()) # Check if the file is compressed with lzma. elif header == 'ZWS': print_info("The opened file appears to be compressed with Lzma") # We need an third party library to decompress this. if not HAVE_PYLZMA: print_error("Missing dependency, please install pylzma (`pip install pylzma`)") return # Open and handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress with pylzma and reconstruct the Flash object. decompressed = 'FWS' + compressed.read(5) + pylzma.decompress(compressed.read()) # If we obtained some decompressed data, we print it and eventually # dump it to file. if decompressed: # Print the decompressed data # TODO: this prints too much, need to find a better wayto display # this. Paginate? print(cyan(hexdump(decompressed))) if arg_dump: # Dump the decompressed SWF file to the specified directory # or to the default temporary one. dump_path = os.path.join(arg_dump, '{0}.swf'.format(get_md5(decompressed))) with open(dump_path, 'wb') as handle: handle.write(decompressed) print_info("Flash object dumped at {0}".format(dump_path)) # Directly open a session on the dumped Flash object. __sessions__.new(dump_path)
def decompress(self): def usage(): print("usage: swf decompress [-d=folder]") def help(): usage() print("") print("Options:") print("\t--help (-h)\tShow this help message") print( "\t--dump (-d)\tDump the SWF object to the destination folder (default is /tmp)" ) print("") try: opts, argv = getopt.getopt(self.args[1:], 'hd', ['help', 'dump']) except getopt.GetoptError as e: print(e) usage() return arg_dump = None for opt, value in opts: if opt in ('-h', '--help'): help() return elif opt in ('-d', '--dump'): if value: arg_dump = value else: arg_dump = tempfile.gettempdir() if not __sessions__.is_set(): print_error("No session opened") return # Check if the file type is right. # TODO: this might be a bit hacky, need to verify whether malformed # Flash exploit would get a different file type. if not 'Flash' in __sessions__.current.file.type: print_error( "The opened file doesn't appear to be a valid SWF object") return # Retrieve key information from the opened SWF file. header, version, size, data = self.parse_swf() # Decompressed data. decompressed = None # Check if the file is already a decompressed Flash object. if header == 'FWS': print_info("The opened file doesn't appear to be compressed") return # Check if the file is compressed with zlib. elif header == 'CWS': print_info("The opened file appears to be compressed with Zlib") # Open an handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress and reconstruct the Flash object. decompressed = 'FWS' + compressed.read(5) + zlib.decompress( compressed.read()) # Check if the file is compressed with lzma. elif header == 'ZWS': print_info("The opened file appears to be compressed with Lzma") # We need an third party library to decompress this. if not HAVE_PYLZMA: print_error( "Missing dependency, please install pylzma (`pip install pylzma`)" ) return # Open and handle on the compressed data. compressed = StringIO(data) # Skip the header. compressed.read(3) # Decompress with pylzma and reconstruct the Flash object. decompressed = 'FWS' + compressed.read(5) + pylzma.decompress( compressed.read()) # If we obtained some decompressed data, we print it and eventually # dump it to file. if decompressed: # Print the decompressed data # TODO: this prints too much, need to find a better wayto display # this. Paginate? print(cyan(hexdump(decompressed))) if arg_dump: # Dump the decompressed SWF file to the specified directory # or to the default temporary one. dump_path = os.path.join( arg_dump, '{0}.swf'.format(get_md5(decompressed))) with open(dump_path, 'wb') as handle: handle.write(decompressed) print_info("Flash object dumped at {0}".format(dump_path)) # Directly open a session on the dumped Flash object. __sessions__.new(dump_path)
def decompress(self, dump_dir): # Check if the file type is right. # TODO: this might be a bit hacky, need to verify whether malformed # Flash exploit would get a different file type. if 'Flash' not in __sessions__.current.file.type: self.log('error', "The opened file doesn't appear to be a valid SWF object") return # Retrieve key information from the opened SWF file. header, version, size, data = self.parse_swf() # Decompressed data. decompressed = None compressed = True # Check if the file is already a decompressed Flash object. if header == b'FWS': self.log('info', "The opened file doesn't appear to be compressed") decompressed = data compressed = False # Check if the file is compressed with zlib. elif header == b'CWS': self.log('info', "The opened file appears to be compressed with Zlib") # Open an handle on the compressed data. compressed = BytesIO(data) # Skip the header. compressed.read(3) # Decompress and reconstruct the Flash object. decompressed = b'FWS' + compressed.read(5) + zlib.decompress(compressed.read()) # Check if the file is compressed with lzma. elif header == b'ZWS': self.log('info', "The opened file appears to be compressed with Lzma") # We need an third party library to decompress this. if not HAVE_PYLZMA: self.log('error', "Missing dependency, please install pylzma (`pip install pylzma`)") return # Open and handle on the compressed data. compressed = BytesIO(data) # Skip the header. compressed.read(3) # Decompress with pylzma and reconstruct the Flash object. # # ZWS(LZMA) # # | 4 bytes | 4 bytes | 4 bytes | 5 bytes | n bytes | 6 bytes | # # | 'ZWS'+version | scriptLen | compressedLen | LZMA props | LZMA data | LZMA end marker | decompressed = b'FWS' + compressed.read(5) compressed.read(4) # skip compressedLen decompressed += pylzma.decompress(compressed.read()) # If we obtained some decompressed data, we print it and eventually # dump it to file. if decompressed: # Print the decompressed data # TODO: this prints too much, need to find a better wayto display # this. Paginate? self.log('', cyan(hexdump(decompressed))) if compressed and dump_dir: # Dump the decompressed SWF file to the specified directory # or to the default temporary one. dump_path = os.path.join(dump_dir, '{0}.swf'.format(get_md5(decompressed))) with open(dump_path, 'wb') as handle: handle.write(decompressed) self.log('info', "Flash object dumped at {0}".format(dump_path)) # Set the parent-child relation between CWS-FWS this_parent = __sessions__.current.file.sha256 # Directly open a session on the dumped Flash object. __sessions__.new(dump_path) db = Database() # Make sure parents is in database if not db.find(key='sha256', value=this_parent): self.log('error', "the parent file is not found in the database. ") else: db.add_parent(__sessions__.current.file.sha256, this_parent)