Module 31 of 45 · Practice

When protocol features become attack surfaces

28 min 5 outcomes 2 diagrams + knowledge check

The largest request flood the internet has recorded did not exploit a broken protocol. It used a behaviour the specification promises: a client may cancel a request it no longer wants. Read that sentence as the answer to the whole module. An attack surface is not only the list of things that are broken in your systems. It is the list of things your systems have promised to do for anyone who asks, and the defence is almost never to withdraw the promise. It is to account for what keeping it costs.

By the end of this module you will be able to:

  • Explain how an intended protocol feature becomes an attack surface
  • Narrate HTTP/2 Rapid Reset end to end, from the first frame to the fix
  • Read attack-scale numbers critically, and say what a headline figure omits
  • Name the mitigation layers and say who owns each one
  • Run the drill of asking what evidence each layer can contribute to an alert

October 2023: three of the largest networks on earth published the same attack on the same day

In October 2023 Google, Cloudflare and Amazon Web Services disclosed, jointly, an attack technique all three had been absorbing on their own networks. It was catalogued as CVE-2023-44487 and named . Google reported mitigating a peak of 398 million requests per second, Cloudflare 201 million and AWS 155 million. Those were the largest request floods any of them had recorded.

What made the disclosure unusual was not the size. It was the finding. There was no memory corruption to fix, no parsing flaw, no forgotten debug endpoint. The attack consisted of a client opening a request and cancelling it, then doing so again, very quickly, on a connection that had already been established and paid for. Every frame it sent was a frame the protocol invites clients to send. The alert from the United States Cybersecurity and Infrastructure Security Agency and the technical breakdown Cloudflare published both describe a technique rather than a defect.

That is why the case sits in this stage rather than in a vulnerability list. Three organisations with different code, different proxies and different capacity all found the same weakness at the same time, because it was not in anyone's code. It was in what the protocol had agreed to do.

Cancelling work faster than servers can start it

The HTTP/2 loop runs on one connection with no new handshake, so the server completes work for streams already cancelled while its concurrency limit sees an idle connection, which is why the mitigation counts resets rather than streams.

HTTP/2 Rapid Reset (CVE-2023-44487, October 2023) opens and cancels streams in a loop, so a server does whole requests its concurrency limit never sees; defenders count resets, not streams.

Cancelling work faster than servers can start it A sequence diagram with two lifelines, an attacker client on the left and an HTTP/2 server on the right. A dashed loop frame labelled REPEAT ON ONE CONNECTION, NO NEW HANDSHAKE holds three arrows: HEADERS opens a stream and the server starts work, RST_STREAM cancels it before any response, and HEADERS opens the next stream. Two labelled arrows leave the loop. The left card lists the work each cancelled request still cost, ending with the concurrency limit never seeing the pressure. The emphasised right card gives the mitigation point: count resets per connection. A band below records the mitigated peaks of 398, 201 and 155 million requests per second. Attacker client HTTP/2 server REPEAT ON ONE CONNECTION, NO NEW HANDSHAKE HEADERS: open a new stream the server allocates state and starts the work one slot of the concurrency budget RST_STREAM: cancel it at once the slot is freed before any response is sent the server keeps working anyway HEADERS: open the next stream no handshake and no new connection is needed repeat thousands of times a second each cancelled stream still cost a full request so mitigation must count the cancellations What the cancelled request still cost Stream state allocated and headers decodedRequest routed and the handler startedBackend call issued, then thrown awayThe concurrency limit never sees the pressure Where the defence goes CVE-2023-44487, disclosed October 2023Count resets per connection, not just streamsDrop connections that cancel abusivelyEdges absorb it; origins still had to patch Peaks mitigated during the October 2023 disclosure Google 398 million rps Cloudflare 201 million rps AWS 155 million rps rps means requests per second: the unit matters when you read attack numbers

27.1 Features are promises attackers cash

Threat modelling that only asks what is broken will miss this entire class of attack. A protocol specification is a list of promises: ask for this and I will do that. An attacker reads the same document as a price list, looking for the request that is cheapest to send and most expensive to serve. Nothing has to malfunction for that gap to be profitable.

Take the feature at the centre of this module. exists for the client's benefit and it is good engineering. A browser changes its mind constantly. The reader scrolls past the images further down the page, navigates away before the article finishes loading, drags a video player's scrub bar to a different position. Before HTTP/2, saying "stop, I no longer want that" meant tearing down the whole connection, which threw away the handshake, the encryption state and the congestion state along with the unwanted response. Cancellation lets a client abandon one request and keep everything else, which saves the network real work.

