iflow-mcp_developermode-korea_reversecore-mcp 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/METADATA +543 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/RECORD +79 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/WHEEL +5 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/licenses/LICENSE +21 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/top_level.txt +1 -0
- reversecore_mcp/__init__.py +9 -0
- reversecore_mcp/core/__init__.py +78 -0
- reversecore_mcp/core/audit.py +101 -0
- reversecore_mcp/core/binary_cache.py +138 -0
- reversecore_mcp/core/command_spec.py +357 -0
- reversecore_mcp/core/config.py +432 -0
- reversecore_mcp/core/container.py +288 -0
- reversecore_mcp/core/decorators.py +152 -0
- reversecore_mcp/core/error_formatting.py +93 -0
- reversecore_mcp/core/error_handling.py +142 -0
- reversecore_mcp/core/evidence.py +229 -0
- reversecore_mcp/core/exceptions.py +296 -0
- reversecore_mcp/core/execution.py +240 -0
- reversecore_mcp/core/ghidra.py +642 -0
- reversecore_mcp/core/ghidra_helper.py +481 -0
- reversecore_mcp/core/ghidra_manager.py +234 -0
- reversecore_mcp/core/json_utils.py +131 -0
- reversecore_mcp/core/loader.py +73 -0
- reversecore_mcp/core/logging_config.py +206 -0
- reversecore_mcp/core/memory.py +721 -0
- reversecore_mcp/core/metrics.py +198 -0
- reversecore_mcp/core/mitre_mapper.py +365 -0
- reversecore_mcp/core/plugin.py +45 -0
- reversecore_mcp/core/r2_helpers.py +404 -0
- reversecore_mcp/core/r2_pool.py +403 -0
- reversecore_mcp/core/report_generator.py +268 -0
- reversecore_mcp/core/resilience.py +252 -0
- reversecore_mcp/core/resource_manager.py +169 -0
- reversecore_mcp/core/result.py +132 -0
- reversecore_mcp/core/security.py +213 -0
- reversecore_mcp/core/validators.py +238 -0
- reversecore_mcp/dashboard/__init__.py +221 -0
- reversecore_mcp/prompts/__init__.py +56 -0
- reversecore_mcp/prompts/common.py +24 -0
- reversecore_mcp/prompts/game.py +280 -0
- reversecore_mcp/prompts/malware.py +1219 -0
- reversecore_mcp/prompts/report.py +150 -0
- reversecore_mcp/prompts/security.py +136 -0
- reversecore_mcp/resources.py +329 -0
- reversecore_mcp/server.py +727 -0
- reversecore_mcp/tools/__init__.py +49 -0
- reversecore_mcp/tools/analysis/__init__.py +74 -0
- reversecore_mcp/tools/analysis/capa_tools.py +215 -0
- reversecore_mcp/tools/analysis/die_tools.py +180 -0
- reversecore_mcp/tools/analysis/diff_tools.py +643 -0
- reversecore_mcp/tools/analysis/lief_tools.py +272 -0
- reversecore_mcp/tools/analysis/signature_tools.py +591 -0
- reversecore_mcp/tools/analysis/static_analysis.py +479 -0
- reversecore_mcp/tools/common/__init__.py +58 -0
- reversecore_mcp/tools/common/file_operations.py +352 -0
- reversecore_mcp/tools/common/memory_tools.py +516 -0
- reversecore_mcp/tools/common/patch_explainer.py +230 -0
- reversecore_mcp/tools/common/server_tools.py +115 -0
- reversecore_mcp/tools/ghidra/__init__.py +19 -0
- reversecore_mcp/tools/ghidra/decompilation.py +975 -0
- reversecore_mcp/tools/ghidra/ghidra_tools.py +1052 -0
- reversecore_mcp/tools/malware/__init__.py +61 -0
- reversecore_mcp/tools/malware/adaptive_vaccine.py +579 -0
- reversecore_mcp/tools/malware/dormant_detector.py +756 -0
- reversecore_mcp/tools/malware/ioc_tools.py +228 -0
- reversecore_mcp/tools/malware/vulnerability_hunter.py +519 -0
- reversecore_mcp/tools/malware/yara_tools.py +214 -0
- reversecore_mcp/tools/patch_explainer.py +19 -0
- reversecore_mcp/tools/radare2/__init__.py +13 -0
- reversecore_mcp/tools/radare2/r2_analysis.py +972 -0
- reversecore_mcp/tools/radare2/r2_session.py +376 -0
- reversecore_mcp/tools/radare2/radare2_mcp_tools.py +1183 -0
- reversecore_mcp/tools/report/__init__.py +4 -0
- reversecore_mcp/tools/report/email.py +82 -0
- reversecore_mcp/tools/report/report_mcp_tools.py +344 -0
- reversecore_mcp/tools/report/report_tools.py +1076 -0
- reversecore_mcp/tools/report/session.py +194 -0
- reversecore_mcp/tools/report_tools.py +11 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Performance metrics collection for monitoring.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
from collections import defaultdict
|
|
9
|
+
from functools import wraps
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from reversecore_mcp.core.result import ToolError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MetricsCollector:
|
|
16
|
+
"""
|
|
17
|
+
Thread-safe performance metrics collector with bounded memory.
|
|
18
|
+
|
|
19
|
+
Uses threading.Lock to ensure safe concurrent access in multi-threaded
|
|
20
|
+
or async environments (e.g., FastMCP server with multiple tool calls).
|
|
21
|
+
|
|
22
|
+
Memory protection: Limits entries to MAX_ENTRIES to prevent unbounded growth.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# Maximum number of unique entries per category to prevent memory leaks
|
|
26
|
+
MAX_TOOL_ENTRIES = 500
|
|
27
|
+
MAX_CACHE_ENTRIES = 200
|
|
28
|
+
MAX_CIRCUIT_BREAKER_ENTRIES = 100
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self._lock = threading.Lock()
|
|
32
|
+
# Use regular dict instead of defaultdict for LRU control
|
|
33
|
+
self.tool_metrics: dict[str, dict[str, Any]] = {}
|
|
34
|
+
self.cache_metrics: dict[str, dict[str, int]] = {}
|
|
35
|
+
self.circuit_breaker_states: dict[str, str] = {}
|
|
36
|
+
|
|
37
|
+
def _get_default_tool_metrics(self) -> dict[str, Any]:
|
|
38
|
+
"""Create default metrics dict for a new tool."""
|
|
39
|
+
return {
|
|
40
|
+
"calls": 0,
|
|
41
|
+
"errors": 0,
|
|
42
|
+
"total_time": 0.0,
|
|
43
|
+
"avg_time": 0.0,
|
|
44
|
+
"max_time": 0.0,
|
|
45
|
+
"min_time": float("inf"),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def _get_default_cache_metrics(self) -> dict[str, int]:
|
|
49
|
+
"""Create default metrics dict for a new cache."""
|
|
50
|
+
return {"hits": 0, "misses": 0}
|
|
51
|
+
|
|
52
|
+
def _evict_oldest(self, d: dict, max_entries: int) -> None:
|
|
53
|
+
"""Evict oldest entries if dict exceeds max size (FIFO eviction)."""
|
|
54
|
+
while len(d) > max_entries:
|
|
55
|
+
oldest_key = next(iter(d))
|
|
56
|
+
del d[oldest_key]
|
|
57
|
+
|
|
58
|
+
def record_tool_execution(self, tool_name: str, execution_time: float, success: bool = True):
|
|
59
|
+
"""
|
|
60
|
+
Record metrics for a tool execution (thread-safe, bounded).
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
tool_name: Name of the tool
|
|
64
|
+
execution_time: Execution duration in seconds
|
|
65
|
+
success: Whether the execution succeeded
|
|
66
|
+
"""
|
|
67
|
+
with self._lock:
|
|
68
|
+
if tool_name not in self.tool_metrics:
|
|
69
|
+
self._evict_oldest(self.tool_metrics, self.MAX_TOOL_ENTRIES - 1)
|
|
70
|
+
self.tool_metrics[tool_name] = self._get_default_tool_metrics()
|
|
71
|
+
|
|
72
|
+
metrics = self.tool_metrics[tool_name]
|
|
73
|
+
metrics["calls"] += 1
|
|
74
|
+
|
|
75
|
+
if not success:
|
|
76
|
+
metrics["errors"] += 1
|
|
77
|
+
|
|
78
|
+
metrics["total_time"] += execution_time
|
|
79
|
+
metrics["avg_time"] = metrics["total_time"] / metrics["calls"]
|
|
80
|
+
metrics["max_time"] = max(metrics["max_time"], execution_time)
|
|
81
|
+
metrics["min_time"] = min(metrics["min_time"], execution_time)
|
|
82
|
+
|
|
83
|
+
def record_cache_hit(self, cache_name: str):
|
|
84
|
+
"""Record a cache hit (thread-safe, bounded)."""
|
|
85
|
+
with self._lock:
|
|
86
|
+
if cache_name not in self.cache_metrics:
|
|
87
|
+
self._evict_oldest(self.cache_metrics, self.MAX_CACHE_ENTRIES - 1)
|
|
88
|
+
self.cache_metrics[cache_name] = self._get_default_cache_metrics()
|
|
89
|
+
self.cache_metrics[cache_name]["hits"] += 1
|
|
90
|
+
|
|
91
|
+
def record_cache_miss(self, cache_name: str):
|
|
92
|
+
"""Record a cache miss (thread-safe, bounded)."""
|
|
93
|
+
with self._lock:
|
|
94
|
+
if cache_name not in self.cache_metrics:
|
|
95
|
+
self._evict_oldest(self.cache_metrics, self.MAX_CACHE_ENTRIES - 1)
|
|
96
|
+
self.cache_metrics[cache_name] = self._get_default_cache_metrics()
|
|
97
|
+
self.cache_metrics[cache_name]["misses"] += 1
|
|
98
|
+
|
|
99
|
+
def record_circuit_breaker_state(self, tool_name: str, state: str):
|
|
100
|
+
"""Record circuit breaker state change (thread-safe, bounded)."""
|
|
101
|
+
with self._lock:
|
|
102
|
+
if tool_name not in self.circuit_breaker_states:
|
|
103
|
+
self._evict_oldest(self.circuit_breaker_states, self.MAX_CIRCUIT_BREAKER_ENTRIES - 1)
|
|
104
|
+
self.circuit_breaker_states[tool_name] = state
|
|
105
|
+
|
|
106
|
+
def get_metrics(self) -> dict[str, Any]:
|
|
107
|
+
"""Get all collected metrics (thread-safe)."""
|
|
108
|
+
with self._lock:
|
|
109
|
+
return {
|
|
110
|
+
"tools": dict(self.tool_metrics),
|
|
111
|
+
"cache": dict(self.cache_metrics),
|
|
112
|
+
"circuit_breakers": dict(self.circuit_breaker_states),
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
def reset(self):
|
|
116
|
+
"""Reset all metrics (thread-safe)."""
|
|
117
|
+
with self._lock:
|
|
118
|
+
self.tool_metrics.clear()
|
|
119
|
+
self.cache_metrics.clear()
|
|
120
|
+
self.circuit_breaker_states.clear()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# Global metrics collector
|
|
124
|
+
metrics_collector = MetricsCollector()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _determine_success(result: Any) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Determine if a tool execution result indicates success.
|
|
130
|
+
|
|
131
|
+
This helper function consolidates the success determination logic
|
|
132
|
+
used in both sync and async wrappers, reducing code duplication.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
result: The result returned by the tool function
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
True if the result indicates success, False otherwise
|
|
139
|
+
"""
|
|
140
|
+
if isinstance(result, ToolError):
|
|
141
|
+
return False
|
|
142
|
+
if hasattr(result, "status"):
|
|
143
|
+
return result.status == "success"
|
|
144
|
+
if isinstance(result, dict) and "status" in result:
|
|
145
|
+
return result["status"] == "success"
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def track_metrics(tool_name: str):
|
|
150
|
+
"""
|
|
151
|
+
Decorator to track tool execution metrics.
|
|
152
|
+
|
|
153
|
+
Supports both synchronous and asynchronous functions.
|
|
154
|
+
Automatically detects function type using inspect.iscoroutinefunction().
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def decorator(func):
|
|
158
|
+
# Check if function is async
|
|
159
|
+
if inspect.iscoroutinefunction(func):
|
|
160
|
+
|
|
161
|
+
@wraps(func)
|
|
162
|
+
async def async_wrapper(*args, **kwargs):
|
|
163
|
+
start_time = time.time()
|
|
164
|
+
success = True
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
result = await func(*args, **kwargs)
|
|
168
|
+
success = _determine_success(result)
|
|
169
|
+
return result
|
|
170
|
+
except Exception:
|
|
171
|
+
success = False
|
|
172
|
+
raise
|
|
173
|
+
finally:
|
|
174
|
+
execution_time = time.time() - start_time
|
|
175
|
+
metrics_collector.record_tool_execution(tool_name, execution_time, success)
|
|
176
|
+
|
|
177
|
+
return async_wrapper
|
|
178
|
+
else:
|
|
179
|
+
|
|
180
|
+
@wraps(func)
|
|
181
|
+
def sync_wrapper(*args, **kwargs):
|
|
182
|
+
start_time = time.time()
|
|
183
|
+
success = True
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
result = func(*args, **kwargs)
|
|
187
|
+
success = _determine_success(result)
|
|
188
|
+
return result
|
|
189
|
+
except Exception:
|
|
190
|
+
success = False
|
|
191
|
+
raise
|
|
192
|
+
finally:
|
|
193
|
+
execution_time = time.time() - start_time
|
|
194
|
+
metrics_collector.record_tool_execution(tool_name, execution_time, success)
|
|
195
|
+
|
|
196
|
+
return sync_wrapper
|
|
197
|
+
|
|
198
|
+
return decorator
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MITRE ATT&CK Mapping Engine.
|
|
3
|
+
|
|
4
|
+
This module provides automated MITRE ATT&CK technique mapping based on
|
|
5
|
+
observable evidence from binary analysis, with confidence-based scoring.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Any, Optional
|
|
10
|
+
|
|
11
|
+
from reversecore_mcp.core.evidence import MITREConfidence, MITRETechnique, Evidence
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class MappingRule:
|
|
16
|
+
"""A rule for mapping indicators to MITRE techniques."""
|
|
17
|
+
technique_id: str
|
|
18
|
+
technique_name: str
|
|
19
|
+
tactic: str
|
|
20
|
+
indicators: list[str] # API names, strings, behaviors to look for
|
|
21
|
+
min_indicators: int = 1 # Minimum indicators required
|
|
22
|
+
confidence_boost_per_indicator: float = 0.1
|
|
23
|
+
base_confidence: MITREConfidence = MITREConfidence.MEDIUM
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# =============================================================================
|
|
27
|
+
# MITRE ATT&CK Mapping Rules Database
|
|
28
|
+
# =============================================================================
|
|
29
|
+
|
|
30
|
+
MITRE_MAPPING_RULES: list[MappingRule] = [
|
|
31
|
+
# Defense Evasion
|
|
32
|
+
MappingRule(
|
|
33
|
+
technique_id="T1055",
|
|
34
|
+
technique_name="Process Injection",
|
|
35
|
+
tactic="Defense Evasion",
|
|
36
|
+
indicators=["VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread",
|
|
37
|
+
"NtCreateThreadEx", "RtlCreateUserThread"],
|
|
38
|
+
min_indicators=2,
|
|
39
|
+
base_confidence=MITREConfidence.HIGH,
|
|
40
|
+
),
|
|
41
|
+
MappingRule(
|
|
42
|
+
technique_id="T1140",
|
|
43
|
+
technique_name="Deobfuscate/Decode Files or Information",
|
|
44
|
+
tactic="Defense Evasion",
|
|
45
|
+
indicators=["CryptDecrypt", "CryptStringToBinary", "Base64", "XOR"],
|
|
46
|
+
min_indicators=1,
|
|
47
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
48
|
+
),
|
|
49
|
+
MappingRule(
|
|
50
|
+
technique_id="T1112",
|
|
51
|
+
technique_name="Modify Registry",
|
|
52
|
+
tactic="Defense Evasion",
|
|
53
|
+
indicators=["RegSetValueEx", "RegCreateKey", "RegOpenKey", "RegDeleteValue"],
|
|
54
|
+
min_indicators=1,
|
|
55
|
+
base_confidence=MITREConfidence.HIGH,
|
|
56
|
+
),
|
|
57
|
+
|
|
58
|
+
# Persistence
|
|
59
|
+
MappingRule(
|
|
60
|
+
technique_id="T1543.003",
|
|
61
|
+
technique_name="Create or Modify System Process: Windows Service",
|
|
62
|
+
tactic="Persistence",
|
|
63
|
+
indicators=["CreateService", "OpenService", "StartService", "ChangeServiceConfig"],
|
|
64
|
+
min_indicators=2,
|
|
65
|
+
base_confidence=MITREConfidence.HIGH,
|
|
66
|
+
),
|
|
67
|
+
MappingRule(
|
|
68
|
+
technique_id="T1547.001",
|
|
69
|
+
technique_name="Boot or Logon Autostart Execution: Registry Run Keys",
|
|
70
|
+
tactic="Persistence",
|
|
71
|
+
indicators=["\\Run", "\\RunOnce", "CurrentVersion\\Run", "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"],
|
|
72
|
+
min_indicators=1,
|
|
73
|
+
base_confidence=MITREConfidence.HIGH,
|
|
74
|
+
),
|
|
75
|
+
MappingRule(
|
|
76
|
+
technique_id="T1053.005",
|
|
77
|
+
technique_name="Scheduled Task/Job: Scheduled Task",
|
|
78
|
+
tactic="Persistence",
|
|
79
|
+
indicators=["schtasks", "at.exe", "TaskScheduler", "ITaskService"],
|
|
80
|
+
min_indicators=1,
|
|
81
|
+
base_confidence=MITREConfidence.HIGH,
|
|
82
|
+
),
|
|
83
|
+
|
|
84
|
+
# Discovery
|
|
85
|
+
MappingRule(
|
|
86
|
+
technique_id="T1082",
|
|
87
|
+
technique_name="System Information Discovery",
|
|
88
|
+
tactic="Discovery",
|
|
89
|
+
indicators=["GetComputerName", "GetSystemInfo", "GetVersionEx", "GetNativeSystemInfo"],
|
|
90
|
+
min_indicators=2,
|
|
91
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
92
|
+
),
|
|
93
|
+
MappingRule(
|
|
94
|
+
technique_id="T1083",
|
|
95
|
+
technique_name="File and Directory Discovery",
|
|
96
|
+
tactic="Discovery",
|
|
97
|
+
indicators=["FindFirstFile", "FindNextFile", "GetFileAttributes", "PathFileExists"],
|
|
98
|
+
min_indicators=2,
|
|
99
|
+
base_confidence=MITREConfidence.LOW,
|
|
100
|
+
),
|
|
101
|
+
MappingRule(
|
|
102
|
+
technique_id="T1057",
|
|
103
|
+
technique_name="Process Discovery",
|
|
104
|
+
tactic="Discovery",
|
|
105
|
+
indicators=["CreateToolhelp32Snapshot", "Process32First", "Process32Next", "EnumProcesses"],
|
|
106
|
+
min_indicators=2,
|
|
107
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
108
|
+
),
|
|
109
|
+
|
|
110
|
+
# Impact
|
|
111
|
+
MappingRule(
|
|
112
|
+
technique_id="T1486",
|
|
113
|
+
technique_name="Data Encrypted for Impact",
|
|
114
|
+
tactic="Impact",
|
|
115
|
+
indicators=["CryptEncrypt", "CryptGenKey", "CryptDeriveKey", "CryptAcquireContext",
|
|
116
|
+
".encrypted", ".locked", "bitcoin", "ransom", "decrypt"],
|
|
117
|
+
min_indicators=3,
|
|
118
|
+
base_confidence=MITREConfidence.HIGH,
|
|
119
|
+
),
|
|
120
|
+
MappingRule(
|
|
121
|
+
technique_id="T1485",
|
|
122
|
+
technique_name="Data Destruction",
|
|
123
|
+
tactic="Impact",
|
|
124
|
+
indicators=["DeleteFile", "SHFileOperation", "cmd /c del", "wipe", "shred"],
|
|
125
|
+
min_indicators=2,
|
|
126
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
127
|
+
),
|
|
128
|
+
MappingRule(
|
|
129
|
+
technique_id="T1489",
|
|
130
|
+
technique_name="Service Stop",
|
|
131
|
+
tactic="Impact",
|
|
132
|
+
indicators=["ControlService", "SERVICE_CONTROL_STOP", "net stop", "sc stop"],
|
|
133
|
+
min_indicators=2,
|
|
134
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
135
|
+
),
|
|
136
|
+
|
|
137
|
+
# Command and Control
|
|
138
|
+
MappingRule(
|
|
139
|
+
technique_id="T1071.001",
|
|
140
|
+
technique_name="Application Layer Protocol: Web Protocols",
|
|
141
|
+
tactic="Command and Control",
|
|
142
|
+
indicators=["InternetOpen", "HttpOpenRequest", "InternetConnect", "WinHttpOpen"],
|
|
143
|
+
min_indicators=2,
|
|
144
|
+
base_confidence=MITREConfidence.HIGH,
|
|
145
|
+
),
|
|
146
|
+
MappingRule(
|
|
147
|
+
technique_id="T1095",
|
|
148
|
+
technique_name="Non-Application Layer Protocol",
|
|
149
|
+
tactic="Command and Control",
|
|
150
|
+
indicators=["socket", "connect", "send", "recv", "WSASocket"],
|
|
151
|
+
min_indicators=2,
|
|
152
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
153
|
+
),
|
|
154
|
+
|
|
155
|
+
# Execution
|
|
156
|
+
MappingRule(
|
|
157
|
+
technique_id="T1059.001",
|
|
158
|
+
technique_name="Command and Scripting Interpreter: PowerShell",
|
|
159
|
+
tactic="Execution",
|
|
160
|
+
indicators=["powershell", "-enc", "-ExecutionPolicy", "Invoke-Expression"],
|
|
161
|
+
min_indicators=1,
|
|
162
|
+
base_confidence=MITREConfidence.HIGH,
|
|
163
|
+
),
|
|
164
|
+
MappingRule(
|
|
165
|
+
technique_id="T1059.003",
|
|
166
|
+
technique_name="Command and Scripting Interpreter: Windows Command Shell",
|
|
167
|
+
tactic="Execution",
|
|
168
|
+
indicators=["cmd.exe", "cmd /c", "cmd /k", "CreateProcess"],
|
|
169
|
+
min_indicators=2,
|
|
170
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
171
|
+
),
|
|
172
|
+
MappingRule(
|
|
173
|
+
technique_id="T1106",
|
|
174
|
+
technique_name="Native API",
|
|
175
|
+
tactic="Execution",
|
|
176
|
+
indicators=["NtCreateProcess", "NtAllocateVirtualMemory", "NtProtectVirtualMemory", "LdrLoadDll"],
|
|
177
|
+
min_indicators=2,
|
|
178
|
+
base_confidence=MITREConfidence.HIGH,
|
|
179
|
+
),
|
|
180
|
+
|
|
181
|
+
# Lateral Movement
|
|
182
|
+
MappingRule(
|
|
183
|
+
technique_id="T1570",
|
|
184
|
+
technique_name="Lateral Tool Transfer",
|
|
185
|
+
tactic="Lateral Movement",
|
|
186
|
+
indicators=["\\\\", "ADMIN$", "C$", "IPC$", "NetShareEnum", "WNetAddConnection"],
|
|
187
|
+
min_indicators=2,
|
|
188
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
189
|
+
),
|
|
190
|
+
MappingRule(
|
|
191
|
+
technique_id="T1021.002",
|
|
192
|
+
technique_name="Remote Services: SMB/Windows Admin Shares",
|
|
193
|
+
tactic="Lateral Movement",
|
|
194
|
+
indicators=["\\\\pipe\\", "SMB", "port 445", "NetUseAdd"],
|
|
195
|
+
min_indicators=2,
|
|
196
|
+
base_confidence=MITREConfidence.MEDIUM,
|
|
197
|
+
),
|
|
198
|
+
|
|
199
|
+
# Collection
|
|
200
|
+
MappingRule(
|
|
201
|
+
technique_id="T1560",
|
|
202
|
+
technique_name="Archive Collected Data",
|
|
203
|
+
tactic="Collection",
|
|
204
|
+
indicators=["zip", "7z", "rar", "tar", "compress", "CreateZipFile"],
|
|
205
|
+
min_indicators=1,
|
|
206
|
+
base_confidence=MITREConfidence.LOW,
|
|
207
|
+
),
|
|
208
|
+
MappingRule(
|
|
209
|
+
technique_id="T1005",
|
|
210
|
+
technique_name="Data from Local System",
|
|
211
|
+
tactic="Collection",
|
|
212
|
+
indicators=["ReadFile", "fread", "GetFileSize", ".doc", ".xls", ".pdf"],
|
|
213
|
+
min_indicators=3,
|
|
214
|
+
base_confidence=MITREConfidence.LOW,
|
|
215
|
+
),
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class MITREMapper:
|
|
220
|
+
"""MITRE ATT&CK mapping engine with confidence-based scoring."""
|
|
221
|
+
|
|
222
|
+
def __init__(self, rules: list[MappingRule] = None):
|
|
223
|
+
self.rules = rules or MITRE_MAPPING_RULES
|
|
224
|
+
|
|
225
|
+
def map_indicators(
|
|
226
|
+
self,
|
|
227
|
+
imports: list[str],
|
|
228
|
+
strings: list[str],
|
|
229
|
+
behaviors: list[str] = None,
|
|
230
|
+
) -> list[MITRETechnique]:
|
|
231
|
+
"""
|
|
232
|
+
Map observed indicators to MITRE ATT&CK techniques.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
imports: List of imported API functions
|
|
236
|
+
strings: List of strings found in binary
|
|
237
|
+
behaviors: List of observed behaviors (optional)
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
List of MITRETechnique with confidence levels
|
|
241
|
+
"""
|
|
242
|
+
all_indicators = set()
|
|
243
|
+
|
|
244
|
+
# Normalize and collect all indicators
|
|
245
|
+
for imp in imports:
|
|
246
|
+
all_indicators.add(imp.lower())
|
|
247
|
+
for s in strings:
|
|
248
|
+
all_indicators.add(s.lower())
|
|
249
|
+
if behaviors:
|
|
250
|
+
for b in behaviors:
|
|
251
|
+
all_indicators.add(b.lower())
|
|
252
|
+
|
|
253
|
+
results = []
|
|
254
|
+
|
|
255
|
+
for rule in self.rules:
|
|
256
|
+
matched_indicators = []
|
|
257
|
+
|
|
258
|
+
for indicator in rule.indicators:
|
|
259
|
+
indicator_lower = indicator.lower()
|
|
260
|
+
# Check if any observed indicator contains this pattern
|
|
261
|
+
for observed in all_indicators:
|
|
262
|
+
if indicator_lower in observed or observed in indicator_lower:
|
|
263
|
+
matched_indicators.append(indicator)
|
|
264
|
+
break
|
|
265
|
+
|
|
266
|
+
# Check if minimum indicators matched
|
|
267
|
+
if len(matched_indicators) >= rule.min_indicators:
|
|
268
|
+
# Calculate confidence based on matches
|
|
269
|
+
match_ratio = len(matched_indicators) / len(rule.indicators)
|
|
270
|
+
|
|
271
|
+
if match_ratio >= 0.8:
|
|
272
|
+
confidence = MITREConfidence.CONFIRMED
|
|
273
|
+
elif match_ratio >= 0.5 or rule.base_confidence == MITREConfidence.HIGH:
|
|
274
|
+
confidence = MITREConfidence.HIGH
|
|
275
|
+
elif match_ratio >= 0.3:
|
|
276
|
+
confidence = MITREConfidence.MEDIUM
|
|
277
|
+
else:
|
|
278
|
+
confidence = MITREConfidence.LOW
|
|
279
|
+
|
|
280
|
+
# Build evidence from matched indicators
|
|
281
|
+
evidence = [
|
|
282
|
+
Evidence(
|
|
283
|
+
source="indicator_match",
|
|
284
|
+
location="imports/strings",
|
|
285
|
+
description=f"Matched indicator: {ind}",
|
|
286
|
+
)
|
|
287
|
+
for ind in matched_indicators
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
technique = MITRETechnique(
|
|
291
|
+
technique_id=rule.technique_id,
|
|
292
|
+
technique_name=rule.technique_name,
|
|
293
|
+
tactic=rule.tactic,
|
|
294
|
+
confidence=confidence,
|
|
295
|
+
evidence=evidence,
|
|
296
|
+
)
|
|
297
|
+
results.append(technique)
|
|
298
|
+
|
|
299
|
+
# Sort by confidence (CONFIRMED > HIGH > MEDIUM > LOW)
|
|
300
|
+
confidence_order = {
|
|
301
|
+
MITREConfidence.CONFIRMED: 0,
|
|
302
|
+
MITREConfidence.HIGH: 1,
|
|
303
|
+
MITREConfidence.MEDIUM: 2,
|
|
304
|
+
MITREConfidence.LOW: 3,
|
|
305
|
+
}
|
|
306
|
+
results.sort(key=lambda t: confidence_order[t.confidence])
|
|
307
|
+
|
|
308
|
+
return results
|
|
309
|
+
|
|
310
|
+
def generate_mitre_report(self, techniques: list[MITRETechnique]) -> str:
|
|
311
|
+
"""Generate a markdown MITRE mapping report."""
|
|
312
|
+
lines = [
|
|
313
|
+
"## MITRE ATT&CK Mapping",
|
|
314
|
+
"",
|
|
315
|
+
"> **Confidence Levels**: ✅ Confirmed | 🟢 High | 🟡 Medium | 🔴 Low",
|
|
316
|
+
"",
|
|
317
|
+
"| Technique ID | Name | Tactic | Confidence | Evidence Count |",
|
|
318
|
+
"|-------------|------|--------|------------|----------------|",
|
|
319
|
+
]
|
|
320
|
+
|
|
321
|
+
for t in techniques:
|
|
322
|
+
conf_symbol = {
|
|
323
|
+
"confirmed": "✅",
|
|
324
|
+
"high": "🟢",
|
|
325
|
+
"medium": "🟡",
|
|
326
|
+
"low": "🔴",
|
|
327
|
+
}[t.confidence.value]
|
|
328
|
+
|
|
329
|
+
lines.append(
|
|
330
|
+
f"| {t.technique_id} | {t.technique_name} | {t.tactic} | "
|
|
331
|
+
f"{conf_symbol} {t.confidence.value} | {len(t.evidence)} |"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
return "\n".join(lines)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# Singleton instance for convenience
|
|
338
|
+
_mapper: Optional[MITREMapper] = None
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def get_mitre_mapper() -> MITREMapper:
|
|
342
|
+
"""Get the global MITRE mapper instance."""
|
|
343
|
+
global _mapper
|
|
344
|
+
if _mapper is None:
|
|
345
|
+
_mapper = MITREMapper()
|
|
346
|
+
return _mapper
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def map_to_mitre(
|
|
350
|
+
imports: list[str],
|
|
351
|
+
strings: list[str],
|
|
352
|
+
behaviors: list[str] = None,
|
|
353
|
+
) -> list[MITRETechnique]:
|
|
354
|
+
"""
|
|
355
|
+
Quick helper to map indicators to MITRE techniques.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
imports: List of imported API functions
|
|
359
|
+
strings: List of strings found in binary
|
|
360
|
+
behaviors: List of observed behaviors (optional)
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
List of MITRETechnique with confidence levels
|
|
364
|
+
"""
|
|
365
|
+
return get_mitre_mapper().map_indicators(imports, strings, behaviors)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plugin interface definition for Reversecore MCP.
|
|
3
|
+
|
|
4
|
+
This module defines the contract that all plugins must adhere to.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Tool(BaseModel):
|
|
15
|
+
"""Wrapper for an MCP tool function."""
|
|
16
|
+
|
|
17
|
+
name: str
|
|
18
|
+
description: str
|
|
19
|
+
func: Callable
|
|
20
|
+
parameters: dict | None = None # Optional schema override
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Plugin(ABC):
|
|
24
|
+
"""Abstract base class for all plugins."""
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def name(self) -> str:
|
|
29
|
+
"""Return the unique name of the plugin."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def description(self) -> str:
|
|
34
|
+
"""Return a brief description of the plugin."""
|
|
35
|
+
return ""
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def register(self, mcp_server: Any) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Register tools with the MCP server.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
mcp_server: The FastMCP server instance
|
|
44
|
+
"""
|
|
45
|
+
pass
|