def __init__(self, config: ConfigParser, plugin_opts: Optional[Dict]) -> None: super().__init__(config, plugin_opts) self.sandbox_url = None self.apikey = None self.delay = 30 self.max_attempts = 10 self.useragent = 'Falcon Sandbox' # Available environments ID: # 300: 'Linux (Ubuntu 16.04, 64 bit)', # 200: 'Android Static Analysis’, # 160: 'Windows 10 64 bit’, # 110: 'Windows 7 64 bit’, # 100: ‘Windows 7 32 bit’ self.environment_id = 160 self.wait_for_results = True if plugin_opts and 'sandbox_url' in plugin_opts: self.sandbox_url = plugin_opts['sandbox_url'] elif config.has_option('options', 'sandbox_url'): self.sandbox_url = config.get('options', 'sandbox_url') if plugin_opts and 'apikey' in plugin_opts: self.apikey = plugin_opts['apikey'] elif config.has_option('options', 'apikey'): self.apikey = config.get('options', 'apikey') if plugin_opts and 'delay' in plugin_opts: self.delay = int(plugin_opts['delay']) elif config.has_option('options', 'delay'): self.delay = int(config.get('options', 'delay')) if plugin_opts and 'max_attempts' in plugin_opts: self.max_attempts = int(plugin_opts['max_attempts']) elif config.has_option('options', 'max_attempts'): self.max_attempts = config.getint('options', 'max_attempts') if plugin_opts and 'useragent' in plugin_opts: self.useragent = plugin_opts['useragent'] elif config.has_option('options', 'useragent'): self.useragent = config.get('options', 'useragent') if plugin_opts and 'environment_id' in plugin_opts: self.environment_id = int(plugin_opts['environment_id']) elif config.has_option('options', 'environment_id'): self.environment_id = config.getint('options', 'environment_id') if plugin_opts and 'wait_for_results' in plugin_opts: self.wait_for_results = plugin_opts['wait_for_results'] elif config.has_option('options', 'wait_for_results'): self.wait_for_results = config.getboolean('options', 'wait_for_results') if not self.sandbox_url: raise StoqPluginException("Falcon Sandbox URL was not provided") if not self.apikey: raise StoqPluginException( "Falcon Sandbox API Key was not provided")
def scan(self, payload: Payload, request_meta: RequestMeta) -> WorkerResponse: """ Decodes and extracts information from Java Class files """ results = {} try: content = unpack_class(payload.content) except ClassUnpackException as err: raise StoqPluginException(f'Unable to parse payload: {err}') try: results = { 'provided': content.get_provides(), 'required': content.get_requires(), 'constants': [], } for obj, _, data in content.cpool.pretty_constants(): if len(data) <= 6: continue constants = {} constants['id'] = obj constants['data'] = data results['constants'].append(constants) except Exception as err: raise StoqPluginException(f'Unable to analyze Java Class {err}') return WorkerResponse(results)
def __init__(self, config: ConfigParser, plugin_opts: Optional[Dict]) -> None: super().__init__(config, plugin_opts) self.opswat_url = None self.apikey = None self.delay = 30 self.max_attempts = 10 if plugin_opts and 'opswat_url' in plugin_opts: self.opswat_url = plugin_opts['opswat_url'] elif config.has_option('options', 'opswat_url'): self.opswat_url = config.get('options', 'opswat_url') if plugin_opts and 'apikey' in plugin_opts: self.apikey = plugin_opts['apikey'] elif config.has_option('options', 'apikey'): self.apikey = config.get('options', 'apikey') if plugin_opts and 'delay' in plugin_opts: self.delay = int(plugin_opts['delay']) elif config.has_option('options', 'delay'): self.delay = int(config.get('options', 'delay')) if plugin_opts and 'max_attempts' in plugin_opts: self.max_attempts = int(plugin_opts['max_attempts']) elif config.has_option('options', 'max_attempts'): self.max_attempts = int(config.get('options', 'max_attempts')) if not self.opswat_url: raise StoqPluginException("MetaDefender URL was not provided") if not self.apikey: raise StoqPluginException("MetaDefender API Key was not provided")
def __init__(self, config: StoqConfigParser) -> None: super().__init__(config) self.opswat_url = config.get('options', 'opswat_url', fallback=None) if not self.opswat_url: raise StoqPluginException('MetaDefender URL was not provided') self.apikey = config.get('options', 'apikey', fallback=None) if not self.apikey: raise StoqPluginException('MetaDefender API Key was not provided') self.delay = config.getint('options', 'delay', fallback=10) self.max_attempts = config.getint('options', 'max_attempts', fallback=10)
def __init__(self, config: StoqConfigParser) -> None: super().__init__(config) self.workspaceid = config.get('options', 'workspaceid', fallback=None) if not self.workspaceid: raise StoqPluginException('workspaceid has not been defined') self.workspacekey = config.get('options', 'workspacekey', fallback=None) if not self.workspacekey: raise StoqPluginException('workspacekey has not been defined') self.logtype = config.get('options', 'logtype', fallback='stoQ') self.uri = f'https://{self.workspaceid}.ods.opinsights.azure.com{self.API_RESOURCE}?api-version={self.API_VERSION}'
def compile_rules(self, filepath: str) -> None: filepath = os.path.realpath(filepath) if not os.path.isfile(filepath): raise StoqPluginException( f"Nonexistent yara rules file provided: {filepath}") else: return yara.compile(filepath=filepath)
def scan(self, payload: Payload, request_meta: RequestMeta) -> WorkerResponse: """ Scan a payload using TRiD """ results = {} with tempfile.NamedTemporaryFile() as temp_file: temp_file.write(payload.content) temp_file.flush() try: cmd = [self.bin_path, f"-d:{self.trid_defs}", temp_file.name] trid_results = check_output(cmd).splitlines() except Exception as err: raise StoqPluginException('Failed gathering TRiD data') for line in trid_results[6:]: if line.startswith('Warning'.encode()): break line = line.decode().split() if line: ext = line[1].strip('(.)') results[ext] = {'likely': line[0], 'type': ' '.join(line[2:])} return WorkerResponse(results)
def __init__(self, config: ConfigParser, plugin_opts: Optional[Dict]) -> None: super().__init__(config, plugin_opts) self.apikey = None self.time_slice = '1m' self.download = False if plugin_opts and 'apikey' in plugin_opts: self.apikey = plugin_opts['apikey'] elif config.has_option('options', 'apikey'): self.apikey = config.get('options', 'apikey') if plugin_opts and 'time_since' in plugin_opts: self.time_since = plugin_opts['time_since'] elif config.has_option('options', 'time_since'): self.time_since = config.get('options', 'time_since') if plugin_opts and 'download' in plugin_opts: self.download = plugin_opts['download'] elif config.has_option('options', 'download'): self.download = config.get('options', 'download') if not self.apikey: raise StoqPluginException("VTMIS API Key does not exist")
def __init__(self, config: StoqConfigParser) -> None: super().__init__(config) self.source_dir = config.get('options', 'source_dir', fallback=None) if not self.source_dir or not os.path.exists(self.source_dir): raise StoqPluginException( f"Source directory not defined or doesn't exist: '{self.source_dir}'" ) self.source_dir = os.path.abspath(self.source_dir)
def __init__(self, config: StoqConfigParser) -> None: super().__init__(config) self.apikey = config.get('options', 'apikey', fallback=None) self.time_since = config.get('options', 'time_since', fallback='1m') self.download = config.getboolean('options', 'download', fallback=False) if not self.apikey: raise StoqPluginException('VTMIS API Key does not exist')
def __init__(self, config: StoqConfigParser) -> None: super().__init__(config) self.sandbox_url = config.get('options', 'sandbox_url', fallback=None) if not self.sandbox_url: raise StoqPluginException("Falcon Sandbox URL was not provided") self.apikey = config.get('options', 'apikey', fallback=None) if not self.apikey: raise StoqPluginException("Falcon Sandbox API Key was not provided") self.delay = config.getint('options', 'delay', fallback=30) self.max_attempts = config.getint('options', 'max_attempts', fallback=10) self.useragent = config.get('options', 'useragent', fallback='Falcon Sandbox') # Available environments ID: # 300: 'Linux (Ubuntu 16.04, 64 bit)', # 200: 'Android Static Analysis’, # 160: 'Windows 10 64 bit’, # 110: 'Windows 7 64 bit’, # 100: ‘Windows 7 32 bit’ self.environment_id = config.getint('options', 'environment_id', fallback=160) self.wait_for_results = config.getboolean( 'options', 'wait_for_results', fallback=True )
def __init__(self, config: ConfigParser, plugin_opts: Optional[Dict]) -> None: super().__init__(config, plugin_opts) self.apikey = None if plugin_opts and 'apikey' in plugin_opts: self.apikey = plugin_opts['apikey'] elif config.has_option('options', 'apikey'): self.apikey = config.get('options', 'apikey') if not self.apikey: raise StoqPluginException("VTMIS API Key does not exist")
async def scan(self, payload: Payload, request: Request) -> WorkerResponse: """ Scan a payload using LIEF """ filename = payload.results.payload_meta.extra_data.get( 'filename', payload.results.payload_id) try: binary = lief.parse(raw=payload.content, name=filename) except lief.exception as err: raise StoqPluginException(f'Unable to parse payload: {err}') if binary is None: raise StoqPluginException('The file type isn\'t supported by LIEF') if self.abstract == True: results = lief.to_json_from_abstract(binary.abstract) else: results = lief.to_json(binary) return WorkerResponse(json.loads(results))
def scan(self, payload: Payload, request_meta: RequestMeta) -> WorkerResponse: """ Scan a payload using Exiftool """ with tempfile.NamedTemporaryFile() as temp_file: temp_file.write(payload.content) temp_file.flush() try: cmd = [self.bin_path, '-j', '-n', temp_file.name] output = run(cmd, stdout=PIPE) results = json.loads(output.stdout)[0] except Exception as err: raise StoqPluginException(f'Failed gathering exif data: {err}') return WorkerResponse(results)
def __init__(self, config: ConfigParser, plugin_opts: Optional[Dict]) -> None: super().__init__(config, plugin_opts) self.source_dir = None if plugin_opts and 'source_dir' in plugin_opts: self.source_dir = plugin_opts['source_dir'] elif config.has_option('options', 'source_dir'): self.source_dir = config.get('options', 'source_dir') if not self.source_dir or not os.path.exists(self.source_dir): raise StoqPluginException( f"Source directory not defined or doesn't exist: '{self.source_dir}'" ) self.source_dir = os.path.abspath(self.source_dir)
async def ingest(self, queue: Queue) -> None: """ Ingest files from a directory """ if not self.source_dir: raise StoqPluginException('Source directory not defined') source_path = Path(self.source_dir).resolve() if source_path.is_dir(): if self.recursive: for path in source_path.rglob('**/*'): await self._queue(path, queue) else: for path in source_path.glob('*'): await self._queue(path, queue) else: await self._queue(source_path, queue)
def __init__(self, config: StoqConfigParser) -> None: super().__init__(config) filename = getframeinfo(currentframe()).filename parent = Path(filename).resolve().parent self.bin = config.get('options', 'bin', fallback='trid') self.skip_warnings = config.getlist( 'options', 'skip_warnings', fallback=['file seems to be plain text/ASCII']) self.trid_defs = config.get('options', 'trid_defs', fallback='triddefs.trd') if not os.path.isabs(self.trid_defs) and self.trid_defs: self.trid_defs = os.path.join(parent, self.trid_defs) if not os.path.isfile(self.trid_defs): raise StoqPluginException( f'TrID definitions do not exist at {self.trid_defs}')
def ingest(self, queue: Queue) -> None: """ Ingest files from a directory """ if not self.source_dir: raise StoqPluginException('Source directory not defined') if os.path.isdir(self.source_dir): if self.recursive: for root_path, subdirs, files in os.walk(self.source_dir): for entry in files: path = os.path.join(root_path, entry) self._queue(path, queue) else: for entry in os.scandir(self.source_dir): if not entry.name.startswith('.') and entry.is_file(): path = os.path.join(self.source_dir, entry.name) self._queue(path, queue) elif os.path.isfile(self.source_dir): self._queue(self.source_dir, queue)
def decorate(self, response: StoqResponse) -> DecoratorResponse: """ Decorate results using a template """ results = None try: dirname = os.path.dirname(self.template) basename = os.path.basename(self.template) env = Environment( loader=FileSystemLoader(dirname), trim_blocks=True, lstrip_blocks=True, autoescape=select_autoescape(default_for_string=True, default=True), ) results = env.get_template(basename).render(response=response) except TemplateNotFound: raise StoqPluginException(f'Template path not found: {self.template}') return DecoratorResponse(results)
def __init__(self, config: ConfigParser, plugin_opts: Optional[Dict]) -> None: super().__init__(config, plugin_opts) self.trid_defs = 'triddefs.trd' self.bin_path = 'trid' filename = getframeinfo(currentframe()).filename parent = Path(filename).resolve().parent if plugin_opts and 'trid_defs' in plugin_opts: self.trid_defs = plugin_opts['trid_defs'] elif config.has_option('options', 'trid_defs'): self.trid_defs = config.get('options', 'trid_defs') if not os.path.isabs(self.trid_defs) and self.trid_defs: self.trid_defs = os.path.join(parent, self.trid_defs) if not self.trid_defs or not os.path.isfile(self.trid_defs): raise StoqPluginException( f'TrID definitions do not exist at {self.trid_defs}') if plugin_opts and 'bin_path' in plugin_opts: self.bin_path = plugin_opts['bin_path'] elif config.has_option('options', 'bin_path'): self.bin_path = config.get('options', 'bin_path')
def scan(self, payload: Payload, request_meta: RequestMeta) -> WorkerResponse: """ Decompress a payload request_meta: - passwords - archiver """ if len(payload.content) > self.maximum_size: raise StoqPluginException( f'Compressed file too large: {len(payload.content)} > {self.maximum_size}' ) archiver = None mimetype = None results = {} errors = [] extracted = [] passwords = request_meta.extra_data.get('passwords', self.passwords) if isinstance(passwords, str): passwords = [p.strip() for p in passwords.split(',')] # Determine the mimetype of the payload so we can identify the # correct archiver. This should either be based off the request_meta # (useful when payload is passed via dispatching) or via magic if 'archiver' in request_meta.extra_data: if request_meta.extra_data['archiver'] in self.ARCHIVE_CMDS: archiver = self.ARCHIVE_CMDS[ request_meta.extra_data['archiver']] else: raise StoqPluginException( f"Unknown archive type of {request_meta['archiver']}") else: mimetype = magic.from_buffer(payload.content, mime=True) if mimetype in self.ARCHIVE_MAGIC: archive_type = self.ARCHIVE_MAGIC[mimetype] if archive_type in self.ARCHIVE_CMDS: archiver = self.ARCHIVE_CMDS[archive_type] else: raise StoqPluginException( f'Unknown archive type of {archive_type}') if not archiver: raise StoqPluginException( f'Unable to determine archive type, mimetype: {mimetype}') with tempfile.TemporaryDirectory() as extract_dir: fd, archive_file = tempfile.mkstemp(dir=extract_dir) with open(fd, 'xb') as f: f.write(payload.content) f.flush() archive_outdir = tempfile.mkdtemp(dir=extract_dir) for password in passwords: cmd = archiver.replace('%INFILE%', shlex.quote(archive_file)) cmd = cmd.replace('%OUTDIR%', shlex.quote(archive_outdir)) cmd = cmd.replace('%PASSWORD%', shlex.quote(password)) cmd = cmd.split(" ") p = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True) try: outs, errs = p.communicate(timeout=self.timeout) except TimeoutExpired: p.kill() raise StoqPluginException( 'Timed out decompressing payload') if p.returncode == 0: break for root, dirs, files in os.walk(archive_outdir): for f in files: path = os.path.join(extract_dir, root, f) if os.path.getsize(path) > self.maximum_size: errors.append( f'Extracted object is too large ({os.path.getsize(path)} > {self.maximum_size})' ) continue with open(path, "rb") as extracted_file: meta = PayloadMeta(extra_data={'filename': f}) try: data = extracted_file.read() except OSError as err: errors.append( f'Unable to access extracted content: {err}') continue extracted.append(ExtractedPayload(data, meta)) return WorkerResponse(results, errors=errors, extracted=extracted)
def __init__(self, config: StoqConfigParser) -> None: super().__init__(config) self.apikey = config.get('options', 'apikey', fallback=None) if not self.apikey: raise StoqPluginException("VTMIS API Key does not exist")