It only works because of the other feature underneath it. carries many independent requests inside one connection, each with its own identifier and its own ordering. A server protects itself there by capping how many streams a client may have open at once, which is a sensible limit and, as the next section shows, the wrong thing to count.

Before going further it is worth checking what the word for this category of attack actually commits you to, because most people carry an assumption inside it that the definition does not support. A is defined by where the traffic comes from, not by how much of it there is.

The glossary of the United States National Institute of Standards and Technology defines it as a denial of service technique carried out from numerous hosts. Read what that definition leaves out. It says nothing about bandwidth, nothing about packet sizes and nothing about volume at all. The word distributed refers to the number of sources, because a great many sources is what makes attack traffic hard to separate from customers. A technique that exhausts a server's ability to start work, sent from many hosts, qualifies even if the bandwidth involved would not trouble a home broadband line. Section 27.4 turns that observation into a way of reading published attack figures.

Abused features are not all the same, and the quadrant below separates them on the two properties that decide what you can do about them. Across: was the behaviour a convenience somebody left switched on, or is it core to how the protocol works? Down: does the abuse scale to a flood, or is its reach bounded?

Intended features carry unintended invitations

Rapid Reset, 0-RTT replay, DNS amplification and NTP monlist are placed by what the feature was for against how far the abuse reaches, and the column decides the fix: a convenience can be closed, while a feature core to the protocol can only be rate limited and accounted for.

HTTP/2 Rapid Reset (CVE-2023-44487) and 0-RTT replay abuse features no operator can delete, while NTP monlist and DNS amplification abuse conveniences left open: the fix follows which kind you have.

Intended features carry unintended invitations A quadrant board. The columns are what the feature was for: a convenience left open on the left, core to how the protocol works on the right. The rows are how far the abuse reaches: it scales to a flood on the top row, and is bounded on the bottom row. Top left holds NTP monlist and DNS amplification. Top right holds HTTP/2 Rapid Reset, CVE-2023-44487 of October 2023, with the 398 million requests a second Google mitigated. Bottom left holds a card saying nothing lands there. Bottom right holds 0-RTT replay. Two labelled arrows lead down to two action cards: close the leftovers, and rate limit the core features that cannot be removed. across: intent down: abuse A CONVENIENCE LEFT OPEN CORE TO HOW THE PROTOCOL WORKS Scales Abuse potential ishigh. One request orpacket becomes aflood at scale. Bounded Abuse potential iscontained. It harmsone exchange, notthe whole service. NTP monlist A diagnostic extra Answered any stranger with amuch longer reply. Removed. DNS amplification An open resolver posture A small spoofed query returns alarge answer to the victim. HTTP/2 Rapid Reset CVE-2023-44487, October 2023 Cancelling a stream is a featureclients need. Sent in a tight loop,it makes a server start work itnever finishes.Google mitigated 398 millionrequests a second. Nothing lands here The retirement corner A feature with little purposeand little reach just goes. 0-RTT replay Early data, TLS 1.3 and QUIC Resuming without a round trip iscore to modern transports, but acopy can be replayed. Onlyrepeatable requests belong in it. what to do with a leftover what to do with a core feature Close it A convenience is not a necessity.Monlist was removed, not redesigned;open recursion is now a fault to fix. Price it Core features cannot be removed. Ratelimit them, and count the work eachrequest starts, not the requests.

The left column holds the leftovers. An sends small queries carrying a forged source address to servers whose answers are much larger, so the answers arrive at the victim rather than at the sender. Open DNS resolvers were the classic reflector, and the NTP monlist command, which returns a list of recent clients when asked, was another. Both are fixable by configuration because neither is load bearing: close the resolver to strangers, disable the diagnostic command, filter traffic that carries a source address it could not legitimately have. Nobody loses a capability they were using.

The right column is the uncomfortable one. Cancellation cannot be removed from HTTP without making every browser worse. Early data sent before a handshake completes, which the TLS module covered as 0-RTT, can be captured and replayed so the server acts on the same request twice, and it still cannot be removed without giving back the round trip it was introduced to save. For features in this column the question is never whether to keep them. It is what accounting you apply so that a peer who uses them a thousand times a second is treated differently from a peer who uses them the way a browser does.

That is the threat-modelling move this module is asking for. For every behaviour you perform on request, write down two figures: what it costs the requester to ask, and what it costs you to comply. Where those two diverge sharply, you have found an attack surface, and you have found it without waiting for a CVE to tell you. Rapid Reset is the case that proves the method, so the next section takes it apart frame by frame.

27.2 Anatomy of Rapid Reset

