Beispiel #1
0
  def Process(self):
    """Executes a Timesketch export."""
    if not self.timesketch_api:
      message = 'Could not connect to Timesketch server'
      self.ModuleError(message, critical=True)

    sketch = self.state.GetFromCache('timesketch_sketch')
    if not sketch:
      sketch = self.timesketch_api.get_sketch(self.sketch_id)

    recipe_name = self.state.recipe.get('name', 'no_recipe')
    input_names = []
    for file_container in self.state.GetContainers(containers.File):
      description = file_container.name
      if not description:
        continue
      name = description.rpartition('.')[0]
      name = name.replace(' ', '_').replace('-', '_')
      input_names.append(name)

    if input_names:
      timeline_name = '{0:s}_{1:s}'.format(
          recipe_name, '_'.join(input_names))
    else:
      timeline_name = recipe_name

    with importer.ImportStreamer() as streamer:
      streamer.set_sketch(sketch)
      streamer.set_timeline_name(timeline_name)

      for file_container in self.state.GetContainers(containers.File):
        path = file_container.path
        streamer.add_file(path)

    api_root = sketch.api.api_root
    host_url = api_root.partition('api/v1')[0]
    sketch_url = '{0:s}sketches/{1:d}/'.format(host_url, sketch.id)
    message = 'Your Timesketch URL is: {0:s}'.format(sketch_url)
    container = containers.Report(
        module_name='TimesketchExporter',
        text=message,
        text_format='markdown')
    self.state.StoreContainer(container)

    for analyzer in self._analyzers:
      results = sketch.run_analyzer(
          analyzer_name=analyzer, timeline_name=timeline_name)
      if not results:
        self.logger.info('Analyzer [{0:s}] not able to run on {1:s}'.format(
            analyzer, timeline_name))
        continue
      session_id = results.id
      if not session_id:
        self.logger.info(
            'Analyzer [{0:s}] didn\'t provide any session data'.format(
                analyzer))
        continue
      self.logger.info('Analyzer: {0:s} is running, session ID: {1:d}'.format(
          analyzer, session_id))
      self.logger.info(results.status_string)
Beispiel #2
0
 def testGetContainer(self):
     """Tests that containers can be retrieved."""
     test_state = state.DFTimewolfState(config.Config)
     test_report = containers.Report(module_name='foo', text='bar')
     test_state.StoreContainer(test_report)
     reports = test_state.GetContainers(containers.Report)
     self.assertEqual(len(reports), 1)
     self.assertIsInstance(reports[0], containers.Report)
Beispiel #3
0
 def testStoreContainer(self):
     """Tests that containers are stored correctly."""
     test_state = state.DFTimewolfState(config.Config)
     test_report = containers.Report(module_name='foo', text='bar')
     test_state.StoreContainer(test_report)
     self.assertEqual(len(test_state.store), 1)
     self.assertIn('report', test_state.store)
     self.assertEqual(len(test_state.store['report']), 1)
     self.assertIsInstance(test_state.store['report'][0], containers.Report)
Beispiel #4
0
 def testStreamingCallback(self, mock_callback):
     """Tests that registered callbacks are appropriately called."""
     test_state = state.DFTimewolfState(config.Config)
     test_state.LoadRecipe(test_recipe.contents)
     test_state.SetupModules()
     # DummyModule1 has registered a StreamingConsumer
     report = containers.Report(module_name='testing', text='asd')
     test_state.StreamContainer(report)
     mock_callback.assert_called_with(report)
Beispiel #5
0
  def testGetAttributeNames(self):
    """Tests the GetAttributeNames function."""
    attribute_container = containers.Report(module_name='name', text='text')

    expected_attribute_names = [
        'attributes', 'module_name', 'text', 'text_format']

    attribute_names = sorted(attribute_container.GetAttributeNames())

    self.assertEqual(attribute_names, expected_attribute_names)
