Cleaner message format, add malware family extraction
This commit is contained in:
+36
-3
@@ -84,9 +84,31 @@ class ContentClassifier:
|
|||||||
'apt1', 'apt28', 'apt29', 'apt32', 'apt33', 'apt34', 'apt35', 'apt37', 'apt38', 'apt39', 'apt40', 'apt41',
|
'apt1', 'apt28', 'apt29', 'apt32', 'apt33', 'apt34', 'apt35', 'apt37', 'apt38', 'apt39', 'apt40', 'apt41',
|
||||||
'lazarus', 'kimsuky', 'andariel', 'fancy bear', 'cozy bear', 'sandworm',
|
'lazarus', 'kimsuky', 'andariel', 'fancy bear', 'cozy bear', 'sandworm',
|
||||||
'turla', 'equation group', 'carbanak', 'fin7', 'fin6', 'fin8',
|
'turla', 'equation group', 'carbanak', 'fin7', 'fin6', 'fin8',
|
||||||
'conti', 'lockbit', 'blackcat', 'alphv', 'cl0p', 'clop', 'revil', 'darkside',
|
|
||||||
'nobelium', 'hafnium', 'phosphorus', 'holmium', 'strontium',
|
'nobelium', 'hafnium', 'phosphorus', 'holmium', 'strontium',
|
||||||
'volt typhoon', 'flax typhoon', 'mustang panda', 'winnti'
|
'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', 'Cactus', 'Play', 'Royal', 'Hive', 'Maze', '8Base',
|
||||||
|
'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 ATT&CK technique pattern
|
# MITRE ATT&CK technique pattern
|
||||||
@@ -125,6 +147,15 @@ class ContentClassifier:
|
|||||||
found_actors.append(actor.upper())
|
found_actors.append(actor.upper())
|
||||||
return list(set(found_actors))
|
return list(set(found_actors))
|
||||||
|
|
||||||
|
def extract_malware_families(self, text: str) -> List[str]:
|
||||||
|
"""Extract known malware family / tool names from text"""
|
||||||
|
text_lower = text.lower()
|
||||||
|
found = []
|
||||||
|
for family in self.MALWARE_FAMILIES:
|
||||||
|
if family.lower() in text_lower:
|
||||||
|
found.append(family)
|
||||||
|
return list(set(found))
|
||||||
|
|
||||||
def extract_iocs(self, text: str) -> Dict[str, List[str]]:
|
def extract_iocs(self, text: str) -> Dict[str, List[str]]:
|
||||||
"""Extract Indicators of Compromise from text"""
|
"""Extract Indicators of Compromise from text"""
|
||||||
iocs = {
|
iocs = {
|
||||||
@@ -285,6 +316,7 @@ class ContentClassifier:
|
|||||||
cves = self.extract_cves(combined_text)
|
cves = self.extract_cves(combined_text)
|
||||||
mitre_techniques = self.extract_mitre_techniques(combined_text)
|
mitre_techniques = self.extract_mitre_techniques(combined_text)
|
||||||
threat_actors = self.extract_threat_actors(combined_text)
|
threat_actors = self.extract_threat_actors(combined_text)
|
||||||
|
malware_families = self.extract_malware_families(combined_text)
|
||||||
iocs = self.extract_iocs(combined_text)
|
iocs = self.extract_iocs(combined_text)
|
||||||
|
|
||||||
# Classify content type
|
# Classify content type
|
||||||
@@ -306,13 +338,14 @@ class ContentClassifier:
|
|||||||
article['cves'] = cves
|
article['cves'] = cves
|
||||||
article['mitre_techniques'] = mitre_techniques
|
article['mitre_techniques'] = mitre_techniques
|
||||||
article['threat_actors'] = threat_actors
|
article['threat_actors'] = threat_actors
|
||||||
|
article['malware_families'] = malware_families
|
||||||
article['iocs'] = iocs
|
article['iocs'] = iocs
|
||||||
|
|
||||||
# Log classification
|
# Log classification
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Classified: {article['title'][:50]}... | "
|
f"Classified: {article['title'][:50]}... | "
|
||||||
f"Score: {quality_score} | Severity: {severity} | "
|
f"Score: {quality_score} | Severity: {severity} | "
|
||||||
f"Types: {', '.join(classifications)} | CVEs: {len(cves)}"
|
f"CVEs: {len(cves)} | Actors: {len(threat_actors)} | Malware: {len(malware_families)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return article
|
return article
|
||||||
|
|||||||
+15
-32
@@ -449,7 +449,6 @@ class RSSFeedManager:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def format_telegram_message(article: Dict) -> Tuple[str, Optional[str]]:
|
def format_telegram_message(article: Dict) -> Tuple[str, Optional[str]]:
|
||||||
"""Format article for Telegram message"""
|
"""Format article for Telegram message"""
|
||||||
# Category emoji mapping
|
|
||||||
category_emojis = {
|
category_emojis = {
|
||||||
'news': '📰',
|
'news': '📰',
|
||||||
'malware': '🦠',
|
'malware': '🦠',
|
||||||
@@ -459,48 +458,32 @@ class RSSFeedManager:
|
|||||||
}
|
}
|
||||||
|
|
||||||
emoji = category_emojis.get(article.get('category', ''), '📰')
|
emoji = category_emojis.get(article.get('category', ''), '📰')
|
||||||
category_display = article.get('category', '').replace('_', ' ').title()
|
|
||||||
|
|
||||||
title = escape(article.get('title', 'No Title'))
|
title = escape(article.get('title', 'No Title'))
|
||||||
description = escape(article.get('description', ''))
|
description = escape(article.get('description', ''))
|
||||||
source = escape(article.get('source', 'Unknown Source'))
|
source = escape(article.get('source', 'Unknown Source'))
|
||||||
published_human = escape(article.get('published_human', 'Unknown'))
|
published_human = escape(article.get('published_human', 'Unknown'))
|
||||||
category_safe = escape(category_display)
|
|
||||||
url = escape(article.get('url', ''), quote=True)
|
url = escape(article.get('url', ''), quote=True)
|
||||||
severity = article.get('severity', 'unknown').lower()
|
cves = article.get('cves', [])[:5]
|
||||||
quality_score = article.get('quality_score')
|
threat_actors = article.get('threat_actors', [])[:3]
|
||||||
cves = article.get('cves', [])[:3]
|
malware_families = article.get('malware_families', [])[:3]
|
||||||
classifications = article.get('classifications', [])[:3]
|
|
||||||
|
|
||||||
severity_emoji = {
|
|
||||||
'critical': '🚨',
|
|
||||||
'high': '🔴',
|
|
||||||
'medium': '🟠',
|
|
||||||
'low': '🟡',
|
|
||||||
}.get(severity, '⚪')
|
|
||||||
severity_safe = escape(severity.upper())
|
|
||||||
|
|
||||||
message = f"{emoji} <b>{title}</b>\n\n"
|
message = f"{emoji} <b>{title}</b>\n\n"
|
||||||
|
|
||||||
if description:
|
if description:
|
||||||
message += f"📋 {description}\n\n"
|
message += f"{description}\n\n"
|
||||||
|
|
||||||
|
message += f"📡 {source} · {published_human}\n"
|
||||||
|
|
||||||
message += f"🏷️ <b>Category:</b> {category_safe}\n"
|
|
||||||
message += f"📡 <b>Source:</b> {source}\n"
|
|
||||||
message += f"{severity_emoji} <b>Severity:</b> {severity_safe}\n"
|
|
||||||
if quality_score is not None:
|
|
||||||
message += f"⭐ <b>Quality:</b> {int(quality_score)}/100\n"
|
|
||||||
if classifications:
|
|
||||||
class_text = ", ".join(escape(c) for c in classifications)
|
|
||||||
message += f"🧠 <b>Type:</b> {class_text}\n"
|
|
||||||
if cves:
|
if cves:
|
||||||
cve_text = ", ".join(escape(cve) for cve in cves)
|
message += f"🆔 {', '.join(escape(c) for c in cves)}\n"
|
||||||
message += f"🆔 <b>CVEs:</b> {cve_text}\n"
|
|
||||||
why_this_matters = RSSFeedManager.build_why_this_matters(article)
|
if threat_actors:
|
||||||
if why_this_matters:
|
message += f"👤 {', '.join(escape(a.title()) for a in threat_actors)}\n"
|
||||||
message += f"🎯 <b>Why This Matters:</b> {escape(why_this_matters)}\n"
|
|
||||||
message += f"⏰ <b>Published:</b> {published_human}\n"
|
if malware_families:
|
||||||
message += f"🔗 <b><a href=\"{url}\">Read Full Article</a></b>"
|
message += f"🦠 {', '.join(escape(f) for f in malware_families)}\n"
|
||||||
|
|
||||||
|
message += f"\n🔗 <a href=\"{url}\">Read Full Article</a>"
|
||||||
|
|
||||||
return message, article.get('thumbnail')
|
return message, article.get('thumbnail')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user