
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Tue, 14 Apr 2026 23:29:42 GMT</lastBuildDate>
        <item>
            <title><![CDATA[BGP zombies and excessive path hunting]]></title>
            <link>https://blog.cloudflare.com/going-bgp-zombie-hunting/</link>
            <pubDate>Fri, 31 Oct 2025 15:30:00 GMT</pubDate>
            <description><![CDATA[ A BGP “zombie” is essentially a route that has become stuck in the Default-Free Zone (DFZ) of the Internet, potentially due to a missed or lost prefix withdrawal. We’ll walk through some situations where BGP zombies are more likely to rise from the dead and wreak havoc.
 ]]></description>
            <content:encoded><![CDATA[ <p>Here at Cloudflare, we’ve been celebrating Halloween with some zombie hunting of our own. The zombies we’d like to remove are those that disrupt the core framework responsible for how the Internet routes traffic: <a href="http://cloudflare.com/learning/security/glossary/what-is-bgp/"><u>BGP (Border Gateway Protocol)</u></a>.</p><p>A <a href="https://dl.acm.org/doi/10.1145/3472305.3472315"><u>BGP zombie</u></a> is a silly name for a route that has become stuck in the Internet’s <a href="https://en.wikipedia.org/wiki/Default-free_zone"><u>Default-Free Zone</u></a>, aka the DFZ: the collection of all internet routers that do not require a default route, potentially due to a missed or lost prefix withdrawal.</p><p>The underlying root cause of a zombie could be multiple things, spanning from buggy software in routers or just general route processing slowness. It’s when a BGP prefix is meant to be gone from the Internet, but for one reason or another it becomes a member of the undead and hangs around for some period of time.</p><p>The longer these zombies linger, the more they create operational impact and become a real headache for network operators. Zombies can lead packets astray, either by trapping them inside of route loops or by causing them to take an excessively scenic route. Today, we’d like to celebrate Halloween by covering how BGP zombies form and how we can lessen the likelihood that they wreak havoc on Internet traffic.</p>
    <div>
      <h2>Path hunting</h2>
      <a href="#path-hunting">
        
      </a>
    </div>
    <p>To understand the slowness that can often lead to BGP zombies, we need to talk about path hunting. <a href="https://www.noction.com/blog/bgp-path-hunting"><u>Path hunting</u></a> occurs when routers running BGP exhaustively search for the best path to a prefix as determined by <a href="https://en.wikipedia.org/wiki/Longest_prefix_match"><u>Longest Prefix Matching</u></a> (LPM) and BGP routing attributes like path length and local preference. This becomes relevant in our observations of exactly how routes become stuck, for how long they become stuck, and how visible they are on the Internet.</p><p>For example, path hunting happens when a more-specific BGP prefix is withdrawn from the global routing table, and networks need to fallback to a less-specific BGP advertisement. In this example, we use 2001:db8::/48 for the more-specific BGP announcement and 2001:db8::/32 for the less-specific prefix. When the /48 is withdrawn by the originating <a href="https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/"><u>Autonomous System</u></a> (AS), BGP routers have to recognize that route as missing and begin routing traffic to IPs such as 2001:db8::1 via the 2001:db8::/32 route, which still remains while the prefix 2001:db8::/48 is gone. </p><p>Let’s see what this could look like in action with a few diagrams. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7xRNAHChJUyiMbtBZyLOlF/973d10be053b7b7f088721389c34c10e/BLOG-3059_2.png" />
          </figure><p><sub><i>Diagram 1: Active 2001:db8::/48 route</i></sub></p><p>In this initial state, 2001:db8::/48 is used actively for traffic forwarding, which all flows through AS13335 on the way to AS64511. In this case, AS64511 would be a BYOIP customer of Cloudflare. AS64511 also announces a <i>backup</i> route to another Internet Service Provider (ISP), AS64510, but this route is not active even in AS64510’s routing table for forwarding to 2001:db8::1 because 2001:db8::/48 is a longer prefix match when compared to 2001:db8::/32.</p><p>Things get more interesting when AS64510 signals for 2001:db8::/48 to be withdrawn by Cloudflare (AS13335), perhaps because a DDoS attack is over and the customer opts to use Cloudflare only when they are actively under attack.</p><p>When the customer signals to Cloudflare (via BGP Control or API call) to withdraw the 2001:db8::/48 announcement, all BGP routers have to <a href="https://en.wikipedia.org/wiki/Convergence_(routing)"><u>converge</u></a> upon this update, which involves path hunting. AS13335 sends a BGP withdrawal message for 2001:db8::/48 to its directly-connected BGP neighbors. While the news of withdrawal may travel quickly from AS13335 to the other networks, news may travel more quickly to some of the neighbors than others. This means that until everyone has received and processed the withdrawal, networks may try routing through one another to reach the 2001:db8::/48 prefix – even after AS13335 has withdrawn it. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7h3Vba4T7tm6XPB2pIyQex/f5f7c27148bed4dd72959b3820d045ac/BLOG-3059_3.png" />
          </figure><p><sub><i>Diagram 2: 2001:db8::/48 route withdrawn via AS13335</i></sub></p><p>Imagine AS64501 is a little slower than the rest – perhaps due to using older hardware, hardware being overloaded, a software bug, specific configuration settings, poor luck, or some other factor – and still has not processed the withdrawal of the /48. This in itself could be a BGP zombie, since the route is stuck for a small period. Our pings toward 2001:db8::1 are never able to actually reach AS64511, because AS13335 knows the /48 is meant to be withdrawn, but some routers carrying a full table have not yet converged upon that result.</p><p>The length of time spent path hunting is amplified by something called the Minimum Route Advertisement Interval (MRAI). The MRAI specifies the minimum amount of time between BGP advertisement messages from a BGP router, meaning it introduces a purposeful number of seconds of delay between each BGP advertisement update. <a href="https://datatracker.ietf.org/doc/html/rfc4271"><u>RFC4271</u></a> recommends an MRAI value of 30-seconds for eBGP updates, and while this can cut down on the chattiness of BGP, or even potential oscillation of updates, it also makes path hunting take longer. </p><p>At the next cycle of path hunting, even AS64501, which was previously still pointing toward a nonexistent /48 route from AS13335, should find the /32 advertisement is all that is left toward 2001:db8::1. Once it has done so, the traffic flow will become the following: </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5sCGMS95R8y32WTjnUigfN/1e5c9a7551c572a08596985edac5c17b/BLOG-3059_4.png" />
          </figure><p><sub><i>Diagram 3: Routing fallback to 2001:db8::/32 and 2001:db8::/48 is gone from DFZ</i></sub></p><p>This would mean BGP path hunting is over, and the Internet has realized that the 2001:db8::/32 is the best route available toward 2001:db8::1, and that 2001:db8::/48 is really gone. While in this example we’ve purposely made path hunting only last two cycles, in reality it can be far more, especially with how highly connected AS13335 is to thousands of peer networks and <a href="https://en.wikipedia.org/wiki/Tier_1_network"><u>Tier-1</u></a>’s globally. </p><p>Now that we’ve discussed BGP path hunting and how it works, you can probably already see how a BGP zombie outbreak can begin and how routing tables can become stuck for a lengthy period of time. Excessive BGP path hunting for a previously-known more-specific prefix can be an early indicator that a zombie could follow.</p>
    <div>
      <h2>Spawning a zombie</h2>
      <a href="#spawning-a-zombie">
        
      </a>
    </div>
    <p>Zombies have captured our attention more recently as they were noticed by some of our customers leveraging <a href="https://developers.cloudflare.com/byoip/"><u>Bring-Your-Own-IP (BYOIP)</u></a> on-demand advertisement for <a href="https://www.cloudflare.com/en-gb/network-services/products/magic-transit/"><u>Magic Transit</u></a>. BYOIP may be configured in two modes: "always-on", in which a prefix is continuously announced, or "on-demand", where a prefix is announced only when a customer chooses to. For some on-demand customers, announcement and withdrawal cycles <i>may</i> be a more frequent occurrence, which can lead to an increase in BGP zombies.</p><p>With that in mind and also knowing how path hunting works, let’s spawn our own zombie onto the Internet. To do so, we’ll take a spare block of IPv4 and IPv6 and announce them like so:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/20shWBMhqLR3tBMh50v7Uy/bf40e90c2f6a506a5bcfc9bafd1e31d2/BLOG-3059_5.png" />
          </figure><p>Once the routes are announced and stable, we’ll then proceed to withdraw the more specific routes advertised via Cloudflare globally. With a few quick clicks, we’ve successfully re-animated the dead.</p><p><i>Variant A: Ghoulish Gateways</i></p><p>One place zombies commonly occur is between upstream ISPs. When one router in a given ISP’s network is a little slower to update, routes can become stuck. </p><p>Take, for example, the following loop we observed between two of our upstream partners:</p>
            <pre><code>7. be2431.ccr31.sjc04.atlas.cogentco.com
8. tisparkle.sjc04.atlas.cogentco.com
9. 213.144.177.184
10. 213.144.177.184
11. 89.221.32.227
12. (waiting for reply)
13. be2749.rcr71.goa01.atlas.cogentco.com
14. be3219.ccr31.mrs02.atlas.cogentco.com
15. be2066.agr21.mrs02.atlas.cogentco.com
16. telecomitalia.mrs02.atlas.cogentco.com
17. 213.144.177.186
18. 89.221.32.227</code></pre>
            <p></p><p>Or this loop - observed on the same withdrawal test - between two different providers:  </p>
            <pre><code>15. if-bundle-12-2.qcore2.pvu-paris.as6453.net
16. if-bundle-56-2.qcore1.fr0-frankfurt.as6453.net
17. if-bundle-15-2.qhar1.fr0-frankfurt.as6453.net
18. 195.219.223.11
19. 213.144.177.186
20. 195.22.196.137
21. 213.144.177.186
22. 195.22.196.137</code></pre>
            <p><i></i></p><p><i>Variant B: Undead LAN (Local Area Network)</i></p><p>Simultaneously, zombies can occur entirely within a given network. When a route is withdrawn from Cloudflare’s network, each device in our network must individually begin the process of withdrawing the route. While this is generally a smooth process, things can still become stuck.</p><p>Take, for instance, a situation where one router inside of our network has not yet fully processed the withdrawal. Connectivity partners will continue routing traffic towards that router (as they have not yet received the withdrawal) while no host remains behind the router which is capable of actually processing the traffic. The result is an internal-only looping path:</p>
            <pre><code>10. 192.0.2.112
11. 192.0.2.113
12. 192.0.2.112
13. 192.0.2.113
14. 192.0.2.112
15. 192.0.2.113
16. 192.0.2.112
17. 192.0.2.113
18. 192.0.2.112
19. 192.0.2.113
</code></pre>
            <p></p><p>Unlike most fictionally-depicted hoards of the walking dead, our highly-visible zombie has a limited lifetime in most major networks – in this instance, only around around 6 minutes, after which most had re-converged around the less-specific as the best path. Sadly, this is on the shorter side – in some cases, we have seen long-lived zombies cause reachability issues for more than 10 minutes. It’s safe to say this is longer than most network operators would expect BGP convergence to take in a normal situation. </p><p>But, you may ask – is this the excessive path hunting we talked about earlier, or a BGP zombie? Really, it depends on the expectation and tolerance around <a href="https://dl.acm.org/doi/10.1145/3472305.3472315"><u>how long BGP convergence</u></a> should take to process the prefix withdrawal. In any case, even over 30 minutes after our withdrawal of our more-specific prefix, we are able to see zombie routes in the route-views public collectors easily:</p>
            <pre><code>~ % monocle search --start-ts 2025-10-28T12:40:13Z --end-ts 2025-10-28T13:00:13Z --prefix 198.18.0.0/24
A|1761656125.550447|206.82.105.116|54309|198.18.0.0/24|54309 13335 395747|IGP|206.82.104.31|0|0|54309:111|false|||route-views.ny

</code></pre>
            <p></p><p>You might argue that six to eleven minutes (or more) is a reasonable time for worst-case BGP convergence in the Tier-1 network layer, though that itself seems like a stretch. Even setting that aside, our data shows that very real BGP zombies exist in the global routing table, and they will negatively impact traffic. Curiously, we observed the path hunting delay is worse on IPv4, with the longest observed IPv6 impact in major (Tier-1) networks being just over 4 minutes. One could speculate this is in part due to the <a href="https://bgp.potaroo.net/index-bgp.html"><u>much higher number</u></a> of IPv4 prefixes in the Internet global routing table than the IPv6 global table, and how BGP speakers handle them separately.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4WOQBZb7MV2a5PA84xm6Be/28e252e5212781ae2d477150692605db/25x_10fps_a.gif" />
          </figure><p><sub><i>Source: RIPEstat’s BGPlay</i></sub></p><p>Part of the delay appears to originate from how interconnected AS13335 is; being heavily peered with a large portion of the Internet increases the likelihood of a route becoming stuck in a given location. Given that, perhaps a zombie would be shorter-lived if we operated in the opposite direction: announcing a less-specific persistently to 13335 and announcing more specifics via our local ISP during normal operation. Since the withdrawal will come from what is likely a less well-peered network, the time-to-convergence may be shorter:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4O7r3Nffpbus6ht3Eo6eiF/a7d577042e43c9b4da988cf9bd29f6fe/BLOG-3059_7.png" />
          </figure><p>Indeed, as predicted, we still get a stuck route, and it only lives for around 20 seconds in the Tier-1 network layer:</p>
            <pre><code>19. be12488.ccr42.ams03.atlas.cogentco.com
20. 38.88.214.142
21. be2020.ccr41.ams03.atlas.cogentco.com
22. 38.88.214.142
23. (waiting for reply)
24. 38.88.214.142
25. (waiting for reply)
26. 38.88.214.142
</code></pre>
            <p></p><p>Unfortunately, that 20 seconds is still an impactful 20 seconds - while better, it’s not where we want to be. The exact length of time will depend on the native ISP networks one is connected with, and it could certainly ease into the minutes worth of stuck routing. </p><p>In both cases, the initial time-to-announce yielded no loss, nor was a zombie created, as both paths remained valid for the entirety of their initial lifetime. Zombies were only created when a more specific prefix was fully withdrawn. A newly-announced route is not subject to path hunting in the same way a withdrawn more-specific route is. As they say, good (new) news travels fast.</p>
    <div>
      <h2>Lessening the zombie outbreak</h2>
      <a href="#lessening-the-zombie-outbreak">
        
      </a>
    </div>
    <p>Our findings lead us to believe that the withdrawal of a more-specific prefix may lead to zombies running rampant for longer periods of time. Because of this, we are exploring some improvements that make the consequences of BGP zombie routing less impactful for our customers relying on our on-demand BGP functionality.</p><p>For the traffic that <b>does</b> reach Cloudflare with stuck routes, we will introduce some BGP traffic forwarding improvements internally that allow for a more graceful withdrawal of traffic, even if routes are erroneously pointing toward us. In many ways, this will closely resemble the BGP <a href="https://www.rfc-editor.org/rfc/rfc1997.html"><u>well-known no-export</u></a> community’s functionality from our servers running BGP. This means even if we receive traffic from external parties due to stuck routing, we will still have the opportunity to deliver traffic to our far-end customers over a tunneled connection or via a <a href="https://www.cloudflare.com/network-services/products/network-interconnect/"><u>Cloudflare Network Interconnect</u></a> (CNI). We look forward to reporting back the positive impact after making this improvement for a more graceful draining of traffic by default. </p><p>For the traffic that <b>does not</b> reach Cloudflare’s edge, and instead loops between network providers, we need to use a different approach. Since we know more-specific to less-specific prefix routing fallback is more prone to BGP zombie outbreak, we are encouraging customers to instead use a multi-step draining process when they want traffic drained from the Cloudflare edge for an on-demand prefix without introducing route loops or blackhole events. The draining process when removing traffic for a BYOIP prefix from Cloudflare should look like this: </p><ol><li><p>The customer is already announcing an example prefix from Cloudflare, ex. 198.18.0.0/24</p></li><li><p>The customer begins <i>natively </i>announcing the prefix 198.18.0.0/24 (i.e. the same-length as the prefix they are advertising via Cloudflare) from their network to the Internet Service Providers that they wish to fail over traffic to.</p></li><li><p>After a few minutes, the customer signals BGP withdrawal from Cloudflare for the 198.18.0.0/24 prefix.</p></li></ol><p>The result is a clean cut over: impactful zombies are avoided because the same-length prefix (198.18.0.0/24) remains in the global routing table. Excessive path hunting is avoided because instead of routers needing to aggressively seek out a missing more-specific prefix match, they can fallback to the same-length announcement that persists in the routing table from the natively-originated path to the customer’s network.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/KmRvPsGUOp5PsNXcKCE1F/78f2c29c8c278d158972114df875ad0c/25x_10fps_b.gif" />
          </figure><p><sub><i>Source: RIPEstat’s BGPlay</i></sub></p>
    <div>
      <h2>What next?</h2>
      <a href="#what-next">
        
      </a>
    </div>
    <p>We are going to continue to refine our methods of measuring BGP zombies, so you can look forward to more insights in the future. There is also <a href="https://www.thousandeyes.com/bgp-stuck-route-observatory/"><u>work from others</u></a> in the <a href="https://blog.benjojo.co.uk/post/bgp-stuck-routes-tcp-zero-window"><u>community</u></a> around zombie measurement that is interesting and producing useful data. In terms of combatting the software bugs around BGP zombie creation, routing vendors should implement <a href="https://datatracker.ietf.org/doc/html/rfc9687"><u>RFC9687</u></a>, the BGP SendHoldTimer. The general idea is that a local router can detect via the SendHoldTimer if the far-end router stops processing BGP messages unexpectedly, which lowers the possibility of zombies becoming stuck for long periods of time. </p><p>In addition, it’s worth keeping in mind our observations made in this post about more-specific prefix announcements and excessive path hunting. If as a network operator you rely on more-specific BGP prefix announcements for failover, or for traffic engineering, you need to be aware that routes could become stuck for a longer period of time before full BGP convergence occurs.</p><p>If you’re interested in problems like BGP zombies, consider <a href="https://www.cloudflare.com/en-gb/careers/jobs/?location=default"><u>coming to work</u></a> at Cloudflare or applying for an <a href="https://www.cloudflare.com/en-gb/careers/early-talent/"><u>internship</u></a>. Together we can help build a better Internet!  </p> ]]></content:encoded>
            <category><![CDATA[BGP]]></category>
            <category><![CDATA[Routing]]></category>
            <category><![CDATA[Network]]></category>
            <category><![CDATA[BYOIP]]></category>
            <guid isPermaLink="false">6Qk6krBb9GkFrf67N6NhyW</guid>
            <dc:creator>Bryton Herdes</dc:creator>
            <dc:creator>June Slater</dc:creator>
            <dc:creator>Mingwei Zhang</dc:creator>
        </item>
        <item>
            <title><![CDATA[QUIC action: patching a broadcast address amplification vulnerability]]></title>
            <link>https://blog.cloudflare.com/mitigating-broadcast-address-attack/</link>
            <pubDate>Mon, 10 Feb 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare was recently contacted by researchers who discovered a broadcast amplification vulnerability through their QUIC Internet measurement research. We've implemented a mitigation. ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare was recently contacted by a group of anonymous security researchers who discovered a broadcast amplification vulnerability through their <a href="https://blog.cloudflare.com/tag/quic"><u>QUIC</u></a> Internet measurement research. Our team collaborated with these researchers through our Public Bug Bounty program, and worked to fully patch a dangerous vulnerability that affected our infrastructure.</p><p>Since being notified about the vulnerability, we've implemented a mitigation to help secure our infrastructure. According to our analysis, we have fully patched this vulnerability and the amplification vector no longer exists. </p>
    <div>
      <h3>Summary of the amplification attack</h3>
      <a href="#summary-of-the-amplification-attack">
        
      </a>
    </div>
    <p>QUIC is an Internet transport protocol that is encrypted by default. It offers equivalent features to <a href="https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/"><u>TCP</u></a> (Transmission Control Protocol) and <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/"><u>TLS</u></a> (Transport Layer Security), while using a shorter handshake sequence that helps reduce connection establishment times. QUIC runs over <a href="https://www.cloudflare.com/en-gb/learning/ddos/glossary/user-datagram-protocol-udp/"><u>UDP</u></a> (User Datagram Protocol).</p><p>The researchers found that a single client QUIC <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-17.2.2"><u>Initial packet</u></a> targeting a broadcast IP destination address could trigger a large response of initial packets. This manifested as both a server CPU amplification attack and a reflection amplification attack.</p>
    <div>
      <h3>Transport and security handshakes</h3>
      <a href="#transport-and-security-handshakes">
        
      </a>
    </div>
    <p>When using TCP and TLS there are two handshake interactions. First, is the TCP 3-way transport handshake. A client sends a SYN packet to a server, it responds with a SYN-ACK to the client, and the client responds with an ACK. This process validates the client IP address. Second, is the TLS security handshake. A client sends a ClientHello to a server, it carries out some cryptographic operations and responds with a ServerHello containing a server certificate. The client verifies the certificate, confirms the handshake and sends application traffic such as an HTTP request.</p><p><a href="https://datatracker.ietf.org/doc/html/rfc9000#section-7"><u>QUIC</u></a> follows a similar process, however the sequence is shorter because the transport and security handshake is combined. A client sends an Initial packet containing a ClientHello to a server, it carries out some cryptographic operations and responds with an Initial packet containing a ServerHello with a server certificate. The client verifies the certificate and then sends application data.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7wsMcFwy8xMRYwQyFNm6oC/5d131543e7704794776dfc3ed89c1693/image2.png" />
          </figure><p>The QUIC handshake does not require client IP address validation before starting the security handshake. This means there is a risk that an attacker could spoof a client IP and cause a server to do cryptographic work and send data to a target victim IP (aka a <a href="https://blog.cloudflare.com/reflections-on-reflections/"><u>reflection attack</u></a>). <a href="https://datatracker.ietf.org/doc/html/rfc9000"><u>RFC 9000</u></a> is careful to describe the risks this poses and provides mechanisms to reduce them (for example, see Sections <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-8"><u>8</u></a> and <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-9.3.1"><u>9.3.1</u></a>). Until a client address is verified, a server employs an anti-amplification limit, sending a maximum of 3x as many bytes as it has received. Furthermore, a server can initiate address validation before engaging in the cryptographic handshake by responding with a <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-8.1.2"><u>Retry packet</u></a>. The retry mechanism, however, adds an additional round-trip to the QUIC handshake sequence, negating some of its benefits compared to TCP. Real-world QUIC deployments use a range of strategies and heuristics to detect traffic loads and enable different mitigations.</p><p>In order to understand how the researchers triggered an amplification attack despite these QUIC guardrails, we first need to dive into how IP broadcast works.</p>
    <div>
      <h3>Broadcast addresses</h3>
      <a href="#broadcast-addresses">
        
      </a>
    </div>
    <p>In Internet Protocol version 4 (IPv4) addressing, the final address in any given <a href="https://www.cloudflare.com/learning/network-layer/what-is-a-subnet/"><u>subnet</u></a> is a special broadcast IP address used to send packets to every node within the IP address range. Every node that is within the same subnet receives any packet that is sent to the broadcast address, enabling one sender to send a message that can be “heard” by potentially hundreds of adjacent nodes. This behavior is enabled by default in most network-connected systems and is critical for discovery of devices within the same IPv4 network.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/49zGjFbeIv7RxZMM6W2i5V/9e9e5f2f3bd8401467887d488930f476/image3.png" />
          </figure><p>The broadcast address by nature poses a risk of DDoS amplification; for every one packet sent, hundreds of nodes have to process the traffic. </p>
    <div>
      <h3>Dealing with the expected broadcast</h3>
      <a href="#dealing-with-the-expected-broadcast">
        
      </a>
    </div>
    <p>To combat the risk posed by broadcast addresses, by default most routers reject packets originating from outside their IP subnet which are targeted at the broadcast address of networks for which they are locally connected. Broadcast packets are only allowed to be forwarded within the same IP subnet, preventing attacks from the Internet from targeting servers across the world.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5TU3GO26KOJgzLHcS9Uxiu/6cd334afc3925b1713b7e706decc7269/image1.png" />
          </figure><p>The same techniques are not generally applied when a given router is not directly connected to a given subnet. So long as an address is not locally treated as a broadcast address, <a href="https://www.cloudflare.com/learning/security/glossary/what-is-bgp/"><u>Border Gateway Protocol</u></a> (BGP) or other routing protocols will continue to route traffic from external IPs toward the last IPv4 address in a subnet. Essentially, this means a “broadcast address” is only relevant within a local scope of routers and hosts connected together via Ethernet. To routers and hosts across the Internet, a broadcast IP address is routed in the same way as any other IP.</p>
    <div>
      <h3>Binding IP address ranges to hosts</h3>
      <a href="#binding-ip-address-ranges-to-hosts">
        
      </a>
    </div>
    <p>Each Cloudflare server is expected to be capable of serving content from every website on the Cloudflare network. Because our network utilizes <a href="https://www.cloudflare.com/learning/cdn/glossary/anycast-network/"><u>Anycast</u></a> routing, each server necessarily needs to be listening on (and capable of returning traffic from) every Anycast IP address in use on our network.</p><p>To do so, we take advantage of the loopback interface on each server. Unlike a physical network interface, all IP addresses within a given IP address range are made available to the host (and will be processed locally by the kernel) when bound to the loopback interface.</p><p>The mechanism by which this works is straightforward. In a traditional routing environment, <a href="https://en.wikipedia.org/wiki/Longest_prefix_match"><u>longest prefix matching</u></a> is employed to select a route. Under longest prefix matching, routes towards more specific blocks of IP addresses (such as 192.0.2.96/29, a range of 8 addresses) will be selected over routes to less specific blocks of IP addresses (such as 192.0.2.0/24, a range of 256 addresses).</p><p>While Linux utilizes longest prefix matching, it consults an additional step — the Routing Policy Database (RPDB) — before immediately searching for a match. The RPDB contains a list of routing tables which can contain routing information and their individual priorities. The default RPDB looks like this:</p>
            <pre><code>$ ip rule show
