def new(self, path=None, misp_event=None): if not path and not misp_event: print_error("You have to open a session on a path or on a misp event.") return session = Session() total = len(self.sessions) session.id = total + 1 if path: if self.is_set() and misp_event is None and self.current.misp_event: session.misp_event = self.current.misp_event # Open a section on the given file. session.file = File(path) # Try to lookup the file in the database. If it is already present # we get its database ID, file name, and tags. row = Database().find(key='sha256', value=session.file.sha256) if row: session.file.id = row[0].id session.file.name = row[0].name session.file.tags = ', '.join(tag.to_dict()['tag'] for tag in row[0].tag) if row[0].parent: session.file.parent = '{0} - {1}'.format(row[0].parent.name, row[0].parent.sha256) session.file.children = Database().get_children(row[0].id) print_info("Session opened on {0}".format(path)) if misp_event: if self.is_set() and path is None and self.current.file: session.file = self.current.file refresh = False if (self.current is not None and self.current.misp_event is not None and self.current.misp_event.event.id is not None and self.current.misp_event.event.id == misp_event.event.id): refresh = True session.misp_event = misp_event if refresh: print_info("Session on MISP event {0} refreshed.".format(misp_event.event.id)) elif not misp_event.event.id: print_info("Session opened on a new local MISP event.") else: print_info("Session opened on MISP event {0}.".format(misp_event.event.id)) if session.file: # Loop through all existing sessions and check whether there's another # session open on the same file and delete it. This is to avoid # duplicates in sessions. # NOTE: in the future we might want to remove this if sessions have # unique attributes (for example, an history just for each of them). for entry in self.sessions: if entry.file and entry.file.sha256 == session.file.sha256: self.sessions.remove(entry) # Add new session to the list. self.sessions.append(session) # Mark the new session as the current one. self.current = session
def store_sample(file_object): sha256 = file_object.sha256 if not sha256: print_error("No hash") return None folder = os.path.join( __project__.get_path(), 'binaries', sha256[0], sha256[1], sha256[2], sha256[3] ) if not os.path.exists(folder): os.makedirs(folder, 0o750) file_path = os.path.join(folder, sha256) if not os.path.exists(file_path): with open(file_path, 'wb') as stored: for chunk in file_object.get_chunks(): stored.write(chunk) else: print_warning("File exists already") return None return file_path
def add_note(self, sha256, title, body): session = self.Session() if sys.version_info < (3, 0): # on Python2 make sure to only handle ASCII try: title.decode('ascii') body.decode('ascii') except UnicodeError as err: raise Python2UnsupportedUnicode("Non ASCII character(s) in Notes not supported on Python2.\n" "Please use Python >= 3.4".format(err), "error") malware_entry = session.query(Malware).filter(Malware.sha256 == sha256).first() if not malware_entry: return try: new_note = Note(title, body) malware_entry.note.append(new_note) session.commit() self.added_ids.setdefault("note", []).append(new_note.id) except SQLAlchemyError as e: print_error("Unable to add note: {0}".format(e)) session.rollback() finally: session.close()
def logo(): print(""" _ (_) _ _ _ ____ _____ ____ | | | | | _ \| ___ |/ ___) \ V /| | |_| | ____| | \_/ |_| __/|_____)_| v{} |_| """.format(__version__)) db = Database() count = db.get_sample_count() try: db.find('all') except Exception: print_error("You need to update your Viper database. Run 'python update.py -d'") sys.exit() if __project__.name: name = __project__.name else: name = 'default' print(magenta("You have " + bold(count)) + magenta(" files in your " + bold(name)) + magenta(" repository"))
def add_malwarevt(self, sha256, scan_id, permalink, resource, verbose_msg, scan_date, positives, total): session = self.Session() if sys.version_info < (3, 0): # on Python2 make sure to only handle ASCII try: title.decode('ascii') body.decode('ascii') except UnicodeError as err: raise Python2UnsupportedUnicode( "Non ASCII character(s) in Notes not supported on Python2.\n" "Please use Python >= 3.4".format(err), "error") malware_entry = session.query(Malware).filter( Malware.sha256 == sha256).first() if not malware_entry: return False try: new_malwarevt = MalwareVT(scan_id, permalink, resource, verbose_msg, scan_date, positives, total) malware_entry.malwarevt.append(new_malwarevt) session.commit() self.added_ids.setdefault("malwarevt", []).append(new_malwarevt.id) except SQLAlchemyError as e: print_error("Unable to add virus total register: {0}".format(e)) session.rollback()
def add_malwareanalysismacros(self, sha256, type, keyword, description): session = self.Session() if sys.version_info < (3, 0): # on Python2 make sure to only handle ASCII try: title.decode('ascii') body.decode('ascii') except UnicodeError as err: raise Python2UnsupportedUnicode( "Non ASCII character(s) in Notes not supported on Python2.\n" "Please use Python >= 3.4".format(err), "error") malware_entry = session.query(Malware).filter( Malware.sha256 == sha256).first() if not malware_entry: return False try: new_malwareanalysismacros = MalwareAnalysisMacros( type, keyword, description) malware_entry.malwareanalysismacros.append( new_malwareanalysismacros) session.commit() self.added_ids.setdefault("malwareanalysismacros", []).append(new_malwareanalysismacros.id) except SQLAlchemyError as e: print_error("Unable to add malware analysis macros: {0}".format(e)) session.rollback()
def add_malwarevtsc(self, sha256, antivirus, detected, version, result, update): session = self.Session() if sys.version_info < (3, 0): # on Python2 make sure to only handle ASCII try: title.decode('ascii') body.decode('ascii') except UnicodeError as err: raise Python2UnsupportedUnicode( "Non ASCII character(s) in Notes not supported on Python2.\n" "Please use Python >= 3.4".format(err), "error") malware_entry = session.query(Malware).filter( Malware.sha256 == sha256).first() if not malware_entry: return False try: new_malwarevtsc = MalwareVTSc(antivirus, detected, version, result, update) malware_entry.malwarevtsc.append(new_malwarevtsc) session.commit() self.added_ids.setdefault("malwarevtsc", []).append(new_malwarevtsc.id) except SQLAlchemyError as e: print_error("Unable to add malware scan: {0}".format(e)) session.rollback()
def delete_tag(self, tag_name, sha256): session = self.Session() try: # First remove the tag from the sample malware_entry = session.query(Malware).filter(Malware.sha256 == sha256).first() tag = session.query(Tag).filter(Tag.tag == tag_name).first() try: malware_entry = session.query(Malware).filter(Malware.sha256 == sha256).first() malware_entry.tag.remove(tag) session.commit() except: print_error("Tag {0} does not exist for this sample".format(tag_name)) # If tag has no entries drop it count = len(self.find("tag", tag_name)) if count == 0: session.delete(tag) session.commit() print_warning("Tag {0} has no additional entries dropping from Database".format(tag_name)) except SQLAlchemyError as e: print_error("Unable to delete tag: {0}".format(e)) session.rollback() finally: session.close()
def download(url, tor=False): def create_connection(address, timeout=None, source_address=None): sock = socks.socksocket() sock.connect(address) return sock if tor: if not HAVE_SOCKS: print_error("Missing dependency, install socks (`pip install SocksiPy`)") return None socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 9050) socket.socket = socks.socksocket socket.create_connection = create_connection try: req = Request(url) req.add_header('User-agent', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)') res = urlopen(req) data = res.read() except HTTPError as e: print_error(e) except URLError as e: if tor and e.reason.errno == 111: print_error("Connection refused, maybe Tor is not running?") else: print_error(e) except Exception as e: print_error("Failed download: {0}".format(e)) else: return data
def remove_sample(file_path): if os.path.exists(file_path): os.remove(file_path) print("Deleted the file from storage: {0}".format(file_path)) else: print_error("File Doesnt exists: {0}".format(file_path)) return True
def is_attached_file(self, quiet=False): if not self.is_set(): if not quiet: print_error("No session opened") return False if not self.current.file: if not quiet: print_error("Not attached to a file") return False return True
def is_attached_misp(self, quiet=False): if not self.is_set(): if not quiet: print_error("No session opened") return False if not self.current.misp_event: if not quiet: print_error("Not attached to a MISP event") return False return True
def find(self, key, value=None, offset=0): session = self.Session() offset = int(offset) rows = None if key == 'all': rows = session.query(Malware).all() elif key == 'ssdeep': ssdeep_val = str(value) rows = session.query(Malware).filter(Malware.ssdeep.contains(ssdeep_val)).all() elif key == 'any': prefix_val = str(value) rows = session.query(Malware).filter(Malware.name.startswith(prefix_val) | Malware.md5.startswith(prefix_val) | Malware.sha1.startswith(prefix_val) | Malware.sha256.startswith(prefix_val) | Malware.type.contains(prefix_val) | Malware.mime.contains(prefix_val)).all() elif key == 'latest': if value: try: value = int(value) except ValueError: print_error("You need to specify a valid number as a limit for your query") return None else: value = 5 rows = session.query(Malware).order_by(Malware.id.desc()).limit(value).offset(offset) elif key == 'md5': rows = session.query(Malware).filter(Malware.md5 == value).all() elif key == 'sha1': rows = session.query(Malware).filter(Malware.sha1 == value).all() elif key == 'sha256': rows = session.query(Malware).filter(Malware.sha256 == value).all() elif key == 'tag': rows = session.query(Malware).filter(self.tag_filter(value)).all() elif key == 'name': if '*' in value: value = value.replace('*', '%') else: value = '%{0}%'.format(value) rows = session.query(Malware).filter(Malware.name.like(value)).all() elif key == 'note': value = '%{0}%'.format(value) rows = session.query(Malware).filter(Malware.note.any(Note.body.like(value))).all() elif key == 'type': rows = session.query(Malware).filter(Malware.type.like('%{0}%'.format(value))).all() elif key == 'mime': rows = session.query(Malware).filter(Malware.mime.like('%{0}%'.format(value))).all() else: print_error("No valid term specified") return rows
def edit_note(self, note_id, body): session = self.Session() try: session.query(Note).get(note_id).body = body session.commit() except SQLAlchemyError as e: print_error("Unable to update note: {0}".format(e)) session.rollback() finally: session.close()
def delete_note(self, note_id): session = self.Session() try: note = session.query(Note).get(note_id) session.delete(note) session.commit() except SQLAlchemyError as e: print_error("Unable to delete note: {0}".format(e)) session.rollback() finally: session.close()
def delete_parent(self, malware_sha256): session = self.Session() try: malware = session.query(Malware).filter(Malware.sha256 == malware_sha256).first() malware.parent = None session.commit() except SQLAlchemyError as e: print_error("Unable to delete parent: {0}".format(e)) session.rollback() finally: session.close()
def delete_analysis(self, analysis_id): session = self.Session() try: analysis = session.query(Analysis).get(analysis_id) session.delete(analysis) session.commit() except SQLAlchemyError as e: print_error("Unable to delete analysis: {0}".format(e)) session.rollback() finally: session.close()
def add_analysis(self, sha256, cmd_line, results): results = json.dumps(results) session = self.Session() malware_entry = session.query(Malware).filter(Malware.sha256 == sha256).first() if not malware_entry: return try: malware_entry.analysis.append(Analysis(cmd_line, results)) session.commit() except SQLAlchemyError as e: print_error("Unable to store analysis: {0}".format(e)) session.rollback() finally: session.close()
def add_note(self, sha256, title, body): session = self.Session() malware_entry = session.query(Malware).filter(Malware.sha256 == sha256).first() if not malware_entry: return try: malware_entry.note.append(Note(title, body)) session.commit() except SQLAlchemyError as e: print_error("Unable to add note: {0}".format(e)) session.rollback() finally: session.close()
def add_note(self, sha256, title, body): session = self.Session() malware_entry = session.query(Malware).filter( Malware.sha256 == sha256).first() if not malware_entry: return try: malware_entry.note.append(Note(title, body)) session.commit() except SQLAlchemyError as e: print_error("Unable to add note: {0}".format(e)) session.rollback() finally: session.close()
def tag_filter(self, value): if not value: return None if "|" in value and "&" in value: print_error("Do not use &' and '|' at the same time.") return None if "|" in value: filt = Malware.tag.any(Tag.tag.in_(value.lower().split("|"))) elif "&" in value: tags = [] for tt in value.lower().split("&"): tags.append(Malware.tag.any(Tag.tag == tt)) filt = and_(*tags) else: filt = Malware.tag.any(Tag.tag == value.lower()) return filt
def update_malware_olevba(self, id, type_olevba): session = self.Session() if sys.version_info < (3, 0): # on Python2 make sure to only handle ASCII try: title.decode('ascii') body.decode('ascii') except UnicodeError as err: raise Python2UnsupportedUnicode( "Non ASCII character(s) in Notes not supported on Python2.\n" "Please use Python >= 3.4".format(err), "error") try: session.query(Malware).get(id).type_olevba = type_olevba session.commit() except SQLAlchemyError as e: print_error("Unable to update type olevba: {0}".format(e)) session.rollback()
def edit_note(self, note_id, body): session = self.Session() if sys.version_info < (3, 0): # on Python2 make sure to only handle ASCII try: body.decode('ascii') except UnicodeError as err: raise Python2UnsupportedUnicode("Non ASCII character(s) in Notes not supported on Python2.\n" "Please use Python >= 3.4".format(err), "error") try: session.query(Note).get(note_id).body = body session.commit() except SQLAlchemyError as e: print_error("Unable to update note: {0}".format(e)) session.rollback() finally: session.close()
def delete_file(self, id): session = self.Session() try: malware = session.query(Malware).get(id) if not malware: print_error("The opened file doesn't appear to be in the database, have you stored it yet?") return False session.delete(malware) session.commit() except SQLAlchemyError as e: print_error("Unable to delete file: {0}".format(e)) session.rollback() return False finally: session.close() return True
def autorun_module(file_hash): if not file_hash: return if not __sessions__.is_set(): __sessions__.new(get_sample_path(file_hash)) for cmd_line in cfg.autorun.commands.split(','): split_commands = cmd_line.split(';') for split_command in split_commands: split_command = split_command.strip() if not split_command: continue root, args = parse_commands(split_command) try: if root in __modules__: print_info("Running command \"{0}\"".format(split_command)) module = __modules__[root]['obj']() module.set_commandline(args) module.run() if cfg.modules.store_output and __sessions__.is_set(): Database().add_analysis(file_hash, split_command, module.output) if cfg.autorun.verbose: print_output(module.output) del (module.output[:]) else: print_error( "\"{0}\" is not a valid command. Please check your viper.conf file." .format(cmd_line)) except: print_error( "Viper was unable to complete the command {0}".format( cmd_line))
def add(self, obj, name=None, tags=None, parent_sha=None, notes_body=None, notes_title=None): session = self.Session() if not name: name = obj.name if parent_sha: parent_sha = session.query(Malware).filter(Malware.sha256 == parent_sha).first() if isinstance(obj, File): try: malware_entry = Malware(md5=obj.md5, crc32=obj.crc32, sha1=obj.sha1, sha256=obj.sha256, sha512=obj.sha512, size=obj.size, type=obj.type, mime=obj.mime, ssdeep=obj.ssdeep, name=name, parent=parent_sha) session.add(malware_entry) session.commit() self.added_ids.setdefault("malware", []).append(malware_entry.id) except IntegrityError: session.rollback() malware_entry = session.query(Malware).filter(Malware.md5 == obj.md5).first() except SQLAlchemyError as e: print_error("Unable to store file: {0}".format(e)) session.rollback() return False if tags: self.add_tags(sha256=obj.sha256, tags=tags) if notes_body and notes_title: self.add_note(sha256=obj.sha256, title=notes_title, body=notes_body) return True
def find(self, key, value=None, offset=0): session = self.Session() offset = int(offset) rows = None if key == 'all': rows = session.query(Malware).options(subqueryload(Malware.tag)).all() elif key == 'ssdeep': ssdeep_val = str(value) rows = session.query(Malware).filter(Malware.ssdeep.contains(ssdeep_val)).all() elif key == 'any': prefix_val = str(value) rows = session.query(Malware).filter(Malware.name.startswith(prefix_val) | Malware.md5.startswith(prefix_val) | Malware.sha1.startswith(prefix_val) | Malware.sha256.startswith(prefix_val) | Malware.type.contains(prefix_val) | Malware.mime.contains(prefix_val)).all() elif key == 'latest': if value: try: value = int(value) except ValueError: print_error("You need to specify a valid number as a limit for your query") return None else: value = 5 rows = session.query(Malware).order_by(Malware.id.desc()).limit(value).offset(offset) elif key == 'md5': rows = session.query(Malware).filter(Malware.md5 == value).all() elif key == 'sha1': rows = session.query(Malware).filter(Malware.sha1 == value).all() elif key == 'sha256': rows = session.query(Malware).filter(Malware.sha256 == value).all() elif key == 'tag': rows = session.query(Malware).filter(self.tag_filter(value)).all() elif key == 'name': if not value: print_error("You need to specify a valid file name pattern (you can use wildcards)") return None if '*' in value: value = value.replace('*', '%') else: value = '%{0}%'.format(value) rows = session.query(Malware).filter(Malware.name.like(value)).all() elif key == 'note': value = unicode(value, "utf-8") value = u'%{0}%'.format(value) rows = session.query(Malware).filter(Malware.note.any(Note.body.like(value))).all() elif key == 'type': rows = session.query(Malware).filter(Malware.type.like('%{0}%'.format(value))).all() elif key == 'mime': rows = session.query(Malware).filter(Malware.mime.like('%{0}%'.format(value))).all() else: print_error("No valid term specified") return rows
def check_and_deploy_yara_rules(): """Yara: check whether rule path exist - if not copy default set of rules to directory""" yara_rules_path = os.path.join(__project__.base_path, "yara") if os.path.exists(yara_rules_path): print_info("Using Yara rules from directory: {}".format(yara_rules_path)) else: # Prio 1: rules if Viper was installed with pip yara_path_setup_utils = os.path.join(VIPER_ROOT, DIST_DIR_YARA_RULES) # Prio 2: rules if Viper was checkout from repo yara_path_repo = os.path.join(VIPER_ROOT, "data", "yara") if os.path.exists(yara_path_setup_utils): print_warning("Yara rule directory not found - copying default " "rules ({}) to: {}".format(yara_path_setup_utils, yara_rules_path)) shutil.copytree(yara_path_setup_utils, yara_rules_path) elif os.path.exists(yara_path_repo): print_warning("Yara rule directory not found - copying default " "rules ({}) to: {}".format(yara_path_repo, yara_rules_path)) shutil.copytree(yara_path_repo, yara_rules_path) else: print_error("No default Yara rules found")
def update_malwarevt(self, resource, scan_date, positives, total): session = self.Session() if sys.version_info < (3, 0): # on Python2 make sure to only handle ASCII try: title.decode('ascii') body.decode('ascii') except UnicodeError as err: raise Python2UnsupportedUnicode( "Non ASCII character(s) in Notes not supported on Python2.\n" "Please use Python >= 3.4".format(err), "error") malwarevt_id = session.query(MalwareVT).filter( MalwareVT.resource == resource) try: session.query(MalwareVT).get(malwarevt_id).scan_date = scan_date session.query(MalwareVT).get(malwarevt_id).positives = positives session.query(MalwareVT).get(malwarevt_id).total = total session.commit() except SQLAlchemyError as e: print_error("Unable to update virus total register: {0}".format(e)) session.rollback()
def autorun_module(file_hash): if not file_hash: return if not __sessions__.is_set(): __sessions__.new(get_sample_path(file_hash)) for cmd_line in cfg.autorun.commands.split(','): split_commands = cmd_line.split(';') for split_command in split_commands: split_command = split_command.strip() if not split_command: continue root, args = parse_commands(split_command) try: if root in __modules__: print_info("Running command \"{0}\"".format(split_command)) module = __modules__[root]['obj']() module.set_commandline(args) module.run() if cfg.modules.store_output and __sessions__.is_set(): Database().add_analysis(file_hash, split_command, module.output) if cfg.autorun.verbose: print_output(module.output) del(module.output[:]) else: print_error("\"{0}\" is not a valid command. Please check your viper.conf file.".format(cmd_line)) except: print_error("Viper was unable to complete the command {0}".format(cmd_line))
def config(data): key = 'C\x00O\x00N\x00F\x00I\x00G' config_coded = extract_config(data) config_raw = rc4crypt(config_coded, key) # 1.3.x - Not implemented yet. if len(config_raw) == 0xe10: print_warning("Detected XtremeRAT 1.3.x, not supported yet") config = None # 2.9.x - Not a stable extract. elif len(config_raw) == 0x1390 or len(config_raw) == 0x1392: config = v29(config_raw) # 3.1 & 3.2 elif len(config_raw) == 0x5Cc: config = v32(config_raw) # 3.5 elif len(config_raw) == 0x7f0: config = v35(config_raw) else: print_error("No known XtremeRAT version detected") config = None return config
def download(url, tor=False): s = requests.Session() s.headers.update({'User-agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'}) proxies = {} if tor: proxies = {'http': 'socks5://{}:{}'.format('127.0.0.1', 9050), 'https': 'socks5://{}:{}'.format('127.0.0.1', 9050)} try: res = s.get(url, proxies=proxies) res.raise_for_status() except ConnectionError as e: if tor: print_error("Connection refused, maybe Tor is not running?") print_error(e) except Exception as e: print_error("Failed download: {0}".format(e)) else: return res.content
def download(url, tor=False): s = requests.Session() s.headers.update({ 'User-agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)' }) proxies = {} if tor: proxies = { 'http': 'socks5://{}:{}'.format('127.0.0.1', 9050), 'https': 'socks5://{}:{}'.format('127.0.0.1', 9050) } try: res = s.get(url, proxies=proxies) res.raise_for_status() except ConnectionError as e: if tor: print_error("Connection refused, maybe Tor is not running?") print_error(e) except Exception as e: print_error("Failed download: {0}".format(e)) else: return res.content
def start(self): # log start log.info('Starting viper-cli') # Logo. logo() # Setup shell auto-complete. def complete(text, state): # filesystem path completion only makes sense for a few commands/modules fs_path_completion = False # clean up user input so far (no leading/trailing/duplicate spaces) line = " ".join(readline.get_line_buffer().split()) words = line.split(" ") # split words; e.g. store -f /tmp -> ['store', '-f', '/tmp'] if words[0] in [i for i in self.cmd.commands]: # handle completion for commands # enable filesystem path completion for certain commands (e.g. export, store) if words[0] in [x for x in self.cmd.commands if self.cmd.commands[x]["fs_path_completion"]]: fs_path_completion = True options = [key for key in self.cmd.commands[words[0]]["parser_args"]] # enable tab completion for projects --switch if words[0] == "projects": if "--switch" in words or "-s" in words: options += get_project_list() # enable tab completion for copy (list projects) if words[0] == "copy": options += get_project_list() completions = [i for i in options if i.startswith(text) and i not in words] elif words[0] in [i for i in __modules__]: # handle completion for modules if len(words) == 1: # only the module name is give so far - present all args and the subparsers (if any) options = [key for key in __modules__[words[0]]["parser_args"]] options += [key for key in __modules__[words[0]]["subparser_args"]] elif len(words) == 2: # 1 complete word and one either complete or incomplete that specifies the subparser or an arg if words[1] in list(__modules__[words[0]]["parser_args"]): # full arg for a module is given options = [key for key in __modules__[words[0]]["parser_args"]] elif words[1] in list(__modules__[words[0]]["subparser_args"]): # subparser is specified - get all subparser args options = [key for key in __modules__[words[0]]["subparser_args"][words[1]]] else: options = [key for key in __modules__[words[0]]["parser_args"]] options += [key for key in __modules__[words[0]]["subparser_args"]] else: # more that 2 words if words[1] in list(__modules__[words[0]]["subparser_args"]): # subparser is specified - get all subparser args options = [key for key in __modules__[words[0]]["subparser_args"][words[1]]] else: options = [key for key in __modules__[words[0]]["parser_args"]] completions = [i for i in options if i.startswith(text) and i not in words] else: # initial completion for both commands and modules completions = [i for i in self.cmd.commands if i.startswith(text)] completions += [i for i in __modules__ if i.startswith(text)] if state < len(completions): return completions[state] if fs_path_completion: # completion for paths only if it makes sense if text.startswith("~"): text = "{0}{1}".format(expanduser("~"), text[1:]) return (glob.glob(text + '*') + [None])[state] return # Auto-complete on tabs. readline.set_completer_delims(' \t\n;') readline.parse_and_bind('tab: complete') readline.set_completer(complete) # Save commands in history file. def save_history(path): readline.write_history_file(path) # If there is an history file, read from it and load the history # so that they can be loaded in the shell. # Now we are storing the history file in the local project folder history_path = os.path.join(__project__.path, 'history') if os.path.exists(history_path): readline.read_history_file(history_path) readline.set_history_length(10000) # Register the save history at program's exit. atexit.register(save_history, path=history_path) # Main loop. while self.active: # If there is an open session, we include the path to the opened # file in the shell prompt. # TODO: perhaps this block should be moved into the session so that # the generation of the prompt is done only when the session's # status changes. prefix = '' if __project__.name: prefix = bold(cyan(__project__.name)) + ' ' if __sessions__.is_set(): stored = '' filename = '' if __sessions__.current.file: filename = __sessions__.current.file.name if not Database().find(key='sha256', value=__sessions__.current.file.sha256): stored = magenta(' [not stored]', True) misp = '' if __sessions__.current.misp_event: misp = ' [MISP' if __sessions__.current.misp_event.event.id: misp += ' {}'.format(__sessions__.current.misp_event.event.id) else: misp += ' New Event' if __sessions__.current.misp_event.off: misp += ' (Offline)' misp += ']' prompt = (prefix + cyan('viper ', True) + white(filename, True) + blue(misp, True) + stored + cyan(' > ', True)) # Otherwise display the basic prompt. else: prompt = prefix + cyan('viper > ', True) # force str (Py3) / unicode (Py2) for prompt if sys.version_info <= (3, 0): prompt = prompt.encode('utf-8') else: prompt = str(prompt) # Wait for input from the user. try: data = input(prompt).strip() except KeyboardInterrupt: print("") # Terminate on EOF. except EOFError: self.stop() print("") continue # Parse the input if the user provided any. else: # If there are recognized keywords, we replace them with # their respective value. data = self.keywords(data) # Skip if the input is empty. if not data: continue # Check for output redirection # If there is a > in the string, we assume the user wants to output to file. if '>' in data: data, console_output['filename'] = data.split('>', 1) if ';' in console_output['filename']: console_output['filename'], more_commands = console_output['filename'].split(';', 1) data = '{};{}'.format(data, more_commands) print("Writing output to {0}".format(console_output['filename'].strip())) # If the input starts with an exclamation mark, we treat the # input as a bash command and execute it. # At this point the keywords should be replaced. if data.startswith('!'): os.system(data[1:]) continue # Try to split commands by ; so that you can sequence multiple # commands at once. # For example: # viper > find name *.pdf; open --last 1; pdf id # This will automatically search for all PDF files, open the first entry # and run the pdf module against it. split_commands = data.split(';') for split_command in split_commands: split_command = split_command.strip() if not split_command: continue # If it's an internal command, we parse the input and split it # between root command and arguments. root, args = self.parse(split_command) # Check if the command instructs to terminate. if root in ('exit', 'quit'): self.stop() continue try: # If the root command is part of the embedded commands list we # execute it. if root in self.cmd.commands: self.cmd.commands[root]['obj'](*args) del(self.cmd.output[:]) # If the root command is part of loaded modules, we initialize # the module and execute it. elif root in __modules__: module = __modules__[root]['obj']() module.set_commandline(args) module.run() if cfg.modules.store_output and __sessions__.is_set(): try: Database().add_analysis(__sessions__.current.file.sha256, split_command, module.output) except Exception: pass del(module.output[:]) else: print("Command not recognized.") except KeyboardInterrupt: pass except Exception: print_error("The command {0} raised an exception:".format(bold(root))) traceback.print_exc() console_output['filename'] = None # reset output to stdout
def find(self, key, value=None, offset=0): session = self.Session() offset = int(offset) rows = None if key == "all": rows = session.query(Malware).all() elif key == "ssdeep": ssdeep_val = str(value) rows = session.query(Malware).filter(Malware.ssdeep.contains(ssdeep_val)).all() elif key == "any": prefix_val = str(value) rows = ( session.query(Malware) .filter( Malware.name.startswith(prefix_val) | Malware.md5.startswith(prefix_val) | Malware.sha1.startswith(prefix_val) | Malware.sha256.startswith(prefix_val) | Malware.type.contains(prefix_val) | Malware.mime.contains(prefix_val) ) .all() ) elif key == "latest": if value: try: value = int(value) except ValueError: print_error("You need to specify a valid number as a limit for your query") return None else: value = 5 rows = session.query(Malware).order_by(Malware.id.desc()).limit(value).offset(offset) elif key == "md5": rows = session.query(Malware).filter(Malware.md5 == value).all() elif key == "sha1": rows = session.query(Malware).filter(Malware.sha1 == value).all() elif key == "sha256": rows = session.query(Malware).filter(Malware.sha256 == value).all() elif key == "tag": rows = session.query(Malware).filter(Malware.tag.any(Tag.tag == value.lower())).all() elif key == "name": if "*" in value: value = value.replace("*", "%") else: value = "%{0}%".format(value) rows = session.query(Malware).filter(Malware.name.like(value)).all() elif key == "note": value = "%{0}%".format(value) rows = session.query(Malware).filter(Malware.note.any(Note.body.like(value))).all() elif key == "type": rows = session.query(Malware).filter(Malware.type.like("%{0}%".format(value))).all() elif key == "mime": rows = session.query(Malware).filter(Malware.mime.like("%{0}%".format(value))).all() else: print_error("No valid term specified") return rows
def start(self): # Logo. logo() # Setup shell auto-complete. def complete(text, state): # Try to autocomplete commands. cmds = [i for i in self.cmd.commands if i.startswith(text)] if state < len(cmds): return cmds[state] # Try to autocomplete modules. mods = [i for i in __modules__ if i.startswith(text)] if state < len(mods): return mods[state] # Then autocomplete paths. if text.startswith("~"): text = "{0}{1}".format(expanduser("~"), text[1:]) return (glob.glob(text+'*')+[None])[state] # Auto-complete on tabs. readline.set_completer_delims(' \t\n;') readline.parse_and_bind('tab: complete') readline.set_completer(complete) # Save commands in history file. def save_history(path): readline.write_history_file(path) # If there is an history file, read from it and load the history # so that they can be loaded in the shell. # Now we are storing the history file in the local project folder history_path = os.path.join(__project__.path, 'history') if os.path.exists(history_path): readline.read_history_file(history_path) # Register the save history at program's exit. atexit.register(save_history, path=history_path) # Main loop. while self.active: # If there is an open session, we include the path to the opened # file in the shell prompt. # TODO: perhaps this block should be moved into the session so that # the generation of the prompt is done only when the session's # status changes. prefix = '' if __project__.name: prefix = bold(cyan(__project__.name)) + ' ' if __sessions__.is_set(): stored = '' filename = '' if __sessions__.current.file: filename = __sessions__.current.file.name if not Database().find(key='sha256', value=__sessions__.current.file.sha256): stored = magenta(' [not stored]', True) misp = '' if __sessions__.current.misp_event: misp = '[MISP' if __sessions__.current.misp_event.event.id: misp += ' {}'.format(__sessions__.current.misp_event.event.id) else: misp += ' New Event' if __sessions__.current.misp_event.off: misp += ' (Offline)' misp += ']' prompt = (prefix + cyan('viper ', True) + white(filename, True) + blue(misp, True) + stored + cyan(' > ', True)) # Otherwise display the basic prompt. else: prompt = prefix + cyan('viper > ', True) # Wait for input from the user. try: data = input(prompt).strip() except KeyboardInterrupt: print("") # Terminate on EOF. except EOFError: self.stop() print("") continue # Parse the input if the user provided any. else: # If there are recognized keywords, we replace them with # their respective value. data = self.keywords(data) # Skip if the input is empty. if not data: continue # Check for output redirection # If there is a > in the string, we assume the user wants to output to file. if '>' in data: data, console_output['filename'] = data.split('>') print("Writing output to {0}".format(console_output['filename'].strip())) # If the input starts with an exclamation mark, we treat the # input as a bash command and execute it. # At this point the keywords should be replaced. if data.startswith('!'): os.system(data[1:]) continue # Try to split commands by ; so that you can sequence multiple # commands at once. # For example: # viper > find name *.pdf; open --last 1; pdf id # This will automatically search for all PDF files, open the first entry # and run the pdf module against it. split_commands = data.split(';') for split_command in split_commands: split_command = split_command.strip() if not split_command: continue # If it's an internal command, we parse the input and split it # between root command and arguments. root, args = self.parse(split_command) # Check if the command instructs to terminate. if root in ('exit', 'quit'): self.stop() continue try: # If the root command is part of the embedded commands list we # execute it. if root in self.cmd.commands: self.cmd.commands[root]['obj'](*args) del(self.cmd.output[:]) # If the root command is part of loaded modules, we initialize # the module and execute it. elif root in __modules__: module = __modules__[root]['obj']() module.set_commandline(args) module.run() if cfg.modules.store_output and __sessions__.is_set(): try: Database().add_analysis(__sessions__.current.file.sha256, split_command, module.output) except: pass del(module.output[:]) else: print("Command not recognized.") except KeyboardInterrupt: pass except Exception: print_error("The command {0} raised an exception:".format(bold(root))) traceback.print_exc() console_output['filename'] = None # reset output to stdout