Fix websocket pattern + improve beacon override logic to prevent false positives/negatives
Browse files- c2sentinel.py +26 -11
c2sentinel.py
CHANGED
|
@@ -278,15 +278,15 @@ LEGITIMATE_PATTERNS = [
|
|
| 278 |
description="Database connection with variable query responses"
|
| 279 |
),
|
| 280 |
LegitimatePattern(
|
| 281 |
-
name="
|
| 282 |
service_type=ServiceType.API,
|
| 283 |
ports=[80, 443, 8080],
|
| 284 |
-
min_packet_size=
|
| 285 |
max_packet_size=100000,
|
| 286 |
-
symmetric_ratio=(0.001,
|
| 287 |
-
max_interval_cv=
|
| 288 |
-
max_size_cv=
|
| 289 |
-
description="WebSocket connection with
|
| 290 |
),
|
| 291 |
]
|
| 292 |
|
|
@@ -1712,14 +1712,29 @@ class C2Sentinel:
|
|
| 1712 |
|
| 1713 |
# ================================================================
|
| 1714 |
# PHASE 5: Apply legitimate pattern discount
|
| 1715 |
-
#
|
| 1716 |
# ================================================================
|
| 1717 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1718 |
if matched_pattern and pattern_confidence > 0.5:
|
| 1719 |
-
#
|
| 1720 |
-
|
| 1721 |
-
|
| 1722 |
-
|
|
|
|
| 1723 |
discount = 1.0 - (pattern_confidence * 0.8) # Up to 80% reduction
|
| 1724 |
c2_prob *= discount
|
| 1725 |
result.mitigating_factors.append(f"Strong {matched_pattern.name} pattern match (conf: {pattern_confidence:.0%})")
|
|
|
|
| 278 |
description="Database connection with variable query responses"
|
| 279 |
),
|
| 280 |
LegitimatePattern(
|
| 281 |
+
name="websocket_stream",
|
| 282 |
service_type=ServiceType.API,
|
| 283 |
ports=[80, 443, 8080],
|
| 284 |
+
min_packet_size=100, # WebSocket frames are typically larger
|
| 285 |
max_packet_size=100000,
|
| 286 |
+
symmetric_ratio=(0.001, 0.3), # Receives much more than sends (streaming)
|
| 287 |
+
max_interval_cv=1.5, # Irregular timing (event-driven)
|
| 288 |
+
max_size_cv=2.0, # High variance in response sizes (required)
|
| 289 |
+
description="WebSocket streaming connection with variable push data"
|
| 290 |
),
|
| 291 |
]
|
| 292 |
|
|
|
|
| 1712 |
|
| 1713 |
# ================================================================
|
| 1714 |
# PHASE 5: Apply legitimate pattern discount
|
| 1715 |
+
# Balance between legitimate patterns and beacon indicators
|
| 1716 |
# ================================================================
|
| 1717 |
|
| 1718 |
+
# Check if we have a very strong beacon signal that should override patterns
|
| 1719 |
+
very_strong_beacon = False
|
| 1720 |
+
if beacon_indicators >= 5:
|
| 1721 |
+
very_strong_beacon = True
|
| 1722 |
+
elif beacon_indicators >= 4:
|
| 1723 |
+
# Check if timing and size CVs are extremely low (strong C2 signature)
|
| 1724 |
+
if len(connections) >= 5:
|
| 1725 |
+
timestamps = sorted([c.get('timestamp', 0) for c in connections])
|
| 1726 |
+
if len(timestamps) > 1:
|
| 1727 |
+
intervals = np.diff(timestamps)
|
| 1728 |
+
interval_cv = np.std(intervals) / (np.mean(intervals) + 1e-6)
|
| 1729 |
+
if interval_cv < 0.1 and recv_cv < 0.1:
|
| 1730 |
+
very_strong_beacon = True
|
| 1731 |
+
|
| 1732 |
if matched_pattern and pattern_confidence > 0.5:
|
| 1733 |
+
# Very strong beacon signals override legitimate patterns (except SSH on port 22)
|
| 1734 |
+
if very_strong_beacon and matched_pattern.name != 'ssh_keepalive':
|
| 1735 |
+
result.mitigating_factors.append(f"{matched_pattern.name} pattern overridden by very strong beacon signal")
|
| 1736 |
+
elif pattern_confidence >= 0.75 and not very_strong_beacon:
|
| 1737 |
+
# Strong legitimate pattern without strong beacon - apply full discount
|
| 1738 |
discount = 1.0 - (pattern_confidence * 0.8) # Up to 80% reduction
|
| 1739 |
c2_prob *= discount
|
| 1740 |
result.mitigating_factors.append(f"Strong {matched_pattern.name} pattern match (conf: {pattern_confidence:.0%})")
|