RFC 5393

Addressing an Amplification Vulnerability in Session Initiation Protocol (SIP) Forking Proxies

๐Ÿ“… published December 2008, from ietf

Addressing an Amplification Vulnerability in Session Initiation Protocol (SIP) Forking Proxies

Okay bestie, let's get into RFC 5393 ๐Ÿฆ‹โœจ It was published in December 2008 under the Network Working Group. R. Sparks, Ed. (Tekelec) led the writing, with help from S. Lawrence (Nortel Networks, Inc.), A. Hawrylyshen (Ditech Networks Inc.), and B. Campen (Tekelec). It's a Standards Track document, and it updates RFC 3261 ๐Ÿ’– The title block doesn't list anything it obsoletes, so nothing gets replaced here, just patched up. The whole point is fixing a loophole in how SIP proxies handle forked requests ๐Ÿ”ฅ

Network Working Group                                     R. Sparks, Ed.
Request for Comments: 5393                                       Tekelec
Updates: 3261                                                S. Lawrence
Category: Standards Track                          Nortel Networks, Inc.
                                                          A. Hawrylyshen
                                                    Ditech Networks Inc.
                                                               B. Campen
                                                                 Tekelec
                                                           December 2008

Status of This Memo

Okay so this doc is a standards track protocol spec for the Internet community, and it's literally asking people for discussion and suggestions ๐Ÿซถ If you want to know where this protocol stands right now, check the current edition of the "Internet Official Protocol Standards" (STD 1), which is the official source for that ๐Ÿ“‹ Distribution of this memo is unlimited, so share it with your whole group chat ๐Ÿ’…

Copyright (c) 2008 IETF Trust and the persons identified as the document authors. All rights reserved. ๐ŸŽ€

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they were in effect on the date it was published. Read those carefully, because they spell out your rights and restrictions for this document ๐Ÿง

Abstract

This document normatively updates RFC 3261, the Session Initiation Protocol (SIP), to fix a security hole in how SIP proxies behave ๐Ÿšจ The bad part is that a small number of requests that are legitimate, and even authorized, can trigger a massive flood of proxy-to-proxy traffic across SIP networks ๐Ÿ’€

The document tightens the loop-detection requirements for SIP proxies that fork requests, meaning they forward one request to more than one destination ๐ŸŒ€ It also fixes and clarifies how the loop-detection algorithm works, since those proxies are required to implement it ๐Ÿ”ง On top of that, it introduces a Max-Breadth mechanism that caps how many concurrent branches can be chased for any given request ๐ŸŒธ

Table of Contents

   1. Introduction ....................................................3
   2. Conventions and Definitions .....................................3
   3. Vulnerability: Leveraging Forking to Flood a Network ............3
   4. Updates to RFC 3261 .............................................7
      4.1. Strengthening the Requirement to Perform Loop Detection ....7
      4.2. Correcting and Clarifying the RFC 3261
           Loop-Detection Algorithm ...................................7
           4.2.1. Update to Section 16.6 ..............................7
           4.2.2. Update to Section 16.3 ..............................8
           4.2.3. Impact of Loop Detection on Overall Network
                  Performance .........................................9
           4.2.4. Note to Implementers ................................9
   5. Max-Breadth ....................................................10
      5.1. Overview ..................................................10
      5.2. Examples ..................................................11
      5.3. Formal Mechanism ..........................................12
           5.3.1. Max-Breadth Header Field ...........................12
           5.3.2. Terminology ........................................13
           5.3.3. Proxy Behavior .....................................13
                  5.3.3.1. Reusing Max-Breadth .......................14
           5.3.4. UAC Behavior .......................................14
           5.3.5. UAS Behavior .......................................14
      5.4. Implementer Notes .........................................14
           5.4.1. Treatment of CANCEL ................................14
           5.4.2. Reclamation of Max-Breadth on 2xx Responses ........14
           5.4.3. Max-Breadth and Automaton UAs ......................14
      5.5. Parallel and Sequential Forking ...........................15
      5.6. Max-Breadth Split Weight Selection ........................15
      5.7. Max-Breadth's Effect on Forking-Based
           Amplification Attacks .....................................15
      5.8. Max-Breadth Header Field ABNF Definition ..................16
   6. IANA Considerations ............................................16
      6.1. Max-Breadth Header Field ..................................16
      6.2. 440 Max-Breadth Exceeded Response .........................16
   7. Security Considerations ........................................16
      7.1. Alternate Solutions That Were Considered and Rejected .....17
   8. Acknowledgments ................................................19
   9. References .....................................................19
      9.1. Normative References ......................................19
      9.2. Informative References ....................................19

1. Introduction โœจ

Okay so interoperability testing found a vulnerability in how forking SIP proxies behave, the ones defined in [RFC3261] ๐Ÿ˜ญ It can be abused so that a small handful of perfectly valid SIP requests produce an absolutely enormous pile of proxy-to-proxy messages. One version of the attack shows fewer than ten messages stimulating potentially 2^71 messages. Iconic? Terrifying? Both ๐Ÿ’€