Beispiel #6
0
  def _ProcessStories(self, stories):
    """Extracts story content from a list of stories and saves as a report.

    The function runs through all saved stories in a sketch and adds a
    formatted version of the story as a report container.

    Args:
      stories (list): a list of Story objects (timesketch_api.story.Story).
    """
    for story in stories:
      if self._formatter.FORMAT == 'html':
        story_string = story.to_html()
      elif self._formatter.FORMAT == 'markdown':
        story_string = story.to_markdown()
      else:
        story_string = story.to_export_format(self._formatter.FORMAT)

      self.state.StoreContainer(containers.Report(
          module_name='TimesketchEnhancer',
          text_format=self._formatter.FORMAT,
          text=story_string))
Beispiel #7
0
    def Process(self):
        """Process files with Turbinia."""
        log_file_path = os.path.join(self._output_path, 'turbinia.log')
        print('Turbinia log file: {0:s}'.format(log_file_path))

        if self.state.input and not self.disk_name:
            _, disk = self.state.input[0]
            self.disk_name = disk.name
            print('Using disk {0:s} from previous collector'.format(
                self.disk_name))

        evidence_ = evidence.GoogleCloudDisk(disk_name=self.disk_name,
                                             project=self.project,
                                             zone=self.turbinia_zone)
        try:
            evidence_.validate()
        except TurbiniaException as exception:
            self.state.AddError(exception, critical=True)
            return

        request = TurbiniaRequest(requester=getpass.getuser())
        request.evidence.append(evidence_)
        if self.sketch_id:
            request.recipe['sketch_id'] = self.sketch_id
        if not self.run_all_jobs:
            request.recipe['jobs_blacklist'] = ['StringsJob']

        # Get threat intelligence data from any modules that have stored some.
        # In this case, observables is a list of containers.ThreatIntelligence
        # objects.
        threatintel = self.state.GetContainers(containers.ThreatIntelligence)
        if threatintel:
            print(
                'Sending {0:d} threatintel to Turbinia GrepWorkers...'.format(
                    len(threatintel)))
            indicators = [item.indicator for item in threatintel]
            request.recipe['filter_patterns'] = indicators

        request_dict = {
            'instance': self.instance,
            'project': self.project,
            'region': self.turbinia_region,
            'request_id': request.request_id
        }

        try:
            print('Creating Turbinia request {0:s} with Evidence {1!s}'.format(
                request.request_id, evidence_.name))
            self.client.send_request(request)
            print('Waiting for Turbinia request {0:s} to complete'.format(
                request.request_id))
            self.client.wait_for_request(**request_dict)
            task_data = self.client.get_task_data(**request_dict)
        except TurbiniaException as exception:
            # TODO: determine if exception should be converted into a string as
            # elsewhere in the codebase.
            self.state.AddError(exception, critical=True)
            return

        message = self.client.format_task_status(**request_dict,
                                                 full_report=True)
        short_message = self.client.format_task_status(**request_dict)
        print(short_message)

        # Store the message for consumption by any reporting modules.
        report = containers.Report(module_name='TurbiniaProcessor',
                                   text=message,
                                   text_format='markdown')
        self.state.StoreContainer(report)

        local_paths, gs_paths = self._DeterminePaths(task_data)

        if not local_paths and not gs_paths:
            self.state.AddError(
                'No interesting files found in Turbinia output.',
                critical=True)
            return

        timeline_label = '{0:s}-{1:s}'.format(self.project, self.disk_name)
        # Any local files that exist we can add immediately to the output
        all_local_paths = [(timeline_label, p) for p in local_paths
                           if os.path.exists(p)]

        downloaded_gs_paths = self._DownloadFilesFromGCS(
            timeline_label, gs_paths)
        all_local_paths.extend(downloaded_gs_paths)

        if not all_local_paths:
            self.state.AddError('No interesting files could be found.',
                                critical=True)
        self.state.output = all_local_paths

        for _, path in all_local_paths:
            if path.endswith('BinaryExtractorTask.tar.gz'):
                self.state.StoreContainer(
                    containers.ThreatIntelligence(
                        name='BinaryExtractorResults',
                        indicator=None,
                        path=path))
            if path.endswith('hashes.json'):
                self.state.StoreContainer(
                    containers.ThreatIntelligence(name='ImageExportHashes',
                                                  indicator=None,
                                                  path=path))
