rust_threat_detector/
mitre_attack.rs

1//! MITRE ATT&CK framework pattern detection
2
3use regex::Regex;
4use serde::{Deserialize, Serialize};
5
6/// MITRE ATT&CK tactic
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub enum AttackTactic {
9    InitialAccess,
10    Execution,
11    Persistence,
12    PrivilegeEscalation,
13    DefenseEvasion,
14    CredentialAccess,
15    Discovery,
16    LateralMovement,
17    Collection,
18    Exfiltration,
19    Impact,
20}
21
22/// MITRE ATT&CK technique
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct AttackTechnique {
25    pub id: String,
26    pub name: String,
27    pub tactic: AttackTactic,
28    pub description: String,
29}
30
31/// Detection pattern
32#[derive(Debug, Clone)]
33pub struct DetectionPattern {
34    pub technique: AttackTechnique,
35    pub pattern: Regex,
36    pub severity: ThreatSeverity,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub enum ThreatSeverity {
41    Low,
42    Medium,
43    High,
44    Critical,
45}
46
47/// MITRE ATT&CK detector
48pub struct MitreAttackDetector {
49    patterns: Vec<DetectionPattern>,
50}
51
52impl MitreAttackDetector {
53    /// Create new detector with common patterns
54    pub fn new() -> Self {
55        let mut patterns = Vec::new();
56
57        // T1110 - Brute Force
58        patterns.push(DetectionPattern {
59            technique: AttackTechnique {
60                id: "T1110".to_string(),
61                name: "Brute Force".to_string(),
62                tactic: AttackTactic::CredentialAccess,
63                description: "Multiple failed authentication attempts".to_string(),
64            },
65            pattern: Regex::new(r"(?i)(failed|invalid|incorrect).*(login|auth|password)").unwrap(),
66            severity: ThreatSeverity::High,
67        });
68
69        // T1059 - Command and Scripting Interpreter
70        patterns.push(DetectionPattern {
71            technique: AttackTechnique {
72                id: "T1059".to_string(),
73                name: "Command and Scripting Interpreter".to_string(),
74                tactic: AttackTactic::Execution,
75                description: "Execution of commands or scripts".to_string(),
76            },
77            pattern: Regex::new(r"(?i)(cmd\.exe|powershell|bash|sh|python|perl)").unwrap(),
78            severity: ThreatSeverity::Medium,
79        });
80
81        // T1190 - Exploit Public-Facing Application
82        patterns.push(DetectionPattern {
83            technique: AttackTechnique {
84                id: "T1190".to_string(),
85                name: "Exploit Public-Facing Application".to_string(),
86                tactic: AttackTactic::InitialAccess,
87                description: "SQL injection or code injection attempts".to_string(),
88            },
89            pattern: Regex::new(r"(?i)(union\s+select|or\s+1\s*=\s*1|<script|eval\(|exec\()").unwrap(),
90            severity: ThreatSeverity::Critical,
91        });
92
93        // T1078 - Valid Accounts
94        patterns.push(DetectionPattern {
95            technique: AttackTechnique {
96                id: "T1078".to_string(),
97                name: "Valid Accounts".to_string(),
98                tactic: AttackTactic::InitialAccess,
99                description: "Use of valid accounts from unusual locations".to_string(),
100            },
101            pattern: Regex::new(r"(?i)(login|auth|signin).*(success|successful)").unwrap(),
102            severity: ThreatSeverity::Low,
103        });
104
105        // T1071 - Application Layer Protocol
106        patterns.push(DetectionPattern {
107            technique: AttackTechnique {
108                id: "T1071".to_string(),
109                name: "Application Layer Protocol".to_string(),
110                tactic: AttackTactic::InitialAccess,
111                description: "C2 communication over standard protocols".to_string(),
112            },
113            pattern: Regex::new(r"(?i)(http|https|ftp|dns|smtp).*(beacon|c2|command)").unwrap(),
114            severity: ThreatSeverity::High,
115        });
116
117        // T1003 - OS Credential Dumping
118        patterns.push(DetectionPattern {
119            technique: AttackTechnique {
120                id: "T1003".to_string(),
121                name: "OS Credential Dumping".to_string(),
122                tactic: AttackTactic::CredentialAccess,
123                description: "Attempts to dump credentials from OS".to_string(),
124            },
125            pattern: Regex::new(r"(?i)(mimikatz|lsass|sam|ntds|hashdump)").unwrap(),
126            severity: ThreatSeverity::Critical,
127        });
128
129        // T1057 - Process Discovery
130        patterns.push(DetectionPattern {
131            technique: AttackTechnique {
132                id: "T1057".to_string(),
133                name: "Process Discovery".to_string(),
134                tactic: AttackTactic::Discovery,
135                description: "Enumeration of running processes".to_string(),
136            },
137            pattern: Regex::new(r"(?i)(tasklist|ps\s|get-process)").unwrap(),
138            severity: ThreatSeverity::Low,
139        });
140
141        // T1083 - File and Directory Discovery
142        patterns.push(DetectionPattern {
143            technique: AttackTechnique {
144                id: "T1083".to_string(),
145                name: "File and Directory Discovery".to_string(),
146                tactic: AttackTactic::Discovery,
147                description: "Enumeration of files and directories".to_string(),
148            },
149            pattern: Regex::new(r"(?i)(dir\s|ls\s|find\s|tree\s)").unwrap(),
150            severity: ThreatSeverity::Low,
151        });
152
153        // T1486 - Data Encrypted for Impact
154        patterns.push(DetectionPattern {
155            technique: AttackTechnique {
156                id: "T1486".to_string(),
157                name: "Data Encrypted for Impact".to_string(),
158                tactic: AttackTactic::Impact,
159                description: "Ransomware encryption activity".to_string(),
160            },
161            pattern: Regex::new(r"(?i)(ransom|encrypt|crypto|\.locked|\.encrypted)").unwrap(),
162            severity: ThreatSeverity::Critical,
163        });
164
165        // T1041 - Exfiltration Over C2 Channel
166        patterns.push(DetectionPattern {
167            technique: AttackTechnique {
168                id: "T1041".to_string(),
169                name: "Exfiltration Over C2 Channel".to_string(),
170                tactic: AttackTactic::Exfiltration,
171                description: "Data exfiltration over command and control channel".to_string(),
172            },
173            pattern: Regex::new(r"(?i)(exfil|upload|post|send).*(data|file|document)").unwrap(),
174            severity: ThreatSeverity::High,
175        });
176
177        Self { patterns }
178    }
179
180    /// Detect threats in log message
181    pub fn detect(&self, message: &str) -> Vec<ThreatDetection> {
182        let mut detections = Vec::new();
183
184        for pattern in &self.patterns {
185            if pattern.pattern.is_match(message) {
186                detections.push(ThreatDetection {
187                    technique: pattern.technique.clone(),
188                    severity: pattern.severity.clone(),
189                    matched_text: message.to_string(),
190                    timestamp: chrono::Utc::now(),
191                });
192            }
193        }
194
195        detections
196    }
197
198    /// Get all supported techniques
199    pub fn get_techniques(&self) -> Vec<&AttackTechnique> {
200        self.patterns.iter().map(|p| &p.technique).collect()
201    }
202
203    /// Get techniques by tactic
204    pub fn get_techniques_by_tactic(&self, tactic: &AttackTactic) -> Vec<&AttackTechnique> {
205        self.patterns
206            .iter()
207            .filter(|p| &p.technique.tactic == tactic)
208            .map(|p| &p.technique)
209            .collect()
210    }
211}
212
213impl Default for MitreAttackDetector {
214    fn default() -> Self {
215        Self::new()
216    }
217}
218
219/// Threat detection result
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct ThreatDetection {
222    pub technique: AttackTechnique,
223    pub severity: ThreatSeverity,
224    pub matched_text: String,
225    pub timestamp: chrono::DateTime<chrono::Utc>,
226}
227
228impl ThreatDetection {
229    /// Generate alert message
230    pub fn to_alert_message(&self) -> String {
231        format!(
232            "[{:?}] MITRE ATT&CK {} ({}) detected: {}",
233            self.severity, self.technique.id, self.technique.name, self.matched_text
234        )
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    #[test]
243    fn test_brute_force_detection() {
244        let detector = MitreAttackDetector::new();
245        let message = "Failed login attempt for user admin";
246
247        let detections = detector.detect(message);
248        assert!(!detections.is_empty());
249        assert_eq!(detections[0].technique.id, "T1110");
250    }
251
252    #[test]
253    fn test_sql_injection_detection() {
254        let detector = MitreAttackDetector::new();
255        let message = "GET /api/users?id=1 OR 1=1";
256
257        let detections = detector.detect(message);
258        assert!(!detections.is_empty());
259        assert_eq!(detections[0].technique.id, "T1190");
260        assert_eq!(detections[0].severity, ThreatSeverity::Critical);
261    }
262
263    #[test]
264    fn test_credential_dumping_detection() {
265        let detector = MitreAttackDetector::new();
266        let message = "mimikatz.exe executed on WORKSTATION-01";
267
268        let detections = detector.detect(message);
269        assert!(!detections.is_empty());
270        assert_eq!(detections[0].technique.id, "T1003");
271    }
272
273    #[test]
274    fn test_ransomware_detection() {
275        let detector = MitreAttackDetector::new();
276        let message = "Files encrypted by ransomware, pay bitcoin to decrypt";
277
278        let detections = detector.detect(message);
279        assert!(!detections.is_empty());
280        assert_eq!(detections[0].technique.id, "T1486");
281        assert_eq!(detections[0].severity, ThreatSeverity::Critical);
282    }
283
284    #[test]
285    fn test_command_execution_detection() {
286        let detector = MitreAttackDetector::new();
287        let message = "powershell.exe -ExecutionPolicy Bypass -File malware.ps1";
288
289        let detections = detector.detect(message);
290        assert!(!detections.is_empty());
291        assert_eq!(detections[0].technique.id, "T1059");
292    }
293
294    #[test]
295    fn test_no_detection() {
296        let detector = MitreAttackDetector::new();
297        let message = "User logged out successfully";
298
299        let detections = detector.detect(message);
300        // Should detect valid account usage (T1078)
301        assert!(!detections.is_empty());
302    }
303
304    #[test]
305    fn test_get_techniques_by_tactic() {
306        let detector = MitreAttackDetector::new();
307        let credential_access = detector.get_techniques_by_tactic(&AttackTactic::CredentialAccess);
308
309        assert!(!credential_access.is_empty());
310        assert!(credential_access.iter().any(|t| t.id == "T1110"));
311    }
312
313    #[test]
314    fn test_alert_message_generation() {
315        let detection = ThreatDetection {
316            technique: AttackTechnique {
317                id: "T1110".to_string(),
318                name: "Brute Force".to_string(),
319                tactic: AttackTactic::CredentialAccess,
320                description: "Test".to_string(),
321            },
322            severity: ThreatSeverity::High,
323            matched_text: "Failed login".to_string(),
324            timestamp: chrono::Utc::now(),
325        };
326
327        let alert = detection.to_alert_message();
328        assert!(alert.contains("T1110"));
329        assert!(alert.contains("Brute Force"));
330        assert!(alert.contains("Failed login"));
331    }
332
333    #[test]
334    fn test_multiple_detections() {
335        let detector = MitreAttackDetector::new();
336        let message = "mimikatz failed login powershell";
337
338        let detections = detector.detect(message);
339        // Should match multiple patterns
340        assert!(detections.len() >= 2);
341    }
342}