diff --git a/scanners/capa/routes/capa.py b/scanners/capa/routes/capa.py index 6b96ed6..30eb627 100644 --- a/scanners/capa/routes/capa.py +++ b/scanners/capa/routes/capa.py @@ -1,4 +1,5 @@ from flask import Blueprint, request, abort +from flask.json import jsonify from werkzeug.utils import secure_filename import capa.main import capa.rules @@ -11,9 +12,13 @@ from pathlib import Path import config import json import os - +import capa.render.utils as rutils +import capa.render.result_document as rd +import collections +from capa.render.default import find_subrule_matches capa_bp = Blueprint('capa', __name__) +# This function is essentially a hacked up version of https://github.com/mandiant/capa/blob/master/capa/render/default.py @capa_bp.route('/analyze', methods=['GET']) def analyze_capa(): file = secure_filename(request.args.get('file', '')) @@ -29,6 +34,82 @@ def analyze_capa(): capabilities = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) meta = capa.loader.collect_metadata([], filepath, FORMAT_AUTO, OS_AUTO, [capa.main.get_default_root()/ "rules"], extractor, capabilities) + doc = rd.ResultDocument.from_capa(meta, rules, capabilities.matches) + caps = {} + tactics = {} + maec = {} + objectives = {} + subrule_matches = find_subrule_matches(doc) + for rule in rutils.capability_rules(doc): + if rule.meta.name in subrule_matches: + # rules that are also matched by other rules should not get rendered by default. + # this cuts down on the amount of output while giving approx the same detail. + # see #224 + continue + + count = len(rule.matches) + if count == 1: + capability = rule.meta.name + else: + capability = rule.meta.name + f" ({count} matches)" + caps[capability] = rule.meta.namespace + for attack in rule.meta.attack: + tactics[attack.tactic] = attack.technique + attack.subtechnique + attack.id.strip("[").strip("]") + + maec_categories = { + "analysis_conclusion", + "analysis_conclusion_ov", + "malware_family", + "malware_category", + "malware_category_ov", + } + for rule in rutils.maec_rules(doc): + for maec_category in maec_categories: + maec_value = getattr(rule.meta.maec, maec_category, None) + if maec_value: + maec[maec_category] = maec_value + for rule in rutils.capability_rules(doc): + for mbc in rule.meta.mbc: + objectives[mbc.objective] = mbc.behavior + mbc.method + mbc.id.strip("[").strip("]") + + return jsonify(capabilities=caps, tactics=tactics, maec=maec, objectives=objectives) + + +@capa_bp.route('/json', methods=['GET']) +def analyze_capa_json(): + file = secure_filename(request.args.get('file', '')) + if file == '': + abort(400) + filepath = Path(path.join(config.Config.FILE_DIRECTORY, file)) + if not os.path.exists(filepath): + print(f"Error: File not found at '{filepath}'") + abort(400) + + rules = capa.rules.get_rules([Path(config.Config.RULES)]) + extractor = capa.loader.get_extractor(filepath, FORMAT_AUTO, OS_AUTO, capa.main.BACKEND_VIV, [], should_save_workspace=False, disable_progress=True) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) + + meta = capa.loader.collect_metadata([], filepath, FORMAT_AUTO, OS_AUTO, [capa.main.get_default_root()/ "rules"], extractor, capabilities) return json.loads(capa.render.json.render(meta=meta, rules=rules, capabilities=capabilities.matches)) + + + +@capa_bp.route('/default', methods=['GET']) +def analyze_capa_default(): + file = secure_filename(request.args.get('file', '')) + if file == '': + abort(400) + filepath = Path(path.join(config.Config.FILE_DIRECTORY, file)) + if not os.path.exists(filepath): + print(f"Error: File not found at '{filepath}'") + abort(400) + + rules = capa.rules.get_rules([Path(config.Config.RULES)]) + extractor = capa.loader.get_extractor(filepath, FORMAT_AUTO, OS_AUTO, capa.main.BACKEND_VIV, [], should_save_workspace=False, disable_progress=True) + capabilities = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) + + meta = capa.loader.collect_metadata([], filepath, FORMAT_AUTO, OS_AUTO, [capa.main.get_default_root()/ "rules"], extractor, capabilities) + + return capa.render.default.render(meta=meta, rules=rules, capabilities=capabilities.matches)