Beispiel #8
0
    def Process(self):
        """Process files with Turbinia."""
        log_file_path = os.path.join(self._output_path, 'turbinia.log')
        print('Turbinia log file: {0:s}'.format(log_file_path))

        if self.state.input and not self.disk_name:
            _, disk = self.state.input[0]
            self.disk_name = disk.name
            print('Using disk {0:s} from previous collector'.format(
                self.disk_name))

        evidence_ = evidence.GoogleCloudDisk(disk_name=self.disk_name,
                                             project=self.project,
                                             zone=self.turbinia_zone)
        try:
            evidence_.validate()
        except TurbiniaException as exception:
            self.state.AddError(exception, critical=True)
            return

        request = TurbiniaRequest(requester=getpass.getuser())
        request.evidence.append(evidence_)
        if self.sketch_id:
            request.recipe['sketch_id'] = self.sketch_id
        if not self.run_all_jobs:
            request.recipe['jobs_blacklist'] = ['StringsJob']

        # Get threat intelligence data from any modules that have stored some.
        # In this case, observables is a list of containers.ThreatIntelligence
        # objects.
        threatintel = self.state.GetContainers(containers.ThreatIntelligence)
        if threatintel:
            print(
                'Sending {0:d} threatintel to Turbinia GrepWorkers...'.format(
                    len(threatintel)))
            indicators = [item.indicator for item in threatintel]
            request.recipe['filter_patterns'] = indicators

        request_dict = {
            'instance': self.instance,
            'project': self.project,
            'region': self.turbinia_region,
            'request_id': request.request_id
        }

        try:
            print('Creating Turbinia request {0:s} with Evidence {1!s}'.format(
                request.request_id, evidence_.name))
            self.client.send_request(request)
            print('Waiting for Turbinia request {0:s} to complete'.format(
                request.request_id))
            self.client.wait_for_request(**request_dict)
            task_data = self.client.get_task_data(**request_dict)
        except TurbiniaException as exception:
            # TODO: determine if exception should be converted into a string as
            # elsewhere in the codebase.
            self.state.AddError(exception, critical=True)
            return

        message = self.client.format_task_status(**request_dict,
                                                 full_report=True)
        short_message = self.client.format_task_status(**request_dict)
        print(short_message)

        # Store the message for consumption by any reporting modules.
        report = containers.Report(module_name='TurbiniaProcessor',
                                   text=message,
                                   text_format='markdown')
        self.state.StoreContainer(report)

        # This finds all .plaso files in the Turbinia output, and determines if they
        # are local or remote (it's possible this will be running against a local
        # instance of Turbinia).
        local_paths = []
        gs_paths = []
        timeline_label = '{0:s}-{1:s}'.format(self.project, self.disk_name)
        for task in task_data:
            # saved_paths may be set to None
            for path in task.get('saved_paths') or []:
                if path.startswith('/') and path.endswith('.plaso'):
                    local_paths.append(path)
                if path.startswith('gs://') and path.endswith('.plaso'):
                    gs_paths.append(path)

        if not local_paths and not gs_paths:
            self.state.AddError('No .plaso files found in Turbinia output.',
                                critical=True)
            return

        # Any local .plaso files that exist we can add immediately to the output
        self.state.output = [(timeline_label, p) for p in local_paths
                             if os.path.exists(p)]

        # For files remote in GCS we copy each plaso file back from GCS and then add
        # to output paths
        # TODO: Externalize fetching files from GCS buckets to a different module.
        for path in gs_paths:
            local_path = None
            try:
                output_writer = output_manager.GCSOutputWriter(
                    path, local_output_dir=self._output_path)
                local_path = output_writer.copy_from(path)
            except TurbiniaException as exception:
                # TODO: determine if exception should be converted into a string as
                # elsewhere in the codebase.
                self.state.AddError(exception, critical=True)
                return

            if local_path:
                self.state.output.append((timeline_label, local_path))

        if not self.state.output:
            self.state.AddError('No .plaso files could be found.',
                                critical=True)
