CVE-2026-39972
Executive Summary
CVE-2026-39972 is a cache key collision vulnerability in the Mercure protocol hub, specifically within the TopicSelectorStore component. Mercure is a protocol designed for efficient real-time data push to web browsers and HTTP clients. The vulnerability arises from the way cache keys are constructed: by concatenating the topic selector and topic with an underscore separator. Because both topic selectors and topics can contain underscores, an attacker can craft inputs that produce identical cache keys, leading to cache poisoning. This can cause private updates to be delivered to unauthorized subscribers or block delivery to authorized ones. The vulnerability affects all versions prior to 0.22.0 and is fixed in that release.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Mercure is a Go-based protocol and hub. The vulnerability is in the core hub implementation, not in a package manager distribution. However, any application or service that relies on Mercure for real-time updates is affected. Users should ensure they are using the official Mercure hub binary or Docker image version 0.22.0 or later.
CVSS Breakdown & Threat Level
The CVSS score is not provided in the NVD data. However, based on the description, the vulnerability has the following characteristics:
- Attack Vector: Network (AV:N)
- Attack Complexity: Low (AC:L)
- Privileges Required: Low (PR:L) – requires ability to subscribe or publish
- User Interaction: None (UI:N)
- Scope: Changed (S:C) – affects authorization boundaries
- Confidentiality Impact: High (C:H) – private updates exposed
- Integrity Impact: Low (I:L) – delivery blocking
- Availability Impact: Low (A:L) – delivery blocking
Estimated CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:L
Estimated Score: 8.5 (High)
Deep Dive & Mechanics
The vulnerability is in the TopicSelectorStore component of the Mercure hub. The hub uses topic selectors to match subscribers to published updates. When a subscriber registers, they provide a topic selector (e.g., user/123). When a publisher sends an update, they specify a topic (e.g., user/123). The hub caches the result of matching a topic selector against a topic to improve performance.
The cache key is constructed by concatenating the topic selector and topic with an underscore separator. For example:
- Topic selector:
user_123and topic:profile→ cache key:user_123_profile - Topic selector:
userand topic:123_profile→ cache key:user_123_profile
Both pairs produce the same cache key, causing a collision. An attacker who can subscribe with a crafted topic selector or publish with a crafted topic name can exploit this to poison the cache. This can lead to:
- Private updates being delivered to unauthorized subscribers (if the cache incorrectly matches a subscriber to a topic).
- Authorized subscribers not receiving updates (if the cache incorrectly fails to match).
The vulnerability is fixed in version 0.22.0 by using a more robust cache key construction method that avoids collisions.
Vulnerable vs. Patched Code
Vulnerable Code (prior to 0.22.0):
func (s *TopicSelectorStore) Match(selector, topic string) bool {
key := selector + "_" + topic
if result, ok := s.cache[key]; ok {
return result
}
// ... perform matching logic ...
s.cache[key] = result
return result
}
Patched Code (0.22.0):
func (s *TopicSelectorStore) Match(selector, topic string) bool {
key := fmt.Sprintf("%s\x00%s", selector, topic) // Use null byte separator
if result, ok := s.cache[key]; ok {
return result
}
// ... perform matching logic ...
s.cache[key] = result
return result
}
The fix uses a null byte (\x00) as the separator, which cannot appear in valid topic selectors or topics, thus preventing collisions.
Exploitation & Lab Setup
Red Team
Prerequisites:
- Access to a Mercure hub (version < 0.22.0)
- Ability to subscribe with a crafted topic selector or publish with a crafted topic name
Exploitation Steps:
- Identify a target private topic (e.g.,
user/123/private). - Subscribe to the hub with a crafted topic selector that collides with the target. For example, if the target topic is
user/123/private, craft a selector likeuser/123_private(if the target topic isuser/123_private, craft a selector likeuser/123). - Publish an update to the target topic. The cache collision may cause the update to be delivered to the attacker's subscription.
- Alternatively, publish a crafted topic that collides with a legitimate subscriber's selector to block their updates.
Example using curl:
# Subscribe with crafted selector
curl -H "Authorization: Bearer <token>" -H "Accept: text/event-stream" \
"https://hub.example.com/.well-known/mercure?topic=user_123_private"
# Publish to target topic
curl -H "Authorization: Bearer <token>" -H "Content-Type: application/x-www-form-urlencoded" \
-d "topic=user/123/private&data=private_data" \
"https://hub.example.com/.well-known/mercure"
Blue Team
Detection:
- Monitor for unusual subscription patterns, especially topic selectors containing underscores that may be crafted to cause collisions.
- Monitor for cache hit/miss anomalies in the hub logs.
- Use the detection rules provided below.
Mitigation:
- Upgrade to Mercure hub version 0.22.0 or later.
- If immediate upgrade is not possible, disable caching in the
TopicSelectorStore(if configurable) or implement additional authorization checks.
Indicators of Compromise (IOCs)
- Log Entries: Cache collisions detected in hub logs (e.g., "cache key collision for selector X and topic Y").
- Network Traffic: Unusual subscription requests with topic selectors containing multiple underscores.
- Behavioral: Private updates being delivered to unexpected subscribers or authorized subscribers not receiving updates.
Detection Engineering
YARA Rule
rule CVE_2026_39972_Mercure_Cache_Collision {
meta:
description = "Detects cache key collision attempts in Mercure hub logs"
author = "Valters IT"
date = "2026-01-01"
reference = "CVE-2026-39972"
strings:
$log1 = "cache key collision" ascii wide nocase
$log2 = "TopicSelectorStore" ascii wide nocase
$log3 = "underscore" ascii wide nocase
condition:
any of ($log1, $log2, $log3)
}
Sigma Rule
title: Mercure Cache Key Collision Attempt
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects attempts to exploit CVE-2026-39972 by crafting topic selectors with underscores
author: Valters IT
date: 2026-01-01
logsource:
category: application
product: mercure
detection:
selection:
event: "cache key collision"
condition: selection
falsepositives:
- Legitimate use of underscores in topic names
level: high
Suricata Rule
alert http any any -> any any (msg:"Mercure Cache Key Collision Attempt"; flow:to_server; content:"topic"; nocase; pcre:"/topic=[^&]*_[^&]*_[^&]*/"; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate:
- Upgrade to Mercure hub version 0.22.0 or later.
Compensating Controls:
- If upgrade is not possible, disable caching in the
TopicSelectorStoreif configurable. - Implement additional authorization checks at the application level.
- Monitor for unusual subscription patterns and cache collisions.
Long-term:
- Ensure all dependencies are regularly updated.
- Implement robust cache key construction methods that avoid collisions (e.g., using null byte separators or hashing).
Valters IT
Securing Digital Frontiers
{/* [Security Hash: E96776454F6C4D2C7CF4AF73] */}