diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fcd647..0573f6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [v1.6.0] - 2025-01-26 +### Added +- New Static analyzer Stringnalyzer implementation + +### Fixed +- Refactoring health check implementation to use configuration file settings + ## [v1.5.1] - 2025-01-25 ### Added - Support for executing payloads with custom command-line arguments @@ -15,7 +22,7 @@ All notable changes to this project will be documented in this file. ## [v1.5.0] - 2025-01-11 ### Added -- LitterBox RedEdr Scanner implementation +- New Dynamic analyzer RedEdr Scanner implementation - Added LICENSE file ### Changed diff --git a/Config/config.yaml b/Config/config.yaml index c5dc3af..c4f918e 100644 --- a/Config/config.yaml +++ b/Config/config.yaml @@ -36,6 +36,12 @@ analysis: command: "{tool_path} -m -r -f {file_path}" timeout: 120 + stringnalyzer: + enabled: true + tool_path: ".\\Scanners\\Stringnalyzer\\Stringnalyzer.exe " + command: "{tool_path} --wide -f {file_path}" + timeout: 120 + dynamic: yara: enabled: true diff --git a/README.md b/README.md index c9e0b7b..3e8d20a 100644 --- a/README.md +++ b/README.md @@ -58,20 +58,20 @@ Features include: - Injection technique analysis - Sleep pattern monitoring - Collect Windows telemetry via ETW -- PE integrity verification ## Integrated Tools ### Static Analysis Suite - [YARA](https://github.com/elastic/protections-artifacts/tree/main/yara) - Pattern matching and signature detection - [CheckPlz](https://github.com/BlackSnufkin/CheckPlz) - AV detection testing +- [Stringnalyzer](https://github.com/BlackSnufkin/Rusty-Playground/Stringnalyzer) - Payload Strings analyzer (Note: May produce false positives) ### Dynamic Analysis Suite - [YARA](https://github.com/elastic/protections-artifacts/tree/main/yara) (memory scanning) - Runtime pattern detection - [PE-Sieve](https://github.com/hasherezade/pe-sieve) - Detecting and dumping in-memory malware implants and advanced process injection techniques - [Moneta](https://github.com/forrest-orr/moneta) - Usermode memory analysis tool to detect malware IOCs - [Patriot](https://github.com/BlackSnufkin/patriot) - Detecting various kinds of in-memory stealth techniques -- [RedEdr](https://github.com/dobin/RedEdr) - Collect Windows telemetry via ETW providers +- [RedEdr](https://github.com/dobin/RedEdr) - Collect Windows telemetry via ETW providers (Note: See issue #6) - [Hunt-Sleeping-Beacons](https://github.com/thefLink/Hunt-Sleeping-Beacons) - Beacon behavior analysis ## Web Endpoint Reference diff --git a/Scanners/Stringnalyzer/Stringnalyzer.exe b/Scanners/Stringnalyzer/Stringnalyzer.exe new file mode 100644 index 0000000..ea53316 Binary files /dev/null and b/Scanners/Stringnalyzer/Stringnalyzer.exe differ diff --git a/app/analyzers/manager.py b/app/analyzers/manager.py index 0c0961d..fa33b02 100644 --- a/app/analyzers/manager.py +++ b/app/analyzers/manager.py @@ -1,6 +1,5 @@ # app/analyzers/manager.py - import logging import subprocess import time @@ -12,6 +11,7 @@ from abc import ABC, abstractmethod # Import analyzers from .static.yara_analyzer import YaraStaticAnalyzer from .static.checkplz_analyzer import CheckPlzAnalyzer +from .static.stringnalyzer_analyzer import StringsAnalyzer from .dynamic.yara_analyzer import YaraDynamicAnalyzer from .dynamic.pe_sieve_analyzer import PESieveAnalyzer from .dynamic.moneta_analyzer import MonetaAnalyzer @@ -32,7 +32,8 @@ class AnalysisManager: # Define analyzer mappings STATIC_ANALYZERS = { 'yara': YaraStaticAnalyzer, - 'checkplz': CheckPlzAnalyzer + 'checkplz': CheckPlzAnalyzer, + 'stringnalyzer': StringsAnalyzer } DYNAMIC_ANALYZERS = { diff --git a/app/analyzers/static/stringnalyzer_analyzer.py b/app/analyzers/static/stringnalyzer_analyzer.py new file mode 100644 index 0000000..6a7d7e2 --- /dev/null +++ b/app/analyzers/static/stringnalyzer_analyzer.py @@ -0,0 +1,102 @@ +import subprocess +import json +import os +from .base import StaticAnalyzer + +class StringsAnalyzer(StaticAnalyzer): + def analyze(self, file_path): + """ + Analyzes a file using strings analysis tool specified in the config. + """ + try: + tool_config = self.config['analysis']['static']['stringnalyzer'] + command = tool_config['command'].format( + tool_path=os.path.abspath(tool_config['tool_path']), + file_path=os.path.abspath(file_path) + ) + + process = subprocess.Popen( + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + cwd=os.path.dirname(os.path.abspath(tool_config['tool_path'])) + ) + + stdout, stderr = process.communicate(timeout=tool_config.get('timeout', 300)) + + # Parse the JSON output + results = self._parse_output(stdout) + + self.results = { + 'status': 'completed' if process.returncode == 0 else 'failed', + 'scan_info': { + 'target': file_path, + 'tool': 'Stringnalyzer' + }, + 'findings': results, + 'errors': stderr if stderr else None + } + + except Exception as e: + self.results = { + 'status': 'error', + 'error': str(e) + } + + def _parse_output(self, output): + """ + Parse the strings tool JSON output into structured data. + Returns a dictionary containing the parsed results. + """ + try: + # Try to parse the JSON output + results = json.loads(output) + + # Ensure all expected fields are present, initialize if missing + default_fields = { + 'file_path': None, + 'total_strings': 0, + 'all_strings': [], + 'found_error_messages': [], + 'found_functions': [], + 'found_url': [], + 'found_dll': [], + 'found_ip': [], + 'found_path': [], + 'found_file': [], + 'found_commands': [], + 'found_suspicious_strings': [], + 'found_suspicious_functions': [], + 'found_network_indicators': [], + 'found_registry_keys': [], + 'found_interesting_strings': [], + 'found_file_operations': [], + 'found_emails': [], + 'found_domains': [] + } + + # Update default fields with actual results + for key in default_fields: + if key not in results: + results[key] = default_fields[key] + + return results + + except json.JSONDecodeError as e: + return { + 'error': f'Failed to parse JSON output: {str(e)}', + **default_fields + } + except Exception as e: + return { + 'error': f'Unexpected error parsing output: {str(e)}', + **default_fields + } + + def cleanup(self): + """ + Cleanup any temporary files or processes. + """ + pass \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index 7428ffe..337c4ab 100644 --- a/app/routes.py +++ b/app/routes.py @@ -724,7 +724,7 @@ def register_routes(app): analysis_config = config.get('analysis', {}) issues = [] - # Check upload folder accessibility + # Simple upload folder check upload_folder = upload_config.get('upload_folder') if not upload_folder: app.logger.warning("Upload folder path is not configured.") @@ -732,9 +732,6 @@ def register_routes(app): elif not os.path.isdir(upload_folder): app.logger.warning(f"Upload folder does not exist: {upload_folder}") issues.append(f"Upload folder does not exist: {upload_folder}") - elif not os.access(upload_folder, os.W_OK): - app.logger.warning(f"Upload folder is not writable: {upload_folder}") - issues.append(f"Upload folder is not writable: {upload_folder}") # Check static and dynamic analysis tools def check_analysis_tool(section, tool_name): @@ -746,29 +743,45 @@ def register_routes(app): issues.append(f"{tool_name}: tool path not configured") elif not os.path.isfile(tool_path): issues.append(f"{tool_name}: tool not found at {tool_path}") - elif not os.access(tool_path, os.X_OK): - issues.append(f"{tool_name}: tool not executable at {tool_path}") + rules_path = tool_config.get('rules_path') if rules_path and not os.path.isfile(rules_path): issues.append(f"{tool_name}: rules not found at {rules_path}") - for tool in ['yara', 'checkplz']: - check_analysis_tool(analysis_config.get('static', {}), tool) + # Get tools from config instead of hardcoding + static_section = analysis_config.get('static', {}) + dynamic_section = analysis_config.get('dynamic', {}) - for tool in ['yara', 'pe_sieve', 'moneta', 'patriot', 'hsb', 'rededr']: - check_analysis_tool(analysis_config.get('dynamic', {}), tool) + # Check all configured static tools + for tool_name in static_section.keys(): + check_analysis_tool(static_section, tool_name) + + # Check all configured dynamic tools + for tool_name in dynamic_section.keys(): + check_analysis_tool(dynamic_section, tool_name) + + # Get all enabled tools for configuration response + static_tools = { + tool: static_section.get(tool, {}).get('enabled', False) + for tool in static_section.keys() + } + + dynamic_tools = { + tool: dynamic_section.get(tool, {}).get('enabled', False) + for tool in dynamic_section.keys() + } status = 'ok' if not issues else 'degraded' app.logger.debug(f"Health check completed. Status: {status}") - + return jsonify({ 'status': status, 'timestamp': datetime.datetime.now().isoformat(), - 'upload_folder_accessible': os.path.isdir(upload_folder) and os.access(upload_folder, os.W_OK) if upload_folder else False, + 'upload_folder_accessible': os.path.isdir(upload_folder) if upload_folder else False, 'issues': issues, 'configuration': { - 'static_analysis': {tool: analysis_config.get('static', {}).get(tool, {}).get('enabled', False) for tool in ['yara', 'threatcheck']}, - 'dynamic_analysis': {tool: analysis_config.get('dynamic', {}).get(tool, {}).get('enabled', False) for tool in ['yara', 'pe_sieve', 'moneta']} + 'static_analysis': static_tools, + 'dynamic_analysis': dynamic_tools } }), 200 if status == 'ok' else 503 diff --git a/app/static/js/results.js b/app/static/js/results.js index b9f78a4..e88652e 100644 --- a/app/static/js/results.js +++ b/app/static/js/results.js @@ -250,6 +250,32 @@ function formatBytes(bytes) { } +function renderSection(title, items) { + if (!items || items.length === 0) return ''; + + const displayItems = items.slice(0, 25); + const remainingCount = items.length - 25; + + return ` +
${scanResults.hex_dump}
+ ${scanResults.hex_dump}
- Payload String Analyzer
+ + +