From d3407351afea0d6e342d64ee49aaf34e60007b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20B=C3=BClow?= Date: Fri, 13 Jun 2025 13:03:52 +0200 Subject: [PATCH] capa scanner start --- scanners/capa/Dockerfile | 24 ++++++++++++++++++++++ scanners/capa/app.py | 16 +++++++++++++++ scanners/capa/config.py | 12 +++++++++++ scanners/capa/requirements.txt | 3 +++ scanners/capa/routes/__init__.py | 0 scanners/capa/routes/capa.py | 31 +++++++++++++++++++++++++++++ scanners/capa/utils/__init__.py | 0 scanners/capa/utils/file_handler.py | 14 +++++++++++++ scanners/ole/app.py | 2 ++ 9 files changed, 102 insertions(+) create mode 100644 scanners/capa/Dockerfile create mode 100644 scanners/capa/app.py create mode 100644 scanners/capa/config.py create mode 100644 scanners/capa/requirements.txt create mode 100644 scanners/capa/routes/__init__.py create mode 100644 scanners/capa/routes/capa.py create mode 100644 scanners/capa/utils/__init__.py create mode 100644 scanners/capa/utils/file_handler.py diff --git a/scanners/capa/Dockerfile b/scanners/capa/Dockerfile new file mode 100644 index 0000000..338a30e --- /dev/null +++ b/scanners/capa/Dockerfile @@ -0,0 +1,24 @@ +FROM python:3-alpine + +# Set environment variables (these can be overridden in `docker-compose.yml` or `docker run`) +ENV FILE_DIRECTORY="/mnt/storage/files/" +ENV HOST="127.0.0.1" +ENV PORT="5001" +ENV DEBUG="True" +ENV WORKERS="4" + +# Expose the port Flask will run on +EXPOSE 5000 + +WORKDIR /opt/capa +COPY . . + +RUN apk --no-cache add wget unzip +RUN pip install --no-cache --upgrade pip +RUN pip install --no-cache -r requirements.txt +RUN wget https://github.com/mandiant/capa-rules/archive/refs/tags/v4.0.0.zip +RUN unzip v4.0.0.zip + +# Start the Flask app +#CMD ["python", "app.py"] +CMD ["sh", "-c", "gunicorn -w $WORKERS -b $HOST:$PORT app:app"] diff --git a/scanners/capa/app.py b/scanners/capa/app.py new file mode 100644 index 0000000..e4063dc --- /dev/null +++ b/scanners/capa/app.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +from flask import Flask +from routes.capa import capa_bp +from config import Config + +app = Flask(__name__) + +# Apply config settings +app.config.from_object(Config) +# Register Blueprints (Modules) +app.register_blueprint(oleid_bp, url_prefix='/oleid') + + +if __name__ == '__main__': + app.run(host=Config.HOST, port=Config.PORT, debug=Config.DEBUG) diff --git a/scanners/capa/config.py b/scanners/capa/config.py new file mode 100644 index 0000000..1cffe4f --- /dev/null +++ b/scanners/capa/config.py @@ -0,0 +1,12 @@ +import os + +class Config: + # Read values from environment variables or use defaults + FILE_DIRECTORY = os.environ.get("FILE_DIRECTORY", "/mnt/storage/files") + HOST = os.environ.get("HOST", "127.0.0.1") + PORT = int(os.environ.get("PORT", 5000)) + DEBUG = os.environ.get("DEBUG", "False").lower() in ("true", "1") + +# Ensure upload directory exists +if not os.path.exists(Config.FILE_DIRECTORY): + os.makedirs(Config.FILE_DIRECTORY) diff --git a/scanners/capa/requirements.txt b/scanners/capa/requirements.txt new file mode 100644 index 0000000..9ccccd7 --- /dev/null +++ b/scanners/capa/requirements.txt @@ -0,0 +1,3 @@ +gunicorn +flask +flare-capa diff --git a/scanners/capa/routes/__init__.py b/scanners/capa/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scanners/capa/routes/capa.py b/scanners/capa/routes/capa.py new file mode 100644 index 0000000..d8bc386 --- /dev/null +++ b/scanners/capa/routes/capa.py @@ -0,0 +1,31 @@ +from logging import log +import logging +from flask import Blueprint, request, jsonify, abort +from os import path +import config + +capa_bp = Blueprint('capa', __name__) + +@capa_bp.route('/analyze', methods=['GET']) +def analyze_mraptor(): + file = request.args.get('file', '') + if file == '': + abort(400) + filepath = path.join(config.Config.FILE_DIRECTORY, file) + # Analyze with olevba + vbaparser = olevba.VBA_Parser(filepath) + if vbaparser.detect_vba_macros(): + vba_code = '' + try: + vba_code = vbaparser.get_vba_code_all_modules() + except Exception as e: + logging.error(e) + abort(500) + raptor = mraptor.MacroRaptor(vba_code) + raptor.scan() + if raptor.suspicious: + return jsonify({'filename': file, 'result': mraptor.Result_Suspicious, 'flags': raptor.get_flags(), 'matches': raptor.matches}) + else: + return jsonify({'filename': file, 'result': mraptor.Result_MacroOK, 'flags': raptor.get_flags(), 'matches': raptor.matches}) + else: + return jsonify({'filename': file, 'result': mraptor.Result_NoMacro}) diff --git a/scanners/capa/utils/__init__.py b/scanners/capa/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scanners/capa/utils/file_handler.py b/scanners/capa/utils/file_handler.py new file mode 100644 index 0000000..6d5c6f4 --- /dev/null +++ b/scanners/capa/utils/file_handler.py @@ -0,0 +1,14 @@ +import os +from flask import current_app + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'doc', 'xls', 'ppt', 'pps', 'bin'} + +def save_file(file): + filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], file.filename) + file.save(filepath) + return filepath + +def delete_file(filepath): + os.remove(filepath) + diff --git a/scanners/ole/app.py b/scanners/ole/app.py index 90e14c3..e9eaf4c 100644 --- a/scanners/ole/app.py +++ b/scanners/ole/app.py @@ -3,6 +3,7 @@ from flask import Flask from routes.oleid import oleid_bp from routes.olevba import olevba_bp +from routes.mraptor import mraptor_bp from config import Config app = Flask(__name__) @@ -13,6 +14,7 @@ app.config.from_object(Config) app.register_blueprint(oleid_bp, url_prefix='/oleid') app.register_blueprint(olevba_bp, url_prefix='/olevba') +app.register_blueprint(mraptor_bp, url_prefix='/mraptor') if __name__ == '__main__': app.run(host=Config.HOST, port=Config.PORT, debug=Config.DEBUG)