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.
This blog is not intended for complete beginners but if you have basic knowledge of networking you are good to go.
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.
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.
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.
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.
NAT ALG (Application Level Gateway)
NAT (Network Address Translation) translates IP addresses on the network layer (L3) and port numbers on the transport…
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:
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,
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.
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.
For our case, we are going to stick only to FTP.
- FTP client initiates the connection this traffic undergoes normal NAT operations.
- The client informs the FTP server about data connection, it sends a packet using the PORT command,
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,
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.
objForm = document.createElement("form");
IP: 192.168.0.5 , Port: 62001
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.
Let’s try to find other similar protocols like FTP or IRC from Netfilter,
As you can see chrome blocks both FTP and IRC, SIP looks good, let’s do some research.
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' +
// 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
POST / HTTP/1.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-Encoding: gzip, deflate
Content-Disposition: form-data; name="textname"
REGISTER sip:samy.pl;transport=TCP SIP/2.0
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.
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 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.
This is pretty cool research, I loved it and learned lots of stuff here are some resources:
Спасибо :) — Mudhalai Mr DSC SASTRA deemed university