The loop is three steps long and the figure above the fold draws it. The client sends a HEADERS frame, which opens a stream and carries a request. The server does what a server does with a request: it allocates state for the stream, parses the headers, works out which route or handler applies, very likely starts an authentication check and a call to a backend or a database. Before any of that can produce a response, the client sends RST_STREAM and cancels the stream. Then it opens the next one. No new connection, no new handshake, no new certificate validation. The same established connection carries the loop for as long as it is allowed to.

The asymmetry is the attack. Sending HEADERS and RST_STREAM costs the client two small frames on a connection it has already paid for. Serving them costs the server most of a real request: the state, the parsing, the routing, the backend call that has already been dispatched and whose result will now be thrown away. The response bytes are the only part of the work the server is spared, and the response bytes were never the expensive part.

Now add the detail that turns an inefficiency into a record-breaking flood. A cancelled stream is closed, and a closed stream does not count against the limit on how many streams may be open at once. The protection the server thought it had was counting concurrency, and concurrency is exactly what the attacker never accrues. An operator watching that metric sees a connection with almost nothing open on it while the machine behind it saturates. This is why the mitigated peaks were measured in requests rather than in bits: 398 million requests per second at Google, 201 million at Cloudflare and 155 million at AWS were floods of work, not floods of traffic.

The fix follows directly and it is worth stating precisely, because it is the evidence for this module's argument. Vendors did not change the protocol. They changed what they counted. Track cancellations per connection, treat a connection whose reset rate is far above what a browser produces as abusive, and close it or charge it. Some implementations also cap the total streams a single connection may open across its lifetime, which bounds the loop even when each individual cancellation looks legitimate. The feature survived. The free ride ended.

Common misconception

Rapid Reset was a flaw in HTTP/2, and applying the patch fixed the protocol.

There was no protocol change. CVE-2023-44487 records the abuse of behaviour that HTTP/2 permits and that clients legitimately rely on, which is why every implementation was affected at once rather than one vendor's. What shipped as a patch was server-side accounting: counting resets per connection, limiting how many streams a connection may open over its lifetime, and dropping peers whose cancellation rate does not resemble a client. Reading the fix as a corrected specification leads to the wrong conclusion, which is that being current on patches protects you from this class. Being current on patches protects you from this instance.

A flood of that size arriving as requests rather than bits also explains where it was stopped. The three organisations that disclosed it are the three that could see it at scale, and that is not a coincidence about their engineering budgets. It is a fact about where the traffic physically arrived.

27.3 Why the edge absorbed it

Start with the structural defence. announces the same address from many separate locations and lets ordinary routing deliver each client to one of them. A client does nothing special and knows nothing about it: it sends to one address, and the routing system decides which instance answers. For a this is how one address can be served close to users everywhere, from dozens or hundreds of separate .

is what that same arrangement does to a flood. Attack traffic is sent from many sources in many places, so routing splits it the same way it splits legitimate traffic, and each share is absorbed near where it entered the network instead of arriving whole at one site. The flood divides itself. There is one hard-won operational warning attached: withdrawing an anycast route while under attack does not shed the load, it moves it to the next instance, and operators tend to learn this during the incident rather than before it.

Capacity alone would not have stopped Rapid Reset, and this is the part worth slowing down for. Splitting a flood works when the scarce resource is bandwidth. It helps far less when the scarce resource is the ability to start and abandon requests, because dividing 398 million requests per second across many sites still leaves every site doing work it will throw away. What the edge contributed was not only capacity. It was protocol awareness: proxies that terminate and HTTP/2 themselves, parse the frames, hold per-connection counters and can therefore see a cancellation rate no browser would produce. A pure bandwidth defence, in front of a proxy that only counted open streams, would have passed the attack through intact.

That gives the ownership map, and it is worth naming each layer with the thing only it can do. The transit provider or scrubbing service owns volume and spoofing: it can drop traffic carrying source addresses it could not legitimately have, which is the control that decides whether amplification is possible at all, and notice that it is usually somebody else's network that has to apply it on your behalf. The content network or edge owns distribution and protocol-aware limits. The reverse proxy or load balancer at the origin owns per-connection accounting for anything that reaches it directly. The application server owns concurrency, queue depth and timeouts. The application itself owns how expensive an individual request is allowed to be, which is the only layer that can make a flood cheap to serve rather than merely cheap to reject.

None of that removed the origin's job. is not a slogan here, it is the practical consequence of the traffic that never passes the edge. Origin servers whose addresses are reachable directly can be hit around the content network entirely. Internal service-to-service traffic frequently speaks the same multiplexed protocols with no edge in front of it at all. Interfaces that partners reach by agreement often bypass the public path by design. Operators who concluded that their provider had handled it, and who therefore did not update the software terminating those connections, kept the exposure in every place their provider could not see.