This document makes normative changes to the SIP protocol to patch it up ๐Ÿ› ๏ธ With this update, whenever a SIP proxy forks a request to more than one destination, it's required to make sure it isn't taking part in a request loop ๐Ÿ”

Now the tea โ˜• That normative update on its own is not enough to protect against crafted versions of the attack that use multiple Addresses of Record (AORs). So the document also defines the Max-Breadth mechanism, which limits the total number of concurrent branches a forked SIP request can spawn ๐ŸŒˆ Just know that it only limits concurrency. It doesn't cap the total number of branches a request can travel through over its whole lifetime ๐Ÿ™ˆ

The mechanisms in this update protect against the variations of the attack that only use a small number of resources. That includes most of the unintentional, self-inflicted ones caused by accidental misconfiguration ๐Ÿงฏ

Real talk though, an attacker with access to a large enough number of distinct resources can still make a huge number of messages happen ๐Ÿ˜ฌ Max-Breadth caps how many run at once, so the whole flood gets stretched out over a long period of time. That gives operators a better shot at spotting the attack and fixing things outside the protocol itself ๐Ÿ‘€ Stopping this particular form of the attack will take future protocol work ๐Ÿ”ฎ

2. Conventions and Definitions ๐Ÿ’…

Here's the key-word situation, bestie ๐ŸŽ€ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [RFC2119] ๐Ÿ“š

3. Vulnerability: Leveraging Forking to Flood a Network ๐Ÿ’ฅ

okay bestie, this section is the attack playbook ๐Ÿ’€๐Ÿ”ฅ It starts with a simplifying assumption: an attacker has two accounts on each of two RFC 3261 compliant proxy/registrar servers, and neither server does loop detection. The attack doesn't actually need that setup, but it makes the scenario way easier to picture. It can also be pulled off with a single account on a single server ๐ŸŽ€

Okay so picture two proxy/registrar services, P1 and P2, and four Addresses of Record: a@P1, b@P1, a@P2, and b@P2. The attacker sets up bindings to all of these with normal REGISTER requests. (The boring details are left out ๐Ÿ™„)

        REGISTER sip:P1 SIP/2.0
        To: <sip:a@P1>
        Contact: <sip:a@P2>, <sip:b@P2>

        REGISTER sip:P1 SIP/2.0
        To: <sip:b@P1>
        Contact: <sip:a@P2>, <sip:b@P2>

        REGISTER sip:P2 SIP/2.0
        To: <sip:a@P2>
        Contact: <sip:a@P1>, <sip:b@P1>

        REGISTER sip:P2 SIP/2.0
        To: <sip:b@P2>
        Contact: <sip:a@P1>, <sip:b@P1>

Once those bindings are in place, the attacker sends one INVITE to any of the four AORs, say a@P1. That request forks into two requests handled by P2, which fork into four requests handled by P1, which fork into eight messages handled by P2, and so on ๐Ÿ˜ญ Figure 1 shows the whole thing.

                                   |
                                 a@P1
                               /       \
                             /           \
                           /               \
                         /                   \
                      a@P2                   b@P2
                      /  \                   /  \
                    /      \               /      \
                   /        \             /        \
                 a@P1       b@P1        a@P1       b@P1
                 /  \       /  \        /  \       /  \
              a@P2  b@P2 a@P2  b@P2  a@P2  b@P2 a@P2  b@P2
               /\    /\   /\    /\    /\    /\   /\    /\
                                   .
                                   .
                                   .

Figure 1: Attack Request Propagation ๐ŸŒˆ

The requests keep multiplying down the tree until Max-Forwards hits zero. If the endpoint and the two proxies all follow the RFC 3261 recommendations, the tree is 70 rows deep, which works out to 2^71-1 requests ๐Ÿคฏ The real number could be way bigger if the proxies take longer to process that whole tree than Timer C at either proxy. Then you also get a storm of 408 responses and/or a storm of CANCEL requests riding along with the INVITEs. Remember, there are only two proxies here, and each one has to hold state for every transaction it sees. Near the end, that's at least 2^70 transactions active at the same time. Not a vibe ๐Ÿ’€

Now the single-account version. The attack still works with one account on one server if the service can be convinced that contacts with different attributes (parameters, schemes, embedded headers) are distinct enough, and those parameters aren't used in AOR comparisons when forwarding a new request. RFC 3261 says that all URI parameters must be removed from a URI before it's looked up in a location service, and that the URIs in the Contact header field are compared with URI equality. So the attacker can get away with a single REGISTER request to a single account, like this ๐Ÿ™ˆ

REGISTER sip:P1 SIP/2.0
To: <sip:a@P1>
Contact: <sip:a@P1;unknown-param=whack>,<sip:a@P1;unknown-param=thud>

This actually happened at one of the SIP Interoperability Test (SIPit) sessions ๐Ÿ˜ณ The scenario got extended to more than two proxies, and every participating proxy capped Max-Forwards at 20. After a handful of messages to set up the attack, the proxies started bombarding each other. Based on the several hours the experiment ran, the scenario would have wrapped up in just under 10 days. If the proxies had used the RFC 3261 recommended Max-Forwards value of 70 instead, and assuming they performed linearly as the state they held grew, finishing the processing of that one INVITE would have taken 3 trillion years ๐Ÿ’€ It's also interesting that a few proxies rebooted during the test and rejoined the attack when they came back up, as long as they kept their registration state across reboots. So if this attack were launched on the Internet at large, stopping it might take coordination among all the affected elements ๐Ÿซ 

