def heuristic_by_process_sids(memory_instance, pslist=None, workdir=None, dump_objects=False): """ Dump suspicious processes, according to running user :param memory_instance: an instance of memory object :param pslist: list of processes obtained from get_new_pslist :param workdir: path to the workdir :param dump_objects: wether to dump suspicious results or not :return: dictionary of suspect code injection sections inside processes """ process_whitelist = ['System', 'msiexec.exe', 'VMwareService.e', 'spoolsv.exe', 'svchost.exe', 'vmacthlp.exe', 'services.exe', 'winlogon.exe', 'csrss.exe', 'smss.exe', 'lsass.exe','vmtoolsd.exe'] suspicious_processes = list() # Get process list if pslist is None: pslist = get_new_pslist(memory_instance) if workdir is None: workdir = create_workdir() # "columns": ["PID", "Process", "SID", "Name"]} output = execute_volatility_command(memory_instance, 'getsids') for priv in output: if priv['SID'] == 'S-1-5-18' and priv['Process'] not in process_whitelist: logging.info('Suspicious priv: {}'.format(priv)) suspicious_processes.append(priv) if dump_objects: logging.info('Dumping {} due to suspicious SID'.format(priv['PID'])) dump_process(memory_instance, priv['PID'], workdir, process_name=priv['Process'], memdump=True) return suspicious_processes
def heuristics_process_privileges(memory_instance, pslist=None, workdir=None, dump_objects=False): """ Find suspicious processes according to process privileges :param memory_instance: memory instance object :param pslist: list of loaded processes created by get_new_pslist() :param workdir: path to working directory :param dump_objects: wether to dump suspicious object or not :return: dictionary of suspect processes """ suspicious_privileges = ['SeDebugPrivilege', 'SeTcbPrivilege', 'SeTrustedCredManAccessPrivilege'] privs_list = execute_volatility_command(memory_instance, 'privs') procs_with_suspicious_privs = list() dumped_process_list = list() for privilege in privs_list: if privilege['Privilege'] in suspicious_privileges: attributes_list = privilege['Attributes'].split(',') if 'Present' in attributes_list and 'Enabled' in attributes_list and 'Default' not in attributes_list: print(json.dumps(privilege)) procs_with_suspicious_privs.append(privilege) if dump_objects and privilege['Pid'] not in dumped_process_list: logging.info('Dumping {} due to suspicious privileges'.format(privilege['Pid'])) dump_process(memory_instance, privilege['Pid'], workdir, process_name=privilege['Process']) dumped_process_list.append(privilege['Pid']) return procs_with_suspicious_privs
def heuristic_ssdt(memory_instance, pslist=None, workdir=None, dump_objects=False): ssdt = execute_volatility_command(memory_instance, 'ssdt') legitimate_owners = ['ntoskrnl.exe', 'win32k.sys'] known_owners = list() for entry in ssdt: if entry['Owner'] not in legitimate_owners and entry['Owner'] not in known_owners: print('New ownwer: {}'.format(entry)) known_owners.append(entry['Owner']) for driver_name in known_owners: # /usr/local/bin/vol.py --profile WinXPSP2x86 -f "/home/MemoryDumps/APT.img" moddump -r irykmmww.sys -D /tmp execute_volatility_command(memory_instance,'moddump',extra_flags='-r {} -D {}'.format(driver_name,workdir)) return known_owners
def get_new_pslist(memory_instance): """ Get output of Volatility pslist command :param object_instance: Reference to machine instance :return: list of currently running processes """ return execute_volatility_command(memory_instance, 'pslist')
def run_extractor(memory_instance, malware_sample, machine_instance=None): golden_image = pslist.load_golden_image(machine_instance) new_pslist = pslist.get_new_pslist(memory_instance) new_processes = [] for proc in new_pslist: new_proc = True for proc_gi in golden_image: if proc['PID'] == proc_gi['PID']: new_proc = False break # TODO! Local patch!! Remove on production!! if proc['Name'] == 'wmiprvse.exe': new_proc = False break if new_proc: logging.info('Identified a new process: {} - {}'.format(proc['PID'], proc['Name'])) new_processes.append(proc) workdir = os.path.dirname(os.path.realpath(malware_sample.file_path)) db_connection = DataBaseConnection() for procdata in new_processes: output = execute_volatility_command(memory_instance, 'procdump', extra_flags='-p {} -D {}/'.format(procdata['PID'], workdir), has_json_output=False) # Rename the file, to contain process name src = workdir + "/executable." + str(procdata['PID']) + ".exe" if os.path.isfile(src): target_dump_path = workdir + "/" + procdata['Name'] + "." + str(procdata['PID']) + "._exe" os.rename(src, target_dump_path) current_dump = SampleDump(target_dump_path) current_dump.sha256 = calc_sha256(target_dump_path) current_dump.md5 = calc_md5(target_dump_path) current_dump.ephash = calc_ephash(target_dump_path) current_dump.imphash = calc_imphash(target_dump_path) current_dump.process_name = procdata['Name'] current_dump.source = 'procdump' current_dump.parent_sample_id = malware_sample.id db_connection.add_dump(current_dump) # Load post processing modules here, if needed with open(target_dump_path + '.strings.json', 'w') as strings_output_file: strings_output_file.write(json.dumps(get_strings(current_dump), indent=4)) with open(target_dump_path + '.static_analysis.json', 'w') as strings_output_file: strings_output_file.write(json.dumps(static_analysis(current_dump), indent=4)) with open(target_dump_path + '.yara.json', 'w') as yara_output_file: yara_output_file.write(json.dumps(scan_with_yara(current_dump), indent=4)) else: logging.info('Could not dump process {} (PID: {})'.format(procdata['Name'], str(procdata['PID'])))
def heuristic_libraries_by_path(memory_instance, pslist=None, workdir=None, dump_objects=False): """ Heuristics by path, using statistics and dlllist :param memory_instance: memory instance object :param pslist: list of loaded processes created by get_new_pslist() :param workdir: path to working directory :param dump_objects: wether to dump suspicious object or not :return: dictionary of suspect processes """ loaded_dlls = execute_volatility_command(memory_instance, 'dlllist') statistic_dict = dict() suspicious_dlls = list() max_files_threshold = 2 statistic_anomalies_list = ['\\systemroot\\system32\\smss.exe', 'c:\\windows\\explorer.exe', 'c:\\program files\\internet explorer\\ieproxy.dll'] for loaded_dll in loaded_dlls: # loaded_dll['Path'].lower() folder_path = '\\'.join(loaded_dll['Path'].lower().split('\\')[0:-1]) try: statistic_dict[folder_path] += 1 except KeyError: statistic_dict[folder_path] = 1 suspect_path_list = list() sorted_dict = sorted(statistic_dict.items(), key=lambda x: x[1], reverse=True) for path in sorted_dict: if path[1] < max_files_threshold: print(path) suspect_path_list.append(path[0]) for loaded_dll in loaded_dlls: for suspect_path in suspect_path_list: if '\\'.join(loaded_dll['Path'].lower().split('\\')[0:-1]) == suspect_path.lower(): if loaded_dll['Path'].lower() not in statistic_anomalies_list: suspicious_dlls.append(loaded_dll) if dump_objects: logging.info('Going to dump {} due to suspicious path'.format(loaded_dll)) dump_dll(memory_instance, loaded_dll['Pid'], loaded_dll['Base'], workdir) return suspicious_dlls
def heuristic_dest_port_anomallies(memory_instance, pslist=None, workdir=None, dump_objects=False): whitelisted_dest_ports = ['80', '443', '8443', '53', '3889'] suspicious_processes = list() connections = execute_volatility_command(memory_instance, 'connections') for conn in connections: dst_ip, dst_port = conn['RemoteAddress'].split(':') if dst_port not in whitelisted_dest_ports: suspicious_processes.append(conn) if dump_objects: procname = 'unknown' for process in pslist: if str(process['Offset(V)']) == str(conn['Offset(V)']): logging.info("Found process name: {}".format(process['Name'])) procname = process['Name'] pid = str(process['PID']) break dump_process(memory_instance, conn['PID'], workdir, process_name=procname) return suspicious_processes
def heuristic_dll_uncommon_on_machine(memory_instance, pslist=None, workdir=None, dump_objects=False): loaded_dlls = execute_volatility_command(memory_instance, 'dlllist') suspect_path_list = list() loaded_dlls_counter = dict() for loaded_dll in loaded_dlls: try: loaded_dlls_counter[loaded_dll['Path']]['counter'] += 1 except KeyError: loaded_dlls_counter[loaded_dll['Path']] = {'counter': 0, 'first_seen': loaded_dll} for key in loaded_dlls_counter: if loaded_dlls_counter[key]['first_seen']['Path'] not in DLLS_IN_SYSDIR: if loaded_dlls_counter[key]['counter'] == 1 and loaded_dlls_counter[key]['first_seen']['LoadCount'] == 1: print('Going to dump: {}'.format(loaded_dlls_counter[key]['first_seen'])) if dump_objects: dump_dll(memory_instance, loaded_dlls_counter[key]['first_seen']['Pid'], loaded_dlls_counter[key]['first_seen']['Base'], workdir) suspect_path_list.append(loaded_dlls_counter[key]['first_seen']) return suspect_path_list
def run_extractor(memory_instance, malware_sample,machine_instance=None): pslist_new_data = pslist.get_new_pslist(memory_instance) target_dump_dir = os.path.join(get_workdir_path(malware_sample), 'injected') os.mkdir(target_dump_dir) output = execute_volatility_command(memory_instance, 'malfind', extra_flags='-D {}/'.format(target_dump_dir), has_json_output=False) db_connection = DataBaseConnection() # Find malfind injections that are binaries, and rename them for single_dump in os.scandir(target_dump_dir): splitted_line = re.split('\.', single_dump.path.rstrip('\n')) logging.info('offset: {}, Imagebase: {}'.format(splitted_line[1], splitted_line[2])) offset = splitted_line[1] imagebase = splitted_line[2] # Verify if it is PE or not try: pe = pefile.PE(single_dump.path) isPE = True except PEFormatError: isPE = False if isPE: db_connection.add_tag("Injects_Code", malware_sample) logging.info('[*] Processing {}'.format(single_dump.path)) logging.info('offset: %s, Imagebase: %s'.format(offset, imagebase)) logging.info('Altering image base: {} => {}'.format(pe.OPTIONAL_HEADER.ImageBase, imagebase)) fixed_pe = fix_pe_from_memory(pe, imagebase=imagebase) # Get original process name process_name = "unknown" for proc_gi in pslist_new_data: if str(hex(proc_gi['Offset(V)'])) == offset: logging.info("Found process name: {}".format(proc_gi['Name'])) process_name = proc_gi['Name'] pid = str(proc_gi['PID']) break outputpath = os.path.join(target_dump_dir, process_name + '.' + offset + '.' + imagebase + '.fixed_bin') fixed_pe.write(filename=outputpath) pe.close() if process_name != 'unknown': # Generate impscan IDC output = execute_volatility_command(memory_instance, 'impscan', extra_flags='-b {} -p {} --output=idc'.format(imagebase, pid), has_json_output=False) # Write IDC data to file with open(outputpath + '.idc', 'w') as idc: idc.write('#include <idc.idc>\n') idc.write('static main(void) {{\n') idc.write(output) idc.write('Exit(0);}}') current_dump = SampleDump(outputpath) current_dump.parent_sample_id = malware_sample.id current_dump.sha256 = calc_sha256(outputpath) current_dump.md5 = calc_md5(outputpath) current_dump.process_name = process_name current_dump.source = 'injected_code' # Calc imphash, or make the parameter = fail current_dump.ephash = calc_ephash(outputpath) # Calc EPhash, or make the parameter = fail current_dump.imphash = calc_imphash(outputpath) db_connection.add_dump(current_dump) # Load post processing modules here, if needed with open(outputpath + '.strings.json', 'w') as strings_output_file: strings_output_file.write(json.dumps(get_strings(current_dump, imagebase=imagebase), indent=4)) with open(outputpath + '.static_analysis.json', 'w') as static_analysis_output_file: static_analysis_output_file.write(json.dumps(static_analysis(current_dump), indent=4)) with open(outputpath + '.yara.json', 'w') as yara_output_file: yara_output_file.write(json.dumps(scan_with_yara(current_dump), indent=4)) with open(outputpath + '.ysa.json', 'w') as yara_semantic_output_file: yara_semantic_output_file.write(json.dumps(semantically_analyze(current_dump), indent=4))
def heuristic_suspicious_handles(memory_instance, pslist=None, workdir=None, dump_objects=False): """ Heuristics by suspicious handles :param memory_instance: memory instance object :param pslist: list of loaded processes created by get_new_pslist() :param workdir: path to working directory :param dump_objects: wether to dump suspicious object or not :return: dictionary of suspect processes """ handles_list = execute_volatility_command(memory_instance, 'handles', extra_flags='-s') supported_handles = ['Key', 'File', 'Mutant', 'Thread'] # Initiate a dict with scoring per PID... process_scoring = dict() for process in pslist: process_scoring[process['PID']] = {'Name': process['Name'], 'PID': process['PID'], 'PPID': process['PPID'], 'susp_keys': 0, 'susp_files': 0, 'susp_mutex': 0, 'susp_thread_handles': 0, 'Key': list(), 'File': list(), 'Mutant': list(), 'Thread': list()} # for each process, add its handles to his dict for handle in handles_list: if handle['Type'] in supported_handles: try: process_scoring[handle['Pid']][handle['Type']].append(handle) except KeyError: logging.info('PID does not exists ({})'.format(handle['Pid'])) # Get a dictionary of running processes by name, for easier iteration (from pslist) running_processes = dict() for running_process in pslist: running_processes[str(running_process['PID'])] = running_process['Name'] # Anomaly detection phase: # Find processes with handles to threads in other processes... for process_pid in process_scoring: for thread_handle in process_scoring[process_pid]['Thread']: # Get pid from regex: m = re.search(r'^TID (\d{2,4})\sPID\s(\d{2,4})$', thread_handle['Details']) if m: tid = m.group(1) pid = m.group(2) if pid != str(process_pid) and pid != str(process_scoring[process_pid]['PPID']) and \ process_scoring[process_pid]['Name'] != 'csrss.exe': try: if process_scoring[process_pid]['Name'] == 'services.exe' and running_processes[ pid] == 'lsass.exe': continue if process_scoring[process_pid]['Name'] == 'lsass.exe' and running_processes[ pid] == 'svchost.exe': continue logging.info( 'This process has an handle to a thread in another process: {}-{} ---> {} ({})'.format( process_scoring[process_pid]['Name'], process_pid, thread_handle['Details'], running_processes[pid])) except KeyError: logging.info('This process has an handle to a thread in another process: {}-{} ---> {}'.format( process_scoring[process_pid]['Name'], process_pid, thread_handle['Details'])) process_scoring[process_pid]['susp_thread_handles'] += 1 # Mutants (i.e statistically outstanding mutants) # TODO: import fuzzywuzzy, from fuzzywuzzy import fuzz, fuzz.ratio(a,b) # Files (i.e executables/DLLs from unusual paths) # Keys (i.e persistency keys...) # Create a final list of processes with more suspicious handles than the treshold: threshold = 0 suspect_processes = list() for process_pid in process_scoring: if (process_scoring[process_pid]['susp_keys'] + process_scoring[process_pid]['susp_files'] + process_scoring[process_pid]['susp_mutex'] + process_scoring[process_pid][ 'susp_thread_handles']) > threshold: suspect_processes.append({'pid': process_pid, 'name': process_scoring[process_pid]['Name'], 'susp_keys': process_scoring[process_pid]['susp_keys'], 'susp_files': process_scoring[process_pid]['susp_files'], 'susp_mutex': process_scoring[process_pid]['susp_mutex'], 'susp_thread_handles': process_scoring[process_pid]['susp_thread_handles']}) return suspect_processes
def heuristic_injected_code(memory_instance, pslist=None, workdir=None, dump_objects=False, delete_non_pe=False): """ Dump injected code :param memory_instance: an instance of memory object :param pslist: list of processes obtained from get_new_pslist :param workdir: path to the workdir :param dump_objects: wether to dump suspicious results or not :param delete_non_pe: delete non PE files or not, they could be shellcode injections :return: dictionary of suspect code injection sections inside processes """ # Get process list if pslist is None: pslist = get_new_pslist(memory_instance) if workdir is None: workdir = create_workdir() injected_dumps_list = list() if dump_objects: logging.info('Going to dump injected processes to {}'.format(workdir)) output = execute_volatility_command(memory_instance, 'malfind', extra_flags='-D {}/'.format(workdir), has_json_output=False) # Find malfind injections that are binaries, and rename them for single_dump in os.scandir(workdir): splitted_line = single_dump.path.strip().split('.') if len(splitted_line) == 4: offset = splitted_line[1] imagebase = splitted_line[2] try: pe = pefile.PE(single_dump.path) fixed_pe = fix_pe_from_memory(pe, imagebase=imagebase) # Get original process name procname = "unknown" for process in pslist: if str(process['Offset(V)']) == str(int(offset, 16)): logging.info("Found process name: {}".format(process['Name'])) procname = process['Name'] pid = str(process['PID']) break outputpath = os.path.join(workdir, procname + '.' + offset + '.' + imagebase + '.fixed_bin') logging.info('Dumping fixed PE to {}'.format(outputpath)) fixed_pe.write(filename=outputpath) pe.close() if procname != 'unknown': injected_dumps_list.append({'path': outputpath, 'process_name': procname, 'pid': pid}) else: injected_dumps_list.append(outputpath) current_dump = SampleDump(outputpath) with open(outputpath + '.strings.json', 'w') as strings_output_file: strings_output_file.write(json.dumps(get_strings(current_dump), indent=4)) with open(outputpath + '.static_analysis.json', 'w') as strings_output_file: strings_output_file.write(json.dumps(static_analysis(current_dump), indent=4)) except PEFormatError: logging.info('Corrupted, or not PE file...') if delete_non_pe: os.remove(single_dump) pass result = {'PE_dump_list': injected_dumps_list} else: logging.info('Not output workdir defined, not going to dump injected processes.') output = execute_volatility_command(memory_instance, 'malfind') result = {'malfind_output': output} return result
def run_extractor(memory_instance, malware_sample,machine_instance=None): if machine_instance is None: return None # Whitelist of modules by name, not optimal at all... mod_white_list = ['TDTCP.SYS', 'RDPWD.SYS', 'kmixer.sys', 'Bthidbus.sys', 'rdpdr.sys', 'tdtcp.sys', 'tssecsrv.sys'] # Get golden image data: with open(os.path.join(VOLATILITYBOT_HOME, 'GoldenImage', machine_instance.machine_name, 'modscan.json')) as data_file: modscan_golden_image = json.load(data_file) modscan_run = execute_volatility_command(memory_instance, 'modscan') db_connection = DataBaseConnection() new_modules = [] for mod in modscan_run: new_mod = True for mod_gi in modscan_golden_image: if mod['File'] == mod_gi['File']: if mod['Size'] == mod_gi['Size']: new_mod = False for wl_mod in mod_white_list: # print '[DEBUG] modscan %s : %s' % (mod['filename'],wl_mod) if (mod['Name'] == wl_mod): new_mod = False if new_mod: logging.info('Identified a new module: {} - {}'.format(mod['File'], mod['Size'])) new_modules.append(mod) output = execute_volatility_command(memory_instance, 'moddump', extra_flags='-b {} -D {}/'.format(mod['Base'], get_workdir_path(malware_sample)), has_json_output=False) base = mod['Base'] src = os.path.join(get_workdir_path(malware_sample), "driver." + base[2:] + ".sys") dest = os.path.join(get_workdir_path(malware_sample), mod['Name'] + '.' + base[2:] + '.sys') try: os.rename(src, dest) except Exception as e: logging.error('Could not rename driver, leaving it as it is... ({})'.format(e) ) dest = src current_dump = SampleDump(dest) current_dump.parent_sample_id = malware_sample.id current_dump.sha256 = calc_sha256(dest) current_dump.md5 = calc_md5(dest) current_dump.process_name = mod['Name'] current_dump.source = 'KMD' current_dump.ephash = calc_ephash(dest) current_dump.imphash = calc_imphash(dest) db_connection.add_dump(current_dump) with open(dest + '.strings.json', 'w') as strings_output_file: strings_output_file.write(json.dumps(get_strings(current_dump), indent=4)) with open(dest + '.yara.json', 'w') as yara_output_file: yara_output_file.write(json.dumps(scan_with_yara(current_dump), indent=4))
def create_golden_image(memory_instance): return execute_volatility_command(memory_instance, 'modscan')