FDS-TEAM
Linux, Windows, Programming and more...
Security vulnerability: Routers acting as proxy when sending fake IRC messages
18 Jun 2013 00:47 CEST

Inspired by a DEFCON speech from 2010 ("How I Met Your Girlfriend" by Samy Kamkar, NAT pinning part) we both wanted to know if the method for NAT pinning described in his talk is still possible - as it turns out it is indeed and moreover we revealed a bug in several router firmwares, which allows to use unsuspecting people as a relay/proxy on the internet.

NAT pinning summarizes several methods which can be used to temporarily open and forward ports in the router. There is a whole bunch of methods out there, you can take a look at Phrack - Issue 63 - Breaking Through a Firewall for example. Usually these methods are used for example for peer to peer applications to trick the local router/NAT. Normal peer to peer programs cannot be used behind a NAT because the router does not know which device in the internal network should receive an incoming connect packet as all local clients share the same public IP address. NAT devices track all incoming and outgoing connections and deliver packets depending on the information saved in their tracking table. The idea of NAT pinning is to get an entry in this table to allow incoming connections, although you can normally get only entries for outgoing connections.

The talk showed some other usage for this NAT pinning methods - embedding these methods in a webpage allows a potential attacker to perform an attack on the local computer, by forwarding all needed ports to the machine visiting the website - at least theoretically. We both wanted to know if we were vulnerable to this attack too, and were both surprised: There has almost nothing changed, and even more advanced router systems like OpenWRT based routers are vulnerable in their default configuration!

# 1. Classical hole punching

For reasons of simplicity we decided to use the IRC DCC SEND method to trick the NAT, as the requirements to trigger this feature are very low in the Linux kernel and most routers are Linux based. DCC is intended to initiate a direct connection between two users in an IRC chat to transfer files or for a private chat. By default such commands are only detected on the IRC port 6667, but this isn't really an issue: Some pouplar browsers like Firefox do not block port 6667 for HTTP requests, so we could just embed the malicious command into a HTTP POST request on Port 6667 (as both protocols are new line based)! Chrome has blocked port 6667 by default (try to open http://127.0.0.1:6667/ and take a look at the error message, it should tell net::ERR_UNSAFE_PORT), but this doesn't really matter, because Chrome thrusts Flash, which is enabled by default, and *suprise* Flash allows us to connect to any port that we want, even port 6667.

In order to do all the testing more systematically, we decided to write some minimal Python scripts, which can trigger this issue. There are three servers needed to reproduce this issue, first of all an HTTP server (which is just a plain HTTP server, could also be apache or nginx), the IRC server (which isn't really an IRC server, but runs on port 6667), and the policy server, which is the only barrier that prevents Flash to connect to our fake IRC server.

The scripts first performs a regular portscan, and after that tries to trick the firewall with several IRC DCC SEND packets. So far nothing unusual, except that this is a very big security leak, if someone runs his local servers without any further protections - and yes, the Port forwarding even works for ports < 1024, like SSH or SMB on Windows devices. It is even possible to overwrite existing port forwardings and do some kind of denial of service attack.

If you have shell access to your router, you can see all forwarded ports in /proc/net/ip_conntrack_expect. The foward rule stays active for exactly one successfully established tcp connection. After a succesful handshake the connection will be treated like all other connections and is visible in /proc/net/ip_conntrack. This is not a big issue for an attacker as he can reopen the port as often as he wants (if the fake IRC connection is still active). The provided scripts will connect to all forwarded ports to remove them from the list (a SYN based scan would not remove the forward rule).

The story could end here, but then we found several unusual things: The DCC SEND command should contain the own IP and Port, like an invitation for the other end to connect. When using the private IP it worked, but when we tried to issue this command with our public IP the port was not forwarded. By pure chance we tried to use the address of the IRC Server instead, and yep, it worked! Obviously this part of the kernel is not really tested very well. By manually inspecting the corresponding code we could also find the source of this issue:

(Source from: https://github.com/torvalds/linux/blob/master/net/netfilter/nf_conntrack_irc.c, 17.06.2013)

/* dcc_ip can be the internal OR external (NAT'ed) IP */
tuple = &ct->tuplehash[dir].tuple;
if (tuple->src.u3.ip != dcc_ip &&
tuple->dst.u3.ip != dcc_ip) {
net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
&tuple->src.u3.ip,
&dcc_ip, dcc_port);
continue;
}


Obviously the comparison here doesn't really check the NAT'ed IP, like the comment suggests, but instead the destination IP (in this case the IP of the IRC server) is checked, which doesn't make any sense! This makes the job much easier for an attacker as you can't get the local IP from javascript and even to get the public IP address requires a remote server, but the IP address of his own fake IRC server should be known to the attacker! This is also very cruel if you have several NATs behind each other, as the attacker should need to know the private IP address of all routers, which is almost impossible but as all NATs accept the destination IP address, we can trigger all of them with just a single packet.

We improved our testing scripts and gave them to some friends, which mostly have commercial router firmware running on their home routers, and let them perform the test, and the result was the next big surprise: There was something weird as the result was not exactly what we had expected - the result showed a Windows user running a SSH server with a Debian signature?! After some more tests we figured out what was going on: These were the scan results for our own testing server. Some tcpdump logs showed that these routers simply returned the packets we sent to them. If they didn't recognize the IRC DCC command, we would expect that they reject or drop the packages. Return to sender is something you may know from postal services but it is not commonly used in TCP, so we dug even further.

If you want to test your own device, you can download the required python scripts below. The scripts have to be executed on a device that is in the WAN network of the router (i.e. the internet for most users) which should be tested. If you are using Linux, you must run the policy server as root, because the port is below 1024 - don't worry, the server immediately drops his rights after opening the port (just take a look at the sources, if you don't trust us). We don't provide a public test server as some people think that port scans are some kind of cyber attack, sorry for this.