Loop detection, as defined in this document, would have stopped the attack right away at any of the proxies in the scenarios so far. (If every proxy involved implemented this loop detection, the first scenario's stimulated messages would drop to 14, and the one-server variation would drop to 10.) But there's a variant that uses multiple AORs where loop detection alone isn't enough protection ๐Ÿ˜ค In this version, each participating AOR forks to all the other participating AORs. With a small number of AORs (10, for example), paths through the resulting tree won't loop until a huge number of messages has been generated. And getting enough AORs to run this on today's networks is totally doable.

In this scenario, requests often take many hops before they complete a loop, and a ton of different loops happen during the attack. Specifically, if N is the number of participating AORs, and N is less than or equal to Max-Forwards, the attack generates more than N! of traffic, even if every proxy involved is doing loop detection ๐Ÿ“ˆ

Let's say we have N AORs, and each one is set up to fork to the entire set. For clarity, AOR 1 is where the attack starts. Every permutation of the remaining N-1 AORs plays out, which defines (N-1)! distinct paths that never repeat an AOR. Then each of those paths forks N ways one last time, and a loop gets detected on each of those branches. Those final branches alone add up to N! requests ((N-1)! paths, with N forks at the end of each path) ๐Ÿงฎ

                    ___N____Requests_
                    |  1 |         1 |
                    |  2 |         4 |
                    |  3 |        15 |
                    |  4 |        64 |
                    |  5 |       325 |
                    |  6 |      1956 |
                    |  7 |     13699 |
                    |  8 |    109600 |
                    |  9 |    986409 |
                    | 10 |   9864100 |

Forwarded Requests vs. Number of Participating AORs ๐Ÿ“Š

Even if every proxy in the network does loop detection, an attacker still gets rapidly growing returns from each extra AOR they can use. The Max-Breadth mechanism defined in this document is designed to limit how well this variation of the attack works ๐Ÿ›ก๏ธ

One more thing, and it's a big one ๐Ÿ‘€ In all of these scenarios, every forking proxy could add an extra branch pointing at a single victim. That victim might not even be a SIP-aware element. The result is a massive flood of traffic aimed at the victim, coming from potentially as many sources as there are AORs taking part in the attack. Yikes ๐Ÿ˜ญ

4. Updates to RFC 3261 โœจ

4.1. Strengthening the Requirement to Perform Loop Detection

Okay, these requirements are how we keep a proxy from falling victim to the attack in this document ๐Ÿซถ

When a SIP proxy forks a particular request to more than one location, it MUST ensure that request is not looping through this proxy. It is RECOMMENDED that proxies meet this requirement by performing the loop-detection steps defined in this document ๐Ÿ’…

The requirement to use this document's refinement of the loop-detection algorithm from RFC 3261 is set at should-strength. That leaves room for future Standards-Track mechanisms that let a proxy figure out it isn't looping. For example, a proxy forking to destinations set up with the sip-outbound mechanism [OUTBOUND] would know those branches won't loop ๐Ÿง 

A SIP proxy forwarding a request to only one location MAY perform loop detection, but it isn't required to. When it forwards to just one location, the amplification risk being exploited isn't there, and Max-Forwards protects the network to the extent it was designed to. Just always keep in mind the constant multiplier from using up Max-Forwards while not forking ๐ŸŒธ A proxy also isn't required to perform loop detection when it forwards a request to a single location, even if it previously forked that same request (and did loop detection) somewhere else along the way.

4.2. Correcting and Clarifying the RFC 3261 Loop-Detection Algorithm ๐ŸŽ€

4.2.1. Update to Section 16.6 โœจ

okay bestie, this section replaces the whole of item 8 in Section 16.6 of RFC 3261 ๐Ÿ“„ (item 8 starts on page 105 and ends on page 106 of RFC 3261).

8. Add a Via Header Field Value ๐ŸŽ€

The proxy MUST insert a Via header field value into the copy before the existing Via header field values. The construction of this value follows the same guidelines of Section 8.1.1.7. This implies that the proxy will compute its own branch parameter, which will be globally unique for that branch, and will contain the requisite magic cookie ๐Ÿ“. Note that following only the guidelines in Section 8.1.1.7 will result in a branch parameter that will be different for different instances of a spiraled or looped request through a proxy ๐ŸŒ€.

Proxies required to perform loop detection by RFC 5393 have an additional constraint on the value they place in the Via header field. Such proxies SHOULD create a branch value separable into two parts in any implementation-dependent way โœจ.

The rest of this section assumes those two parts exist ๐Ÿ’–. If a proxy chooses to employ some other mechanism, it is the implementer's responsibility to verify that the detection properties defined by the requirements placed on these two parts are achieved ๐Ÿซถ.

The first part of the branch value MUST satisfy the constraints of Section 8.1.1.7. The second part is used to perform loop detection and distinguish loops from spirals ๐ŸŒˆ.

This second part MUST vary with any field used by the location service logic in determining where to retarget or forward this request. This is necessary to distinguish looped requests from spirals by allowing the proxy to recognize if none of the values affecting the processing of the request have changed ๐Ÿ‘‘. Hence, the second part MUST depend at least on the received Request-URI and any Route header field values used when processing the received request. Implementers need to take care to include all fields used by the location service logic in that particular implementation ๐Ÿ’….

This second part MUST NOT vary with the request method. CANCEL and non-200 ACK requests MUST have the same branch parameter value as the corresponding request they cancel or acknowledge. This branch parameter value is used in correlating those requests at the server handling them (see Sections 17.2.3 and 9.2) ๐Ÿฆ‹.


4.2.2. Update to Section 16.3 ๐Ÿ’œ

this section replaces the whole of item 4 in Section 16.3 of RFC 3261 (item 4 appears on page 95 of RFC 3261) ๐Ÿ“–.

4. Loop-Detection Check ๐Ÿ”ฅ

Proxies required to perform loop detection by RFC 5393 MUST perform the following loop-detection test before forwarding a request. Each Via header field value in the request whose sent-by value matches a value placed into previous requests by this proxy MUST be inspected for the "second part" defined in Section 4.2.1 of RFC 5393. This second part will not be present if the message was not forked when that Via header field value was added ๐Ÿ™ˆ. If the second field is present, the proxy MUST perform the second-part calculation described in Section 4.2.1 of RFC 5393 on this request and compare the result to the value from the Via header field. If these values are equal, the request has looped and the proxy MUST reject the request with a 482 (Loop Detected) response ๐Ÿšซ. If the values differ, the request is spiraling and processing continues to the next step ๐ŸŒ€โœจ.


4.2.3. Impact of Loop Detection on Overall Network Performance ๐Ÿ“Š

these requirements, plus the recommendation to use the loop-detection mechanisms in this document, make a pretty favorable trade ๐Ÿค: exponential message growth gets swapped for work that is, at worst, order n^2 as a message crosses n proxies. Specifically, this work is order m*n, where m is the number of proxies in the path that fork the request to more than one location. In practice, m is expected to be small ๐Ÿงš.

The loop-detection algorithm expressed in this document requires a proxy to inspect each Via element in a received request. In the worst case, where a message crosses N proxies and each of them loop-detects, proxy k does k inspections. The overall number of inspections spread across the proxies handling this request is the sum of k from k=1 to k=N, which is N(N+1)/2 ๐Ÿงฎ๐Ÿ’€.


4.2.4. Note to Implementers ๐Ÿ“

okay implementers, listen up ๐Ÿ‘‚. A common way to create the second part of the branch parameter value when forking a request is to compute a hash over the concatenation of the Request-URI, any Route header field values used during processing the request, and any other values used by the location service logic while processing this request ๐ŸŒธ. The hash should be chosen so that there is a low probability that two distinct sets of these parameters will collide. Because the maximum number of inputs that need to be compared is 70, the chance of a collision is low even with a relatively small hash value, such as 32 bits โœจ. CRC-32c as specified in [RFC4960] is a specific acceptable function, as is MD5 [RFC1321]. Note that MD5 is being chosen purely for non-cryptographic properties. An attacker who can control the inputs in order to produce a hash collision can attack the connection in a variety of other ways ๐Ÿ˜ฑ. When forming the second part using a hash, implementations SHOULD include at least one field in the input to the hash that varies between different transactions attempting to reach the same destination, to avoid repeated failure should the hash collide. The Call-ID and CSeq fields would be good inputs for this purpose ๐Ÿซถ.

A common point of failure to interoperate at SIPit events has been parsers objecting to the contents of another element's Via header field values when inspecting the Via stack for loops ๐Ÿ˜ค. Implementers need to take care to avoid making assumptions about the format of another element's Via header field value beyond the basic constraints placed on that format by RFC 3261. In particular, parsing a header field value with unknown parameter names, parameters with no values, or parameter values with or without quoted strings must not cause an implementation to fail ๐Ÿ™…โ€โ™€๏ธ.

Removing, obfuscating, or in any other way modifying the branch parameter values in Via header fields in a received request before forwarding it removes the ability for the node that placed that branch parameter into the message to perform loop detection. If two elements in a loop modify branch parameters this way, a loop can never be detected ๐Ÿ’€๐Ÿšจ.

5. Max-Breadth ๐Ÿฆ‹

5.1. Overview ๐Ÿ’…

the Max-Breadth mechanism defined here limits the total number of concurrent branches caused by a forked SIP request ๐ŸŒณ. With this mechanism, all proxyable requests are assigned a positive integral Max-Breadth value, which denotes the maximum number of concurrent branches this request may spawn through parallel forking as it is forwarded from its current point ๐ŸŽฏ. When a proxy forwards a request, its Max-Breadth value is divided among the outgoing requests. In turn, each of the forwarded requests has a limit on how many concurrent branches it may spawn. As branches complete, their portion of the Max-Breadth value becomes available for subsequent branches, if needed ๐Ÿ”„. If there is insufficient Max-Breadth to carry out a desired parallel fork, a proxy can return the 440 (Max-Breadth Exceeded) response defined in this document ๐Ÿ™…โ€โ™€๏ธ.

This mechanism operates independently from Max-Forwards. Max-Forwards limits the depth of the tree a request may traverse as it is forwarded from its origination point to each destination it is forked to ๐ŸŒฒ. As Section 3 shows, the number of branches in a tree of even limited depth can be made large (exponential with depth) by leveraging forking ๐Ÿคฏ. Each such branch has a pair of SIP transaction state machines associated with it. The Max-Breadth mechanism limits the number of branches that are active (those that have running transaction state machines) at any given point in time โฑ๏ธ.

Max-Breadth does not prevent forking. It only limits the number of concurrent parallel forked branches. In particular, a Max-Breadth of 1 restricts a request to pure serial forking rather than restricting it from being forked at all ๐Ÿงต.

A client receiving a 440 (Max-Breadth Exceeded) response can infer that its request did not reach all possible destinations ๐Ÿ‘€. Recovery options are similar to those when receiving a 483 (Too Many Hops) response, and include affecting the routing decisions through whatever mechanisms are appropriate to result in a less broad search, or refining the request itself before submission to make the search space smaller ๐Ÿ”๐Ÿ’–.

5.2. Examples โœจ

okay bestie, let's look at some diagrams ๐Ÿฆ‹ The first one is Parallel Forking: the UAC sends an INVITE with Max-Breadth: 60 and Max-Forwards: 70, Proxy A forwards it to Proxy B and Proxy C with Max-Breadth: 30 each, and the Max-Forwards drops to 69 on the way.

    UAC                 Proxy A              Proxy B             Proxy C
     | INVITE              |                    |                   |
     | Max-Breadth: 60     | INVITE             |                   |
     | Max-Forwards: 70    | Max-Breadth: 30    |                   |
     |-------------------->| Max-Forwards: 69   |                   |
     |                     |------------------->|                   |
     |                     | INVITE             |                   |
     |                     | Max-Breadth: 30    |                   |
     |                     | Max-Forwards: 69   |                   |
     |                     |--------------------------------------->|
     |                     |                    |                   |

                             Parallel Forking

The second one is Sequential Forking ๐Ÿ’… Same starting point, but Proxy A sends the request to Proxy B with Max-Breadth: 60. Proxy B sends back some error response, and only then does Proxy A send the INVITE on to Proxy C, still with Max-Breadth: 60 and Max-Forwards: 69. One branch at a time, periodt.

    UAC                 Proxy A              Proxy B             Proxy C
     | INVITE              |                    |                   |
     | Max-Breadth: 60     | INVITE             |                   |
     | Max-Forwards: 70    | Max-Breadth: 60    |                   |
     |-------------------->| Max-Forwards: 69   |                   |
     |                     |------------------->|                   |
     |                     | some error response|                   |
     |                     |<-------------------|                   |
     |                     | INVITE             |                   |
     |                     | Max-Breadth: 60    |                   |
     |                     | Max-Forwards: 69   |                   |
     |                     |--------------------------------------->|
     |                     |                    |                   |

                            Sequential Forking

Last one is No Forking ๐Ÿงš The request just goes straight down the line, with Max-Breadth: 60 staying the same and Max-Forwards ticking down from 70 to 69 to 68 as it hops from Proxy A to Proxy B to Proxy C.

    UAC                 Proxy A              Proxy B             Proxy C
     | INVITE              |                    |                   |
     | Max-Breadth: 60     | INVITE             |                   |
     | Max-Forwards: 70    | Max-Breadth: 60    | INVITE            |
     |-------------------->| Max-Forwards: 69   | Max-Breadth: 60   |
     |                     |------------------->| Max-Forwards: 68  |
     |                     |                    |------------------>|
     |                     |                    |                   |
     |                     |                    |                   |
     |                     |                    |                   |

                                No Forking

And then there's the big boss figure ๐Ÿ‘‘ It shows how MB (Max-Breadth) and MF (Max-Forwards) work together as a request spreads through a tree of proxies, with the branches splitting and the values getting smaller as you go deeper. It's giving tree energy ๐ŸŒณ

              MB == Max-Breadth               MF == Max-Forwards

                                    | MB: 4
                                    | MF: 5
                         MB: 2      P            MB: 2
                         MF: 4    /  \           MF: 4
                 +---------------+    +------------------+
         MB: 1   P    MB: 1                     MB: 1    P    MB: 1
         MF: 3 /  \   MF: 3                     MF: 3  /  \   MF: 3
          +---+    +-------+                     +----+    +-------+
          P                P                     P                 P
    MB: 1 |          MB: 1 |               MB: 1 |           MB: 1 |
    MF: 2 |          MF: 2 |               MF: 2 |           MF: 2 |
          P                P                     P                 P
    MB: 1 |          MB: 1 |               MB: 1 |           MB: 1 |
    MF: 1 |          MF: 1 |               MF: 1 |           MF: 1 |
          P                P                     P                 P
                                     .
                                     .
                                     .

               Max-Breadth and Max-Forwards Working Together

5.3. Formal Mechanism ๐ŸŽ€

5.3.1. Max-Breadth Header Field

okay, the actual header ๐Ÿ’– The Max-Breadth header field takes a single positive integer as its value, and it takes no parameters. That's it, that's the whole thing!

5.3.2. Terminology

so for each "response context" (see Section 16 of [RFC3261]) in a proxy, this mechanism gives you two positive integer values to keep track of: Incoming Max-Breadth and Outgoing Max-Breadth ๐Ÿซถ

Incoming Max-Breadth is the value in the Max-Breadth header field of the request that created the response context. Outgoing Max-Breadth is the sum of the Max-Breadth header field values across all the forwarded requests in that response context that haven't gotten a final response yet.

5.3.3. Proxy Behavior

this is the part where proxies have to behave ๐Ÿ™ˆ

If a SIP proxy gets a request with no Max-Breadth header field value, it MUST add one, and the value is RECOMMENDED to be 60. Proxies MUST also have a maximum allowable Incoming Max-Breadth value, which is RECOMMENDED to be 60. If a received request goes over that maximum, the proxy MUST overwrite it with a value that SHOULD be no greater than its allowable maximum.

All proxied requests MUST contain a single Max-Breadth header field value. SIP proxies MUST NOT let the Outgoing Max-Breadth go above the Incoming Max-Breadth in a given response context.

If a SIP proxy figures out that a response context doesn't have enough Incoming Max-Breadth to pull off a parallel fork, and the proxy is unwilling or unable to make up for it by forking serially or sending a redirect, then that proxy MUST return a 440 (Max-Breadth Exceeded) response ๐Ÿ’€

Notice that these requirements mean a proxy that gets a request with a Max-Breadth of 1 can only fork serially, but it is not required to fork at all. It can return a 440 instead. So this mechanism is not a tool a user agent can use to force every proxy on the path to fork serially. not me trying to be the boss of the whole network ๐Ÿ™…โ€โ™€๏ธ

A SIP proxy MAY distribute Max-Breadth in an arbitrary fashion between active branches. A proxy SHOULD NOT use a smaller amount of Max-Breadth than was in the original request, unless the Incoming Max-Breadth went over the proxy's maximum acceptable value. And a proxy MUST NOT decrement Max-Breadth for each hop or otherwise use it to restrict the "depth" of a request's propagation. the hop count is not Max-Breadth's job, bestie ๐Ÿฆ‹

5.3.3.1. Reusing Max-Breadth

because forwarded requests that already got a final response don't count toward the Outgoing Max-Breadth, the moment a final response shows up, the Max-Breadth that was being used on that branch becomes available to reuse ๐Ÿ”ฅ Proxies SHOULD be prepared to reuse this Max-Breadth in cases where there may still be elements left in the target-set.

5.3.4. UAC Behavior

a User Agent Client (UAC) MAY put a Max-Breadth header field value in its outgoing requests. If it does, this value is RECOMMENDED to be 60 ๐ŸŒธ

5.3.5. UAS Behavior

this mechanism doesn't change how a User Agent Server (UAS) behaves. If a UAS gets a request with a Max-Breadth header field, it just ignores that field while processing the request. no drama ๐Ÿ’…


5.4. Implementer Notes ๐Ÿ“

5.4.1. Treatment of CANCEL

since CANCEL requests are never proxied, a Max-Breadth header field value means nothing in a CANCEL request โœจ Sending a CANCEL doesn't affect the Outgoing Max-Breadth in the associated INVITE response context at all. And receiving a CANCEL doesn't affect the Incoming Max-Breadth of the associated INVITE response context either. totally separate lanes ๐Ÿ›ฃ๏ธ

5.4.2. Reclamation of Max-Breadth on 2xx Responses

okay so whether 2xx responses free up Max-Breadth is mostly a moot point, since proxies aren't allowed to start new branches in this case ๐Ÿ™„ BUT there's one caveat. A proxy might get multiple 2xx responses for a single forwarded INVITE request. Also, [RFC2543] implementations might send back a 6xx followed by a 2xx on the same branch. So implementations that subtract from the Outgoing Max-Breadth when they receive a 2xx response to an INVITE request must be careful to avoid bugs caused by subtracting more than once for a single branch. don't double-count, bestie ๐Ÿ˜ญ

5.4.3. Max-Breadth and Automaton UAs

designers of automaton user agents (UAs) should think about whether Max-Breadth limits should go on their outgoing requests. This includes B2BUAs, gateways, exploders, and any other element that sends requests programmatically because of incoming SIP traffic ๐Ÿค– For example, it's reasonable to design B2BUAs to carry the Max-Breadth value from incoming requests into the requests they send as a result.

It's also reasonable to put Max-Breadth constraints on sets of requests sent by exploders when they might get leveraged in an amplification attack. protect your network, periodt ๐Ÿ›ก๏ธ


5.5. Parallel and Sequential Forking ๐Ÿฆ‹

the cool thing here is that a proxy can get its Max-Breadth back while forking sequentially ๐Ÿ‘‘ The limit on outgoing Max-Breadth only applies to concurrent branches.

For example, say a proxy gets a request with a Max-Breadth of 4 and has 8 targets to forward it to. That proxy can parallel fork to 4 of those targets at first, with each one getting a Max-Breadth of 1, for a total Outgoing Max-Breadth of 4. If one of those transactions finishes with a failure response, the outgoing Max-Breadth drops to 3, so the proxy can forward to one of the 4 remaining targets (again, with a Max-Breadth of 1). math is mathing ๐Ÿงฎ


5.6. Max-Breadth Split Weight Selection ๐ŸŒˆ

there are a bunch of ways to control the weight of each fork branch โš–๏ธ Fork branches that get more Max-Breadth are more likely to finish quickly, because a proxy further down the line is less likely to be forced to fork sequentially. By the same token, if you know a given branch won't fork later on, you can assign it a Max-Breadth of 1 with no ill effect. This would be appropriate, for example, if a proxy knows the branch is using the SIP outbound extension [OUTBOUND] ๐Ÿงš


5.7. Max-Breadth's Effect on Forking-Based Amplification Attacks ๐Ÿ’œ

Max-Breadth limits the total number of active branches spawned by a given request at any one time. It puts no limit on the distance (measured in hops) that the request can travel. (In other words, receiving a request with a Max-Breadth of 1 means that any forking has to be sequential. It does not mean that forking is forbidden.)

This limits how well any amplification attack that leverages forking can work, because the amount of state and bandwidth needed to process the traffic at any given moment is capped ๐Ÿ”’


5.8. Max-Breadth Header Field ABNF Definition ๐ŸŽ€

this spec extends the grammar for the Session Initiation Protocol by adding an extension-header. Here's the ABNF [RFC5234] definition:

Max-Breadth  =  "Max-Breadth" HCOLON 1*DIGIT

6. IANA Considerations ๐ŸŒธ

this spec registers a new SIP header field and a new SIP response, following the processes defined in [RFC3261] ๐Ÿ“

6.1. Max-Breadth Header Field

this info shows up in the Header Fields sub-registry of the SIP Parameters registry:

RFC 5393 (this specification)

Header Field Name: Max-Breadth

Compact Form: none

6.2. 440 Max-Breadth Exceeded Response

and this one shows up in the Response Codes sub-registry of the SIP Parameters registry:

Response code: 440

Default Reason Phrase: Max-Breadth Exceeded

7. Security Considerations โœจ

okay bestie, this section is SERIOUS ๐Ÿ˜ญ and I'm going to be honest, it's the part where the RFC gets very dramatic, but it's giving "important stuff, read carefully" ๐Ÿซถ This whole document is literally about one vulnerability in SIP proxies, as defined by RFC 3261, that can lead to an exponentially growing message exchange attack ๐Ÿ’€

So here's the tea. The Max-Breadth mechanism does not reduce the total traffic the forking-loop attack creates ๐Ÿ™ˆ What it does is spread that traffic out over a longer period, by limiting how many branches are being processed at the same time. An attacker could keep pumping requests into a network that uses Max-Breadth and slowly build the traffic up to unreasonable levels ๐Ÿ˜ญ So deployments should watch closely and react when the number of concurrent outstanding transactions for a given resource creeps up gradually ๐Ÿ‘€ Operators should be ready to temporarily disable any resources that show up in this kind of attack ๐Ÿ”ฅ A rapid jump in outstanding concurrent transactions across the whole system might mean this attack is happening across lots of resources at once ๐Ÿ’… And deployments where an attacker could easily grab a huge number of resources are at extra risk ๐Ÿšจ If detecting and stopping each instance of the attack isn't enough to reduce the load, overload may happen ๐Ÿ’€

Implementers and operators are encouraged to follow the overload-handling recommendations that are being developed (see [REQS] and [DESIGN]). Don't skip this one, it's important ๐Ÿซถ

Designers of protocol gateways need to think hard about this attack ๐Ÿง  For example, say a message goes from a SIP network into the Public Switched Telephone Network (PSTN) and then back into a SIP network, and the info about the request's history gets lost on either side of the translation. Then it becomes possible to build loops that neither Max-Forwards nor loop detection can stop ๐Ÿ˜ญ Add forking amplification on the SIP side of that loop and you get an attack like the one described in this document, and the mechanisms here won't calm it down, not even to the point of limiting how many concurrent messages are in the attack ๐Ÿ’€ This matters even more for designers of SIP-to-SIP gateways, like the ones you find in B2BUAs ๐ŸŽ€ Lots of existing B2BUA implementations are under pressure to hide as much as possible about the two sides they talk to. Implementers of those might be tempted to strip out the data that loop detection, Max-Forwards, or Max-Breadth could use at other points in the network, and take on the job of detecting loops (or this attack) themselves ๐Ÿ™ˆ But if two of those implementations are both involved in the attack, neither one will be able to detect it ๐Ÿ˜ญ

7.1. Alternate Solutions That Were Considered and Rejected ๐ŸŒธ

Okay so the group also looked at other solutions, and here's why they didn't make the cut ๐Ÿ’…

Doing nothing, and just suing the offender: systems with accounts have logs you can dig through to find abusers, but it's not clear that this stops the attack or scares anyone off ๐Ÿ˜ญ Systems that don't notice the situation and fix it are likely to fail so badly that the records of how the attack got set up can't be recovered ๐Ÿ’€ (In one scenario, the registrations happen in a totally different time period from the INVITE transaction, and the INVITE might have come from someone totally innocent ๐Ÿ™ˆ) It's even possible the whole thing gets set up by accident ๐Ÿ˜ฌ And for some existing deployments, an account is just an email address, so finding someone to punish might be impossible ๐Ÿ’” Finally, some people won't respond to any legal threat at all, and even one successful attack like this would be devastating for a service provider ๐Ÿ˜ญ

Putting a smaller cap on Max-Forwards: the attack grows exponentially with the starting Max-Forwards value ๐Ÿ“ˆ So turning that value down does limit the attack ๐Ÿ™Œ BUT it severely limits how far requests can reach in the network, possibly to the point where existing architectures start breaking ๐Ÿ’€

Disallowing registration bindings to arbitrary contacts: the way registration bindings work right now is a big part of why this attack succeeds ๐Ÿ‘€ One alternative that got discussed (especially with the mechanisms being defined in [OUTBOUND]) is to only allow bindings to the network element doing the registration. That could even mean ignoring whatever is in the Contact and using the transport details seen in the registration request instead ๐Ÿงš These ideas might come back later, but right now they aren't developed enough to handle the current threat ๐Ÿฆ‹

Deprecate forking: this attack just doesn't exist in a system that relies entirely on redirection and on the original endpoint starting new requests itself โœจ But removing such a big architectural piece from the system at this point was considered way too extreme ๐Ÿ˜ญ

Don't reclaim breadth: another design that got considered and rejected was to NOT let the breadth from finished branches be reused (see Section 5.3.3.1) ๐Ÿซ  Under that idea, one introduced request would cause at most the initial value of Max-Breadth transactions to show up in the network. That does cap any version of this amplification vulnerability at a constant multiplier ๐Ÿ”’ BUT it would drastically change how far requests can reach, and people believe it would break existing deployments ๐Ÿ’”

8. Acknowledgments ๐Ÿซถ

Thanks to the implementers who put their code through this scenario and helped analyze the results at SIPit 17 ๐ŸŽ€ And a big shoutout to Eric Rescorla, who provided guidance and text for the hash recommendation note ๐Ÿ‘‘

9. References ๐Ÿ“š

9.1. Normative References ๐Ÿ”ฅ

[RFC2119]   Bradner, S., "Key words for use in RFCs to Indicate
            Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC3261]   Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston,
            A., Peterson, J., Sparks, R., Handley, M., and E.
            Schooler, "SIP: Session Initiation Protocol", RFC 3261,
            June 2002.

