Man in the middle with TLS/SSL
Man in the middle attack (aka MITM) is very famous and well known network attack. Lately I found myself playing with it, turning my theoretical knowledge into practical methods (on my own computers of course). I’m going to try explain the theory, as well as practical methods I tried, including attacks on TLS/SSL. Is it another “how to perform man in the middle attack” tutorial ? not at all. It’s not about attacking, it’s about what’s behind it, the problems I had and my solutions. Prerequisite knowledge: basic networking and understanding of TCP/IP.
Disclaimer: I do not in any way encourage you to do any kind of illegal act. All the information provided is for educational purpose only. Everything you do is on your own responsibility.
Introduction to MITM (skip if you already know)
The concept: A wants to communicate with B. Attacker C gets in the middle so all communication goes through him. Once he got into that position he can either passively eavesdrop to the communication or actively change it. Obviously it’s a security threat.
How is it done ? for the purpose of this post, lets assume C has physical access to A’s LAN (ethernet or wireless connection for example), and they both on the same hub. Each packet that gets out of A is transmitted as a set of electric signals. All communication devices connected to the hub sense those signals and convert them into data. Then, each device handles the data, starting from layer 2 (ethernet usually). Layer 2 checks if the packet is addressed to the device according to destination MAC header (checks if it equals to device’s MAC or broadcast address).
This is the very first check and has no security mechanism involved so it’s very convenient for C to start the attack from here. So first, he needs to convince A that he is B, but only on layer 2. He can do it by simply transmitting message “I’m B”. Easy as that. Then A would address his packets to C’s destination MAC, believing it’s B’s. Of course the real B might send “I’m B” as well, in that case C would send “I’m B” again. C just needs to be a little more aggressive and it would catch. In the same manner, C would convince B that he is A. This process is called ARP poisoning, and can be done with arpspoof, which is part of dsniff package.
Once done, all communication between A and B goes through C, completely transparently to them. C can read the packets, forward them unchanged or modify them on the fly. Packet sniffing can be done with wireshark. Injections/password stealing/etc can be done with ettercap. So far nothing new under the sun.
How TLS/SSL works (skip if you already know)
TLS/SSL tries to eliminate the security threat (along another threats I haven’t mentioned) by providing secure tunnel between A and B. All data in tunnel is signed and encrypted. That way, communication might still go through C but he won’t be able to understand the data (because it’s encrypted). Any tampering attempt (modifying/retransmitting) the data or it’s signature would be detected. I won’t get into cryptology behind it but it’s practically impossible for C to find A’s or B’s private keys needed to decrypt/sign the data.
However there is a flaw. A and B must know each other’s public keys. The public keys are not confidential because the private keys cannot be derived from them. So, the TLS/SSL protocol begins with unencrypted public keys exchange (as they are needed to start the encryption). At that moment attacker C can modify the keys, sending B: “Hi, I’m A, this is my public key” but actually sending his own public key instead of A’s and the same towards A (faking B’s public key). If A and B won’t know the keys have been changed they will continue as usual. What will happen is creation of two separate secured tunnels, A-C and C-B. C would be able to read/modify the data with his own private key, then sign and encrypt it again on the way to the other peer.
To prevent this attack (SSL man in the middle), A and B must either know each other’s public keys before they connect, or have reliable authentication system that can verify public keys’ authenticity. In real world, it’s impractical for one to magically know all public keys of all computers he would ever possibly communicate with. Specially keys that don’t exist yet. So a reliable authentication system must be used. SSL uses public key certificates system. In this system, the problem is narrowed down to knowing a limited set of computers’ public keys (=trusting those computers), which is practical.
Those trusted computers would be signing authorities. They will sign on public key – identity pairs (aka certificates). Now, when A receives messages “I’m B, this is my public key”, it would be followed by a certificate, signed by someone A trusts, lets call him CA (certification authority). If the public key is modified on the way by attacker, it won’t fit the public key in B’s certificate. If the certificate would be modified as well, A would know it has been tampered (cryptographic feature of knowing CA’s public key).
What I described is fairly simplification of how it really works, but these are the concepts behind it. Also, it’s important to mention that while certificates may be good solution for servers and web sites, they don’t suit clients so well, because clients usually have no constant IP address, besides, would you pay yearly for certificate ? For most people, publishing their public key on their website/blog/email is more than enough. Therefor, today’s client-server authentication is usually asymmetric, for example when you login into your bank account on the web, your browser (hopefully) authenticate it’s really your bank’s website using the bank’s certificate, while the bank authenticate your identity using some sort of username/password combination.
The juicy part
Having all that written, you now understand that TLS/SSL provides good confidentiality and data integrity if implemented correctly. I’m going to try SSL man in the middle attack to check whether the client implements SSL correctly, meaning how does it handle data tampering events. The client will be “google talk” application on (emulated) android mobile, connected to my home LAN. As I wrote in “Faking the Green Robot – Part 1“, it establishes connection to mtalk.google.com, port 5228.
First, I need to perform the non-SSL man in the middle methods to gain control over communication between the mobile and my home router (which connected to the outer world). Then, I need a platform that acts as SSL server towards the client, establishes secure connection with it, decrypts the data sent by client, establishes new SSL connection with the google’s server, encrypts the data again and sends it to the server. When server responses the platform should handle the data the same way, backwards.
My first choice was ettercap, which is very common. I like it because it’s one tool that supposedly does everything for you, and it doesn’t affect layers 3 and 4, meaning, when it recreates the packets as man in the middle, only MAC addresses (and SSL layer in our case) are modified. It’s important feature (you’ll soon find out why), and can only be done when the same application is responsible for packet forwarding and doing the SSL stuff.
I started with arp poisoning my LAN and some checks on the computer emulating the android mobile first. Clear text data was going through my attacking computer, I was able to see everything, and when I browse to secured website (https), the browser showed me warning that the certificate can’t be validated (meaning ettercap performed man in the middle, acting as server, presenting it’s own certificate). So everything is ready for the real test.
I started Talk app on the android, and was amazed to see that the app is working! no warnings whatsoever. However, I soon found out that ettercap’s big advantage (doing everything hidden in the background for me) became disadvantage, as I couldn’t decrypt the data with ettercap’s key, and I haven’t got the slightest idea what went wrong. It turned out that ettercap just forwarded those packets untouched. I tried configuring it to dissect https on port 5228 (in addition to 443) but still no luck. After googling for answers, and quick inspection of ettercap’s source code for hardcoded 443s (didn’t find any accept default for https dissection which I changed via configuration file), I came to the conclusion that it just doesn’t work and I don’t know why. Also, it provided me with no useful diagnostic information, so I decided to try another tool.
Google gave me webmitm, which is used together with arpspoof and dnsspoof, all included in dsniff package mentioned before. These tools work differently than ettercap. Arpspoof forces (on layer 2) all traffic to go through attacker’s computer, but no tool is in responsible for general packet forwarding, so unless operating system is configured to forward the packets they get “stuck” and discarded. The attack focuses on web man in the middle. Generally, when a web browser visits url, a DNS request is sent in order to translate url to ip address. Dnsspoof listens for those requests and returns fake response: “The ip address of url X is attacker’s ip”. Now the target would address it’s packets (on layer 3) to attacker computer. Then comes the role of webmitm, it listens on ports 80 (HTTP) and 443 (HTTPS), acting as man in the middle, forwarding the packets to the real servers. How does it know who was the packet originally addressed to ? I’m glad you asked. Unlike ettercap which keeps layer 3 untouched and has no such problem, it reads the “Host:” header from HTTP request and redirects accordingly.
Webmitm wasn’t useful for many reasons. First, it supports only ports 80 and 443 (hardcoded). I changed the source code to use port 5228 instead of 443. After I got it compiled (which wasn’t very pleasuring act) I tried using it, but it didn’t know where to forward the packet because there was no “Host:” header. I even tried modifying the source to have “default” host, but it was buggy and crashed. I guess my C skills are not as good as they used to be… anyway, back to square one.
What else ? I found sslstrip but it attacks web browsers, so it’s no good for me. All I need is something that listens on port 5228, talks SSL, takes the output and talks it to another SSL server on port 5228. How hard can that be ?!! Umm… when I say it like this, it seems like all I ever really needed was netcat that supports SSL. I found new candidates:
Stunnel. At first glance it looked too complicated to configure, with all inet based service wrapping and stuff. Got no flags that do exactly what I need… so there was no second glance. I moved on.
SSL Capable NetCat. Very nice perl based utility. I managed to get it listening as TCP server and forwarding to SSL connection, but no SSL – SSL forwarding. When I tried using it as SSL server (-c), the connection made by android was always killed in very early stage of the (SSL) handshake. I got no idea why. I tried changing SSL options in the source (well documented here) but got no luck.
netcat SSL. As they say, the third time’s the charm. This C based utility, has built-in forwarding (-x) that took me exactly the same place SSL Capable NetCat took me (only TCP – SSL forwarding), but unlike him, it’s SSL server worked with android client. So, all I needed to do is simply a few shell redirections:
# nssl -l 5228 | nssl mtalk.google.com 5228
What’s wrong here ? the forwarding works only in one direction, but it’s easily fixable.
# mknod tmp_pipe p
# nssl -l 5228 0< tmp_pipe | nssl mtalk.google.com 5228 1> tmp_pipe
What’s wrong here ? forward-wise nothing, but the android rejected nssl’s built-in certificate.
# nssl -l 5228 -f faked_certificate.crt 0< tmp_pipe | nssl mtalk.google.com 5228 1> tmp_pipe
Still, android rejected the (self-signed) certificate I made (using openssl), which looks just like the original, but has different issuer.
I tried couple more different fake certificates, checking different potential failures in android’s security, and I’m glad to inform I couldn’t find any. I’m glad, because if I did find, I wouldn’t feel comfortable publishing it without letting google fix it first… (or maybe I did find, but I can’t say…). At last, I wanted to make sure my forwarding does really work, so I made the android trust my fake certificate (how to do that – in another post). Once done everything worked perfectly.
I hope you now have better understanding of how SSL man in the middle attack works (although in this case, the SSL worked seamlessly, the fact I could eventually get the data decrypted was only because the client was accessible).
By the time I tried attempt 2 and got rejected I thought it has something to do with the two separate commands, when one of them gets closed, so I modified the source of nssl and added support for new SSL-SSL forwarding, which also worked. How is that for my C skills ? :)