Notice how much of this section depended on knowing that the headline figure was a rate of requests. Had it been a rate of bits, the ownership map would have started and very nearly ended at the transit provider. Reading the unit correctly is not pedantry, it is what selects the defence.

27.4 Reading attack numbers

Three units appear in attack reporting and they are not interchangeable, because each one names a different resource running out.

Requests per second measures application work. It is exhausted at the server: the handlers, the connection pools, the database behind them. Bits per second measures link capacity. It is exhausted in the pipe, and it is the figure that matters when a circuit fills and traffic is discarded before any server sees it. Packets per second measures per-packet processing. It is exhausted in the hardware that has to make a forwarding or filtering decision for every packet regardless of how small that packet is, which is why a flood of tiny packets can overwhelm equipment that would have carried the same bits comfortably in fewer, larger ones.

The three come apart in both directions. Rapid Reset produced enormous request rates on modest bandwidth, because two small frames trigger a whole request. A large amplification flood is the mirror image: it can fill a circuit with very few distinct requests, and reporting it in requests per second would make it sound trivial. A figure without its unit is unreadable, and a figure whose unit you did not check is worse, because you will act on it.

Common misconception

A record-breaking attack means record-breaking bandwidth.

Rapid Reset set records in requests per second, which is a measure of work rather than of traffic. Two small frames on an existing connection cause a server to allocate state, parse, route and dispatch a backend call, so the bandwidth required to generate the load is a small fraction of what a volumetric flood of the same notoriety would need. Treating every large number as a bandwidth problem sends the response to the wrong layer: the transit provider is asked to add capacity while the resource actually being exhausted is the application's ability to start work, which only per-connection accounting at the proxy or the server can protect.

is the habit this course has been building since the foundations stage, and it applies to attack figures exactly as it applies to adoption figures. Four questions turn a headline into something you can use.

What is the unit, and therefore which resource was exhausted? Whose vantage point produced it, given that a defender can only report the traffic that arrived at their own network and a different defender would have seen a different share of the same campaign? Over what window, since a peak sustained for two seconds and a rate held for an hour are different engineering problems wearing the same number? And was the figure mitigated or delivered, which is the distinction that decides what the number is evidence of at all?

That last question deserves its own sentence. A mitigated peak is a defender's telemetry: it records what their systems saw and absorbed. It is a real measurement of what arrived, and it is not a measurement of what the attacker was capable of, which is unknown and probably larger. The figures in this module are stated as mitigated peaks for that reason, and quoting them as the size of the botnet or the limit of the technique would be a claim nobody has the evidence to make.

Applied to an alert on your own estate, the same discipline stops the first expensive mistake. Before anyone is asked to add capacity, establish which unit the alert is actually reporting, because the answer decides which of the layers in section 27.3 you are going to call. Establishing that takes evidence, and different layers of the stack hold very different amounts of it.

27.5 The layered evidence drill

The drill is one question asked three times: what can this layer prove, and what can it not? Write both halves down. The habit the applied stage built, that transport evidence narrows the candidates without concluding, is the whole point here, and under incident pressure it is the first discipline people drop.

Start at layer 3, the network layer, where are usually the cheapest evidence still available. They prove distribution and volume: how many distinct source addresses, arriving at what rate, towards which of your addresses, over what period. That is enough to distinguish one noisy client from a genuinely distributed event, and it is often the only record that survives long enough to reconstruct what happened last Tuesday. What layer 3 cannot prove is whether those addresses are real, because a source address can be forged, or which of the requests behind them were expensive to serve.

Layer 4, the transport layer, answers the first of those two questions. A completed TCP handshake is evidence that a source address could receive the reply, which spoofed traffic cannot do, so the split between connections that completed a handshake and packets that never did separates a reflected flood from a flood sent by real hosts. It also shows whether the traffic is TCP or UDP, and how many connections each source held open. What layer 4 cannot prove is what was asked for. A connection that completes a handshake and stays open looks identical whether it is carrying one video stream or a cancellation loop.

Only layer 7, the application layer, could have identified Rapid Reset, and it is worth being blunt about why. The signature is the ratio between streams opened and streams cancelled on a single connection, and the only component that can see it is one that parses the frames: the proxy, the load balancer or the server itself. No amount of flow data and no connection table would have produced that number. Layer 7 also holds what was requested, what it cost and what status was returned, at the price of being the most expensive evidence to keep and the layer encryption has moved furthest out of a passive observer's reach.