• MD5:    85d153f90f2deb9bfd46880b7ec74a0b
SHA256: f28bd39453761c307695f1f63c2719861c6e4293a70707e034932605ff82a1f9
Size:   15 KB
Portscanner using IRC DCC to bypass the NAT, written for Python 2.6
Includes socket_bridge.swf from Ionel Cristian Mărieș published under the MIT license

In order to use the script you first have to change the config.py file and setup everything correctly. You probably want to configure a personal password so only you can use the portscan service and change all the magic keys to prevent anyone else using the script. Then just execute ./runall.sh as root to automatically start all 3 required servers. Root privileges are necessary as one of the servers has a port below 1024, the rights are dropped immediately after the startup.

We created a tcpdump/wireshark/pcap log from one of these port scans, which you can view here in an obfuscated version (parts of the user's ip are removed). The capture is from a slightly older version of the script, but I think that the text commands used in the version are self explaining. You can see some interesting points in this log: First of all the received DCC packet contains a different IP than the one we told the client to put into the packet. The NAT must have exchanged it with the public IP address. All packets we send to the client are simply sent back, only the source and destination IP is swapped. The router even notices that this is somehow silly and sends an ICMP REDIRECT to tell us, that we should send the packets directly to ourself. As the checksum of the packet is invalid (yes, the checksum was also invalid before I altered the IP) the redirect request is ignored (You can check this by comparing the destination mac address of the outgoing packets). If you want to test the scripts on your own linux server, you may want to execute

echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects


to disable icmp redirections. Moreover ensure that you don't have conntrack modules running (see below) as they might interfere with the scan packets - I even noticed that some hosting providers have them enabled by default, even when they are completely useless .

We don't own one of this routers, so we could only do some rudimental tests and it would be interessting how these routers react, if we send for example the routers IP in the DCC packet. Feel free to change our scripts and tell us your results.

How to interpret the results of the scan?

Assuming that you configured your router properly, the regular port scan should show "closed" for all ports - except you have intentionally opened some ports for any service. If you are not vulnerable to any of the attacks described above, all other port scans using different IP addresses should display exactly the same results like for regular portscan.

In case you are vulnerable to the classical DCC CHAT exploit, you should see the servers running locally instead of the results for a regular portscan - don't worry, the port is immediately closed after our server is connected, so it is very unlikely that someone else on the internet was able to connect in this short period to your local server.

If you are vulnerable to the mysterious internet redirect bug, you should see the scan results for MY server. To make it very easy to distinguish our HTTP server running on port 8080 responds with status code "666 Your Router might be vulnerable to the redirect exploit! Please check this manually!" in case the request is redirected back. As the message suggests you should probably do some further testing in this case, for example use the options to manually setting up a forward and check the results using telnet or ssh.

If your result doesn't match any of the outputs described above, your router probably has some additional bugs! You can use the options to send DCC packets manually to find out what exactly is going on.

What can we do to prevent this kind of attacks?

Disable the connection tracking modules in the kernel. Most routers are linux based and often offer some kind of telnet access, which must be enabled through some obscure ways. If you got a working shell on your router, you may want to remove the corresponding connection tracking modules (example for irc):

rmmod nf_conntrack_irc
rmmod nf_nat_irc


Be aware that the modules will most probably be loaded again on the next router reboot. If you are using OpenWRT you can prevent this by adding a comment sign (#) in front of the helpers in /etc/modules.d/45-ipt-nathelper. Otherwise you may need to wait for a firmware update, which may take a very long time!

Additionally the following tips do not really solve the problem, but they make it much harder to use for an attack from a browser:

• Prevent the browser from using the port 6667 as HTTP port. Chrome already blocks port 6667 and you can also tell Firefox to do so: Open about:config in Firefox and make a right click on one of the options, select New -> String, enter network.security.ports.banned as property name and 6667 as value. If you try to open http://127.0.0.1:6667/ you should get an error message telling you that this port is insecure.
• Use browser plugins like NoScript to disable JavaScript, Flash and Java on sites you do not trust.

# 3. Conclusion

These issues are just another point on the list of common security issues in typical home routers. The past showed that most vendors are not really interested in patching vulnerabilities very fast and as service providers can force you to use their hardware (at least in Germany) you are not even allowed to switch to a more secure router. On the other hand, you are responsible for the traffic coming from your internet connection, so you may have to live with risk of being judged for things you are not responsible for. Mad world we are living in…

Writing comments is currently disabled, as we're updating the content management system.