0:	from all lookup local
32766:	from all lookup main
32767:	from all lookup default</code></pre>
            <p>Linux will consult each routing table in ascending numerical order to try and find a matching route. Once one is found, the search is terminated and the route immediately used.</p><p>If you’ve previously worked with routing rules on Linux, you are likely familiar with the contents of the main table. Contrary to the existence of the table named “default”, “main” generally functions as the default lookup table. It is also the one which contains what we traditionally associate with route table information:</p>
            <pre><code>$ ip route show table main
default via 192.0.2.1 dev eth0 onlink
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.2</code></pre>
            <p>This is, however, not the first routing table that will be consulted for a given lookup. Instead, that task falls to the local table:</p>
            <pre><code>$ ip route show table local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
local 192.0.2.2 dev eth0 proto kernel scope host src 192.0.2.2
broadcast 192.0.2.255 dev eth0 proto kernel scope link src 192.0.2.2</code></pre>
            <p>Looking at the table, we see two new types of routes — local and broadcast. As their names would suggest, these routes dictate two distinctly different functions: routes that are handled locally and routes that will result in a packet being broadcast. Local routes provide the desired functionality — any prefix with a local route will have all IP addresses in the range processed by the kernel. Broadcast routes will result in a packet being broadcast to all IP addresses within the given range. Both types of routes are added automatically when an IP address is bound to an interface (and, when a range is bound to the loopback (lo) interface, the range itself will be added as a local route).</p>
    <div>
      <h3>Vulnerability discovery</h3>
      <a href="#vulnerability-discovery">
        
      </a>
    </div>
    <p>Deployments of QUIC are highly dependent on the load-balancing and packet forwarding infrastructure that they sit on top of. Although QUIC’s RFCs describe risks and mitigations, there can still be attack vectors depending on the nature of server deployments. The reporting researchers studied QUIC deployments across the Internet and discovered that sending a QUIC Initial packet to one of Cloudflare’s broadcast addresses triggered a flood of responses. The aggregate amount of response data exceeded the RFC's 3x amplification limit.</p><p>Taking a look at the local routing table of an example Cloudflare system, we see a potential culprit:</p>
            <pre><code>$ ip route show table local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