Beispiel #9
0
  def TurbiniaProcess(self, evidence_):
    """Creates and sends a Turbinia processing request.

    Args:
      evidence_(turbinia.evidence.Evidence): The evience to proecess

    Returns:
      list[dict]: The Turbinia task data
    """
    try:
      evidence_.validate()
    except TurbiniaException as exception:
      self.ModuleError(str(exception), critical=True)

    request = TurbiniaRequest(requester=getpass.getuser())
    request.evidence.append(evidence_)
    if self.sketch_id:
      request.recipe['sketch_id'] = self.sketch_id
    if not self.run_all_jobs:
      # TODO(aarontp): Remove once the release with
      # https://github.com/google/turbinia/pull/554 is live.
      request.recipe['jobs_blacklist'] = [
          'StringsJob', 'BinaryExtractorJob', 'BulkExtractorJob', 'PhotorecJob']
      request.recipe['jobs_denylist'] = [
          'StringsJob', 'BinaryExtractorJob', 'BulkExtractorJob', 'PhotorecJob']

    # Get threat intelligence data from any modules that have stored some.
    # In this case, observables is a list of containers.ThreatIntelligence
    # objects.
    threatintel = self.state.GetContainers(containers.ThreatIntelligence)
    if threatintel:
      self.logger.info(
          'Sending {0:d} threatintel to Turbinia GrepWorkers...'.format(
              len(threatintel)))
      indicators = [item.indicator for item in threatintel]
      request.recipe['filter_patterns'] = indicators

    request_dict = {
        'instance': self.instance,
        'project': self.project,
        'region': self.turbinia_region,
        'request_id': request.request_id
    }

    task_data = None
    try:
      self.logger.info(
          'Creating Turbinia request {0:s} with Evidence {1!s}'.format(
              request.request_id, evidence_.name))
      self.client.send_request(request)
      self.logger.info('Waiting for Turbinia request {0:s} to complete'.format(
          request.request_id))
      self.client.wait_for_request(**request_dict)
      task_data = self.client.get_task_data(**request_dict)
    except TurbiniaException as exception:
      # TODO: determine if exception should be converted into a string as
      # elsewhere in the codebase.
      self.ModuleError(str(exception), critical=True)

    message = self.client.format_task_status(full_report=True, **request_dict)
    short_message = self.client.format_task_status(**request_dict)
    self.logger.info(short_message)

    # Store the message for consumption by any reporting modules.
    report = containers.Report(
        module_name='TurbiniaProcessor', text=message, text_format='markdown')
    self.state.StoreContainer(report)

    return task_data
