def analyze_lambda_handler(event_data, lambda_context): """Lambda function entry point. Args: event_data: [dict] of the form: { 'S3Objects': [...], # S3 object keys. 'SQSReceipts': [...] # SQS receipt handles (to be deleted after processing). } There can be any number of S3objects, but no more than 10 SQS receipts. lambda_context: LambdaContext object (with .function_version). Returns: A dict mapping S3 object identifier [string] to a summary [dict] of file info and matched YARA rule information. """ result = {} binaries = [] # List of the BinaryInfo data. # Build the YaraAnalyzer now if we could not do it when this file was imported. global ANALYZER, NUM_YARA_RULES # pylint: disable=global-statement if not ANALYZER: ANALYZER = yara_analyzer.YaraAnalyzer(COMPILED_RULES_FILEPATH) NUM_YARA_RULES = ANALYZER.num_rules # The Lambda version must be an integer. try: lambda_version = int(lambda_context.function_version) except ValueError: lambda_version = -1 LOGGER.info('Processing %d record(s)', len(event_data['S3Objects'])) for s3_key in event_data['S3Objects']: LOGGER.info('Analyzing %s', s3_key) with binary_info.BinaryInfo(os.environ['S3_BUCKET_NAME'], s3_key, ANALYZER) as binary: result[binary.s3_identifier] = binary.summary() binaries.append(binary) if binary.yara_matches: LOGGER.warning('%s matched YARA rules: %s', binary, binary.matched_rule_ids) binary.save_matches_and_alert( lambda_version, os.environ['YARA_MATCHES_DYNAMO_TABLE_NAME'], os.environ['YARA_ALERTS_SNS_TOPIC_ARN']) else: LOGGER.info('%s did not match any YARA rules', binary) # Delete all of the SQS receipts (mark them as completed). analyzer_aws_lib.delete_sqs_messages(os.environ['SQS_QUEUE_URL'], event_data['SQSReceipts']) # Publish metrics. try: analyzer_aws_lib.put_metric_data(NUM_YARA_RULES, binaries) except BotoError: LOGGER.exception('Error saving metric data') return result
def test_analyze_no_matches(self): """Analyze returns empty list if no matches.""" # Setup a different YaraAnalyzer with an empty ruleset. yara_mocks.save_test_yara_rules('./empty.yara.rules', empty_rules_file=True) empty_analyzer = yara_analyzer.YaraAnalyzer('./empty.yara.rules') self.assertEqual([], empty_analyzer.analyze('/target.exe'))
def setUp(self): """For each test, build a new YaraAnalyzer.""" self.setUpPyfakefs() yara_mocks.save_test_yara_rules('./all.yara.rules') self._analyzer = yara_analyzer.YaraAnalyzer('./all.yara.rules') # Write target file. self.fs.CreateFile( './target.exe', contents='This is definitely not an evil file. ^_^\n')
def test_analyze_no_matches(self): """Analyze returns empty list if no matches.""" # Setup a different YaraAnalyzer with an empty ruleset. yara_mocks.save_test_yara_rules('./empty.yara.rules', empty_rules_file=True) with mock.patch.object(yara_analyzer.yara, 'load', side_effect=yara_mocks.mock_yara_load): empty_analyzer = yara_analyzer.YaraAnalyzer('./empty.yara.rules') self.assertEqual([], empty_analyzer.analyze('/target.exe'))
def setUp(self): """For each test, build a new YaraAnalyzer.""" self.setUpPyfakefs() yara_mocks.save_test_yara_rules('./all.yara.rules') with mock.patch.object(yara_analyzer.yara, 'load', side_effect=yara_mocks.mock_yara_load): self._analyzer = yara_analyzer.YaraAnalyzer('./all.yara.rules') # Write target file. self.fs.CreateFile( './target.exe', contents='This is definitely not an evil file. ^_^\n')
def setUp(self): """For each test, build a new YaraAnalyzer.""" self.setUpPyfakefs() with mock.patch.object(subprocess, 'Popen', side_effect=thor_mocks.mock_thor_start): self._analyzer = yara_analyzer.YaraAnalyzer() # Write target file. # pylint: disable=no-member self.fs.create_file( './target.exe', contents='This is definitely not an evil file. ^_^\n')
from botocore.exceptions import ClientError as BotoError if __package__: # Imported by unit tests or other external code. from lambda_functions.analyzer import analyzer_aws_lib, binary_info, yara_analyzer from lambda_functions.analyzer.common import COMPILED_RULES_FILEPATH, LOGGER else: import analyzer_aws_lib import binary_info from common import COMPILED_RULES_FILEPATH, LOGGER import yara_analyzer # Build the YaraAnalyzer from the compiled rules file at import time (i.e. once per container). # This saves 50-100+ ms per Lambda invocation, depending on the size of the rules file. ANALYZER = yara_analyzer.YaraAnalyzer(COMPILED_RULES_FILEPATH) # Due to a bug in yara-python, num_rules only be computed once. Thereafter, it will return 0. # So we have to compute this here since multiple invocations may share the same analyzer. NUM_YARA_RULES = ANALYZER.num_rules def analyze_lambda_handler(event_data: Dict[str, Any], lambda_context) -> Dict[str, Dict[str, Any]]: """Lambda function entry point. Args: event_data: [dict] of the form: { 'S3Objects': [...], # S3 object keys. 'SQSReceipts': [...] # SQS receipt handles (to be deleted after processing). } There can be any number of S3objects, but no more than 10 SQS receipts.
# YARA_MATCHES_DYNAMO_TABLE_NAME: Name of the Dynamo table which stores YARA match results. # YARA_ALERTS_SNS_TOPIC_ARN: ARN of the SNS topic which should be alerted on a YARA match. # Expects a binary YARA rules file to be at './compiled_yara_rules.bin' import json import os from typing import Any, Dict, Generator, Tuple import urllib.parse from botocore.exceptions import ClientError from lambda_functions.analyzer import analyzer_aws_lib, binary_info, yara_analyzer from lambda_functions.analyzer.common import LOGGER # Build the YaraAnalyzer from the compiled rules file at import time (i.e. once per container). # This saves 50-100+ ms per Lambda invocation, depending on the size of the rules file. ANALYZER = yara_analyzer.YaraAnalyzer() # Due to a bug in yara-python, num_rules only be computed once. Thereafter, it will return 0. # So we have to compute this here since multiple invocations may share the same analyzer. NUM_YARA_RULES = ANALYZER.num_rules def _objects_to_analyze( event: Dict[str, Any]) -> Generator[Tuple[str, str], None, None]: """Parse the invocation event into a list of objects to analyze. Args: event: Invocation event (SQS message whose message body is an S3 event notification) Yields: (bucket_name, object_key) string tuples to analyze """
def init(): global analyzer compile_rules.compile_rules(COMPILED_RULES_FILENAME) analyzer = yara_analyzer.YaraAnalyzer(COMPILED_RULES_FILENAME) return ("OK", 200)