local 192.0.2.2 dev eth0 proto kernel scope host src 192.0.2.2
broadcast 192.0.2.255 dev eth0 proto kernel scope link src 192.0.2.2
local 203.0.113.0 dev lo proto kernel scope host src 203.0.113.0
local 203.0.113.0/24 dev lo proto kernel scope host src 203.0.113.0
broadcast 203.0.113.255 dev lo proto kernel scope link src 203.0.113.0</code></pre>
            <p>On this example system, the anycast prefix 203.0.113.0/24 has been bound to the loopback interface (lo) through the use of standard tooling. Acting dutifully under the standards of IPv4, the tooling has assigned both special types of routes — a local one for the IP range itself and a broadcast one for the final address in the range — to the interface.</p><p>While traffic to the broadcast address of our router’s directly connected subnet is filtered as expected, broadcast traffic targeting our routed anycast prefixes still arrives at our servers themselves. Normally, broadcast traffic arriving at the loopback interface does little to cause problems. Services bound to a specific port across an entire range will receive data sent to the broadcast address and continue as normal. Unfortunately, this relatively simple trait breaks down when normal expectations are broken.</p><p>Cloudflare’s frontend consists of several worker processes, each of which independently binds to the entire anycast range on UDP port 443. In order to enable multiple processes to bind to the same port, we use the SO_REUSEPORT socket option. While SO_REUSEPORT <a href="https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/"><u>has additional benefits</u></a>, it also causes traffic sent to the broadcast address to be copied to every listener.</p><p>Each individual QUIC server worker operates in isolation. Each one reacted to the same client Initial, duplicating the work on the server side and generating response traffic to the client's IP address. Thus, a single packet could trigger a significant amplification. While specifics will vary by implementation, a typical one-listener-per-core stack (which sends retries in response to presumed timeouts) on a 128-core system could result in 384 replies being generated and sent for each packet sent to the broadcast address.</p><p>Although the researchers demonstrated this attack on QUIC, the underlying vulnerability can affect other UDP request/response protocols that use sockets in the same way.</p>
    <div>
      <h3>Mitigation</h3>
      <a href="#mitigation">
        
      </a>
    </div>
    <p>As a communication methodology, broadcast is not generally desirable for anycast prefixes. Thus, the easiest method to mitigate the issue was simply to disable broadcast functionality for the final address in each range.</p><p>Ideally, this would be done by modifying our tooling to only add the local routes in the local routing table, skipping the inclusion of the broadcast ones altogether. Unfortunately, the only practical mechanism to do so would involve patching and maintaining our own internal fork of the iproute2 suite, a rather heavy-handed solution for the problem at hand.</p><p>Instead, we decided to focus on removing the route itself. Similar to any other route, it can be removed using standard tooling:</p>
            <pre><code>$ sudo ip route del 203.0.113.255 table local</code></pre>
            <p>To do so at scale, we made a relatively minor change to our deployment system:</p>
            <pre><code>  {%- for lo_route in lo_routes %}
    {%- if lo_route.type == "broadcast" %}
        # All broadcast addresses are implicitly ipv4
        {%- do remove_route({
        "dev": "lo",
        "dst": lo_route.dst,
        "type": "broadcast",
        "src": lo_route.src,
        }) %}
    {%- endif %}
  {%- endfor %}</code></pre>
            <p>In doing so, we effectively ensure that all broadcast routes attached to the loopback interface are removed, mitigating the risk by ensuring that the specification-defined broadcast address is treated no differently than any other address in the range.</p>
    <div>
      <h3>Next steps </h3>
      <a href="#next-steps">
        
      </a>
    </div>
    <p>While the vulnerability specifically affected broadcast addresses within our anycast range, it likely expands past our infrastructure. Anyone with infrastructure that meets the relatively narrow criteria (a multi-worker, multi-listener UDP-based service that is bound to all IP addresses on a machine with routable IP prefixes attached in such a way as to expose the broadcast address) will be affected unless mitigations are in place. We encourage network administrators and security professionals to assess their systems for configurations that may present a local amplification attack vector.</p> ]]></content:encoded>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Network]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[HTTP3]]></category>
            <category><![CDATA[Bug Bounty]]></category>
            <guid isPermaLink="false">6ZaxgQxDACeIF6MZAquLPV</guid>
            <dc:creator>Josephine Chow</dc:creator>
            <dc:creator>June Slater</dc:creator>
            <dc:creator>Bryton Herdes</dc:creator>
            <dc:creator>Lucas Pardue</dc:creator>
        </item>
    </channel>
</rss>