Beispiel #10
0
  def Process(self):
    """Executes a Timesketch enhancer module."""
    if not self._wait_for_analyzers:
      self.logger.warning(
          'Not waiting for analyzers to run, skipping enhancer.')
      return

    if not self.timesketch_api:
      message = 'Could not connect to Timesketch server'
      self.ModuleError(message, critical=True)

    sketch = self.state.GetFromCache('timesketch_sketch')
    if not sketch:
      message = (
          'Sketch not found in cache, maybe the previous module was unable '
          'to connect to Timesketch or unable to connect to and/or create '
          'a sketch.')
      self.ModuleError(message, critical=True)

    self.logger.info('Waiting for analyzers to complete their run.')

    summary_lines = [self._formatter.Heading('Timesketch Run', level=1)]
    summary_lines.append(self._formatter.Paragraph(
        'This is a summary of actions taken by Timesketch '
        'during its run.'))
    summary_lines.append(self._formatter.Paragraph(
        'To visit the sketch, click {0:s}'.format(self._formatter.Link(
            url=self._GetSketchURL(sketch), text='here'))))
    summary_lines.append(self._formatter.Paragraph(
        'Here is an overview of actions taken:'))

    self._WaitForAnalyzers(sketch)

    # Force a refresh of sketch data.
    _ = sketch.lazyload_data(refresh_cache=True)

    summary_lines.append(self._formatter.IndentStart())

    saved_searches = sketch.list_saved_searches()
    self._ProcessSavedSearches(saved_searches)
    sketch_url = self._GetSketchURL(sketch)
    search_string = self._GenerateSavedSearchString(
        saved_searches, sketch_url)
    formatted_string = ''
    if search_string:
      formatted_string = self._formatter.IndentText(
          'The following saved searches were discovered:\n'
          '{0:s}{1:s}{2:s}'.format(
              self._formatter.IndentStart(),
              search_string,
              self._formatter.IndentEnd()))
    else:
      formatted_string = self._formatter.IndentText(
          'Analyzers didn\'t save any searches.')
    summary_lines.append(formatted_string)

    aggregations = sketch.list_aggregations(
        exclude_labels=['informational'])
    self._ProcessAggregations(aggregations)
    aggregation_string = self._GenerateAggregationString(aggregations)
    if aggregation_string:
      formatted_string = self._formatter.IndentText(
          'The following aggregations were discovered:'
          '\n{0:s}{1:s}{2:s}'.format(
              self._formatter.IndentStart(),
              aggregation_string,
              self._formatter.IndentEnd()))

    else:
      formatted_string = self._formatter.IndentText(
          'No aggregations were generated by analyzers.')
    summary_lines.append(formatted_string)

    stories = sketch.list_stories()
    if self._include_stories:
      self._ProcessStories(stories)

    story_string = self._GenerateStoryString(stories, sketch_url)
    if story_string:
      formatted_string = self._formatter.IndentText(
          'The following stories were generated:\n{0:s}{1:s}{2:s}'.format(
              self._formatter.IndentStart(),
              story_string,
              self._formatter.IndentEnd()))
    else:
      formatted_string = self._formatter.IndentText(
          'No stories were generated by analyzers.')
    summary_lines.append(formatted_string)

    summary_lines.append(self._formatter.IndentEnd())

    analyzer_results = sketch.get_analyzer_status(as_sessions=True)
    if analyzer_results:
      line_string = self._formatter.Line()
      summary_lines.append(line_string)
      paragraph = self._formatter.Paragraph(
          'Information from analyzer run:')
      summary_lines.append(paragraph)
      indent = self._formatter.IndentStart()
      summary_lines.append(indent)

      completed_ids = set()
      for result in analyzer_results:
        if result.id in completed_ids:
          continue

        if result.log:
          log_text = self._formatter.IndentText(
              'Logs: {0:s}'.format(result.log), level=2)
        else:
          log_text = ''

        formatted_string = self._formatter.IndentText(
            'ID: {0:d}\n{1:s}{2:s}\n{3:s}{4:s}'.format(
                result.id,
                self._formatter.IndentStart(),
                '\n'.join([self._formatter.IndentText(
                    x.strip(), level=2) for x in result.results.split('\n')]),
                log_text,
                self._formatter.IndentEnd()
            )
        )
        summary_lines.append(formatted_string)
        completed_ids.add(result.id)
      summary_lines.append(self._formatter.IndentEnd())

    report_attributes = [{'update_comment': True}]
    self.state.StoreContainer(containers.Report(
        module_name='TimesketchEnhancer', text_format=self._formatter.FORMAT,
        text='\n'.join(summary_lines), attributes=report_attributes))
    self.logger.info('Analyzer reports generated')