132 lines
5.6 KiB
Python
132 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Content Classifier for Threat Intelligence RSS Bot
|
|
Extracts CVEs, threat actors, malware families, MITRE techniques, and IOCs.
|
|
"""
|
|
|
|
import re
|
|
from typing import Dict, List
|
|
from datetime import datetime, timezone
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ContentClassifier:
|
|
|
|
THREAT_ACTORS = {
|
|
'apt1', 'apt28', 'apt29', 'apt32', 'apt33', 'apt34', 'apt35', 'apt37', 'apt38', 'apt39', 'apt40', 'apt41',
|
|
'lazarus', 'kimsuky', 'andariel', 'fancy bear', 'cozy bear', 'sandworm',
|
|
'turla', 'equation group', 'carbanak', 'fin7', 'fin6', 'fin8',
|
|
'nobelium', 'hafnium', 'phosphorus', 'holmium', 'strontium',
|
|
'volt typhoon', 'flax typhoon', 'mustang panda', 'winnti',
|
|
'scattered spider', 'lapsus', 'lapsus$', 'unc2452', 'unc3944',
|
|
'ta505', 'ta577', 'ta558', 'gold southfield', 'gold dupont',
|
|
}
|
|
|
|
# Stored as display names; matched case-insensitively
|
|
MALWARE_FAMILIES = {
|
|
# Ransomware
|
|
'LockBit', 'REvil', 'BlackCat', 'ALPHV', 'Cl0p', 'Conti', 'DarkSide',
|
|
'Ryuk', 'BlackMatter', 'Akira', 'Black Basta', 'RansomHub', 'Rhysida',
|
|
'Medusa Locker', 'Cactus Ransomware', '8Base', 'Play Ransomware',
|
|
'Hunters International', 'Inc Ransom', 'Monti', 'Nokoyawa',
|
|
# C2 frameworks / RATs
|
|
'Cobalt Strike', 'Mimikatz', 'Sliver', 'Brute Ratel', 'Havoc',
|
|
'AsyncRAT', 'Remcos', 'njRAT', 'NanoCore', 'XWorm', 'QuasarRAT',
|
|
'DarkComet', 'NetWire', 'Metasploit',
|
|
# Loaders / droppers
|
|
'Emotet', 'TrickBot', 'QakBot', 'IcedID', 'BazarLoader', 'Dridex',
|
|
'GootLoader', 'BumbleBee', 'PikaBot', 'DarkGate',
|
|
# Stealers
|
|
'AgentTesla', 'FormBook', 'RedLine', 'Vidar', 'Raccoon', 'Lumma',
|
|
'Rhadamanthys', 'StealC', 'Meduza', 'Aurora',
|
|
# APT tooling
|
|
'PlugX', 'ShadowPad', 'Gh0stRAT', 'PoisonIvy',
|
|
}
|
|
|
|
MITRE_PATTERN = re.compile(r'T\d{4}(?:\.\d{3})?', re.IGNORECASE)
|
|
CVE_PATTERN = re.compile(r'CVE-\d{4}-\d{4,7}', re.IGNORECASE)
|
|
IP_PATTERN = re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b')
|
|
DOMAIN_PATTERN = re.compile(r'\b[a-z0-9]+(?:[\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}\b', re.IGNORECASE)
|
|
HASH_MD5_PATTERN = re.compile(r'\b[a-f0-9]{32}\b', re.IGNORECASE)
|
|
HASH_SHA1_PATTERN = re.compile(r'\b[a-f0-9]{40}\b', re.IGNORECASE)
|
|
HASH_SHA256_PATTERN = re.compile(r'\b[a-f0-9]{64}\b', re.IGNORECASE)
|
|
|
|
def extract_cves(self, text: str) -> List[str]:
|
|
return list(set(cve.upper() for cve in self.CVE_PATTERN.findall(text)))
|
|
|
|
def extract_mitre_techniques(self, text: str) -> List[str]:
|
|
return list(set(t.upper() for t in self.MITRE_PATTERN.findall(text)))
|
|
|
|
def extract_threat_actors(self, text: str) -> List[str]:
|
|
text_lower = text.lower()
|
|
return list(set(
|
|
actor.upper()
|
|
for actor in self.THREAT_ACTORS
|
|
if re.search(r'\b' + re.escape(actor) + r'\b', text_lower)
|
|
))
|
|
|
|
def extract_malware_families(self, text: str) -> List[str]:
|
|
text_lower = text.lower()
|
|
return list(set(
|
|
family
|
|
for family in self.MALWARE_FAMILIES
|
|
if re.search(r'\b' + re.escape(family.lower()) + r'\b', text_lower)
|
|
))
|
|
|
|
def extract_iocs(self, text: str) -> Dict[str, List[str]]:
|
|
iocs: Dict[str, List[str]] = {
|
|
'ips': [], 'domains': [], 'md5': [], 'sha1': [], 'sha256': []
|
|
}
|
|
for ip in self.IP_PATTERN.findall(text):
|
|
if all(0 <= int(o) <= 255 for o in ip.split('.')):
|
|
iocs['ips'].append(ip)
|
|
iocs['md5'] = self.HASH_MD5_PATTERN.findall(text)
|
|
iocs['sha1'] = self.HASH_SHA1_PATTERN.findall(text)
|
|
iocs['sha256'] = self.HASH_SHA256_PATTERN.findall(text)
|
|
iocs['domains'] = [m.group(0).lower() for m in self.DOMAIN_PATTERN.finditer(text)]
|
|
for key in iocs:
|
|
iocs[key] = list(set(iocs[key]))[:5]
|
|
return iocs
|
|
|
|
def classify_article(self, article: Dict) -> Dict:
|
|
combined_text = f"{article.get('title', '')} {article.get('description', '')}"
|
|
|
|
cves = self.extract_cves(combined_text)
|
|
mitre_techniques = self.extract_mitre_techniques(combined_text)
|
|
threat_actors = self.extract_threat_actors(combined_text)
|
|
malware_families = self.extract_malware_families(combined_text)
|
|
iocs = self.extract_iocs(combined_text)
|
|
|
|
article['cves'] = cves
|
|
article['mitre_techniques'] = mitre_techniques
|
|
article['threat_actors'] = threat_actors
|
|
article['malware_families'] = malware_families
|
|
article['iocs'] = iocs
|
|
|
|
logger.info(
|
|
f"Classified: {article.get('title', '')[:50]}... | "
|
|
f"CVEs: {len(cves)} | Actors: {len(threat_actors)} | Malware: {len(malware_families)}"
|
|
)
|
|
return article
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_article = {
|
|
'title': 'APT28 Deploys Cobalt Strike via CVE-2024-12345 Zero-Day in Apache Struts',
|
|
'description': 'Fancy Bear exploited a critical RCE flaw. LockBit ransomware also observed. '
|
|
'Hash: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
|
'source': 'watchTowr Labs',
|
|
'url': 'https://example.com',
|
|
'category': 'research',
|
|
'published': datetime.now(timezone.utc).isoformat(),
|
|
'published_human': 'test',
|
|
}
|
|
c = ContentClassifier()
|
|
result = c.classify_article(test_article)
|
|
print(f"CVEs: {result['cves']}")
|
|
print(f"Actors: {result['threat_actors']}")
|
|
print(f"Malware: {result['malware_families']}")
|
|
print(f"MITRE: {result['mitre_techniques']}")
|