How I found my first Chrome bug (CVE-2021–21210)

On October 31, 2020, @SamyKamkar published his research on NAT Slipstreaming. According to his own words, NAT Slipstreaming —

… allows an attacker to remotely access any TCP/UDP service bound to a victim machine, bypassing the victim’s NAT/firewall (arbitrary firewall pinhole control), just by the victim visiting a website.

I would go further and say that NAT Slipstreaming is actually more than that. I consider NAT Slipstreaming a whole vulnerability category. Basically, whenever an attacker can force a user to generate traffic to a specific server on the Internet and control both the content sent and the target port, you have a potential NAT Slipstreaming vulnerability.

On February 27, 2021, a Saturday, I decided to revisit Samy’s publication as I was not comfortable with my level of understanding of the vulnerability. Took me a few rounds of reading to put the pieces together but eventually, I felt secure enough to try to implement the attack myself.

I won’t deep dive into the details as I believe Samy already did a great job on his publication and that it is a must-read for anyone interested in the subject. However, I will briefly introduce the basic steps of the attack.

  • First, you need a victim in a network whose router implements an Application-level gateway (ALG) for a specific protocol
  • Second, your need to craft a webpage which when visited, identifies the client’s private IP address and generates an HTTP POST message to a server you control. The message needs to contain a payload that triggers your router’s ALG when the message’s TCP segments are properly aligned
  • Third, you need a custom TCP server that will receive the crafted POST requests and verify if the TCP segments sent by the victim contain an indication that the ALG translated the addresses in the message. If not, the server replies to the page with an offset, indicating the required padding to align the TCP segments
  • Lastly, if no extra padding is required, the server tries to communicate with the port open by the victim’s router. If padding is required, the malicious web page repeats the third step until the segments are properly aligned.

I started by first inspecting which ALGs my home router had enabled by default. At the time, I was using a TP-Link Archer A7 v5.0 wifi router running the latest available firmware. The following picture shows the ALGs that were enabled.

Figure 1: TP-Link Archer A7 v5.0 default ALG configuration

I then went through an elimination process to decide which protocol to use for my implementation. Samy had already used SIP and Ben Seri & Gregory Vishnipolsky of Armis had used H.323 in their v2.0 version of the attack so I discarded these from the start. After a little research, I decided to go with RTSP. The protocol was text-based, the messages were simple, and Wikipedia had everything I needed to understand it (https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol).

Once I decided which protocol to tackle it was time to implement the attack. Firstly I scanned the protocol’s Wikipedia page and built a basic foundation about the protocol’s messages. The SETUP message looked like a promising candidate for a NAT Slipstream attack. According to Wikipedia:

A SETUP request specifies how a single media stream must be transported. This must be done before a PLAY request is sent. The request contains the media stream URL and a transport specifier. This specifier typically includes a local port for receiving RTP data (audio or video), and another for RTCP data (meta information). The server reply usually confirms the chosen parameters, and fills in the missing parts, such as the server’s chosen ports. Each media stream must be configured using SETUP before an aggregate play request may be sent.

The following couple of messages are an example of an RTSP SETUP message request sent from a client to a server and its respective response.

As previously mentioned, “The request contains the media stream URL and a transport specifier. This specifier typically includes a local port for receiving RTP data (audio or video) and another for RTCP data (meta information).”. This means the client tells the server about a couple of ports it can use to send data back to the client. As these ports are dynamic, for the protocol to work behind NAT, we need an ALG who can parse this type of message and create the required NAT rules once they are needed.
It is also worth mentioning that this dynamic port exchange only happens when the data transport uses UDP. When TCP is used for data transport, the protocol uses Embedded (Interleaved) Binary Data, which basically means data is encoded and encapsulated in the RTSP TCP connection itself (usually port 554/TCP). No NAT is needed in this case.

To confirm my initial thoughts about the RTSP SETUP message, I reviewed the RTSP conntrack module code (https://github.com/maru-sama/rtsp-linux/blob/master/nf_conntrack_rtsp.c). Figure 2 shows references to the SETUP message in the RTSP conntrack module source code. Moreover, it also shows that if the selected transport is not UDP the message processing is stopped.

Figure 2: nf_conntrack_rtsp.c

I now had everything I needed to implement an attack proof-of-concept (PoC).

I built a simple HTML file to simulate a malicious page visited by a victim. Once visited the page makes an HTTP POST request to a server on the Internet running on port 554/TCP. The POST request body contains a long string (padding) followed by a fake RTSP SETUP message. When the custom server receives a message, it will check if the SETUP message is properly aligned with its TCP segment. If the message is not aligned, it will respond to the client page with the offset required to adjust the alignment. If the message is aligned, the server will parse the SETUP message, and send a UDP message containing the string “It works!” to the mapped port.
Eventually, I was able to get my PoC to work in a non-patched version of Firefox. I was about to call it a day when I decided to give it a try on my fully patched version of Chrome [89.0.4389.72 (Official Build) (64-bit)], and, to my surprise, it worked!

All the files I used to demonstrate the attack and look for similar vulnerabilities in other browsers are available at https://github.com/bananabr/natslipstream.

Security researcher and penetration tester