One trap sits inside that last layer and it catches experienced teams. Many access logs are written when a response completes. A request that is cancelled before it produces a response may therefore never appear in the log at all, which is how an origin can run at full processor load while its request log looks unremarkable. Before an incident, find out which event your own logging is keyed to. During an incident, treat a quiet access log next to a saturated server as a finding rather than as reassurance.

Run the drill on a concrete alert and the shape becomes obvious. Processor load at the origin is at its ceiling, the circuit is nowhere near full, and the access log is calm. Layer 3 shows many sources but cannot say whether they are real. Layer 4 shows completed handshakes, which rules out the reflected floods in the left column of the quadrant and points at real hosts. Layer 7 shows a cancellation rate per connection that no browser would produce, which is the observation that separates the remaining candidates. Each layer narrowed the field. The last one concluded, and it could only conclude because the first two had already removed the alternatives.

27.6 Check your understanding

A company's inbound circuit is saturated by traffic from thousands of public servers it has never sent anything to, and its own outbound traffic has not changed. Which protocol behaviour is being abused?

Core distinctions

  • An attack surface includes intended behaviour. Rapid Reset used stream cancellation exactly as specified, so threat modelling has to ask what each promised behaviour costs to ask for and costs to serve, not only what is broken.
  • HTTP/2 Rapid Reset (CVE-2023-44487, October 2023) opens a stream, cancels it before any response and repeats on the same connection. Cancelled streams stop counting towards the concurrency limit, so the server does whole requests the limit never sees.
  • Google mitigated a peak of 398 million requests per second, Cloudflare 201 million and AWS 155 million. All three are mitigated peaks reported by defenders, which is evidence of what arrived rather than of what the attackers could produce.
  • Anycast splits a flood across many instances and absorbs it near where it entered, and withdrawing a route under attack moves the traffic rather than shedding it. Against a request flood, protocol-aware counting at the edge mattered more than capacity.
  • Requests per second, bits per second and packets per second name three different exhausted resources: application work, link capacity and per-packet processing. The unit selects which layer owns the mitigation.
  • Layer 3 proves distribution and volume, layer 4 proves whether sources completed a handshake and are therefore real, and only layer 7 can show a cancellation rate per connection. Transport evidence narrows the candidates; it does not conclude alone.

Standards and sources cited in this module

  1. CISA alert, HTTP/2 Rapid Reset Vulnerability (CVE-2023-44487)

    Published 10 October 2023

    The joint disclosure behind this module's opening case, describing the technique, the affected implementations and the mitigation advice for operators. Source for the October 2023 date and the CVE identifier used throughout sections 27.1 and 27.2.

  2. Cloudflare, HTTP/2 Rapid Reset: deconstructing the record-breaking attack

    Technical breakdown, October 2023

    The frame-by-frame account of the loop drawn in the primary figure, the reason cancelled streams escape the concurrency limit, and the mitigated peak figures of 398, 201 and 155 million requests per second reported by Google, Cloudflare and AWS. Used in sections 27.2 and 27.4.

  3. RFC 9114, HTTP/3

    Request cancellation and rejection

    The standard behind the paraphrase in section 27.2: cancellation terminates the open directions of a stream, and a server signals separately whether it had performed no application processing or abandoned a response part way. That distinction is the accounting a concurrency limit never performs.

  4. RFC 5358 (BCP 140), Preventing Use of Recursive Nameservers in Reflector Attacks

    Reflection and amplification, and ingress filtering

    The authority for the amplification mechanism described in section 27.1: spoofed queries sent to open resolvers reflect amplified traffic at a victim, and filtering traffic that carries an illegitimate source address is the real remedy.

  5. RFC 4786 (BCP 126), Operation of Anycast Services

    Operational practice for anycast services

    Operational guidance for the anycast behaviour described in section 27.3, covering load distribution across dispersed nodes, proximity, and localising the damage of denial-of-service attacks.

  6. RFC 7094, Architectural Considerations of IP Anycast

    Denial-of-service considerations

    Source for the warning in section 27.3 that anycast localises attack damage to individual instances, and that withdrawing a route while under attack can cascade the failure to the remaining instances rather than shedding the load.

  7. NIST Computer Security Resource Center glossary, distributed denial of service

    Definition entry

    The definition quoted in section 27.1. It is used to make the point that the word distributed refers to the number of sources rather than to any level of bandwidth, which is what allows a request flood to qualify.

Rapid Reset was absorbed by networks that had already put capacity, protocol-aware proxies and anycast announcements between the internet and their customers' origins. This module treated that edge as a defence. The next one treats it as architecture: how anycast steers a request to the nearest point of presence, what a cache hit and a cache miss actually cost, and why the client address in your logs may belong to a relay rather than to a user.

Module 31 of 45 · Practice