[RFC5234]   Crocker, D. and P. Overell, "Augmented BNF for Syntax
            Specifications: ABNF", STD 68, RFC 5234, January 2008.

9.2. Informative References ๐Ÿ’œ

[DESIGN]    Hilt, V., "Design Considerations for Session Initiation
            Protocol (SIP) Overload Control", Work in Progress,
            July 2008.

[OUTBOUND]  Jennings, C. and R. Mahy, "Managing Client Initiated
            Connections in the Session Initiation Protocol (SIP)",
            Work in Progress, October 2008.

[REQS]      Rosenberg, J., "Requirements for Management of Overload
            in the Session Initiation Protocol", Work in Progress,
            July 2008.

[RFC1321]   Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321,
            April 1992.

[RFC2543]   Handley, M., Schulzrinne, H., Schooler, E., and J.
            Rosenberg, "SIP: Session Initiation Protocol", RFC 2543,
            March 1999.

[RFC4960]   Stewart, R., "Stream Control Transmission Protocol",
            RFC 4960, September 2007.

Authors' Addresses ๐ŸŽ€

Robert Sparks (editor)
Tekelec
17210 Campbell Road
Suite 250
Dallas, Texas  75254-4203
USA

EMail: RjS@nostrum.com


Scott Lawrence
Nortel Networks, Inc.
600 Technology Park
Billerica, MA  01821
USA

Phone: +1 978 288 5508
EMail: scott.lawrence@nortel.com


Alan Hawrylyshen
Ditech Networks Inc.
823 E. Middlefield Rd
Mountain View, CA  94043
USA

Phone: +1 650 623 1300
EMail: alan.ietf@polyphase.ca


Byron Campen
Tekelec
17210 Campbell Road
Suite 250
Dallas, Texas  75254-4203
USA

EMail: bcampen@estacado.net

That's the whole RFC, bestie! ๐ŸŒˆโœจ You made it through 5 parts ๐Ÿ˜ญ๐Ÿซถ

๐Ÿ“ฆ where this page comes from