The last talked about implementing hostname verification, which was a particular concern in TMDCitW. This post shows how you can test that your TLS client implements hostname verification correctly, by staging an attack. We're going to use dnschef, a DNS proxy server, to confuse the client into talking to the wrong server.
To keep things simple, I'm going to assume you're on Mac OS X Mavericks at this point. (If you're on Linux, this is old hat. If you're on Windows, it's probably easier to use a VM like Virtualbox to set up a Linux environment.)
The first step to installing dnschef is to install a decent Python. The Python Guide suggests Homebrew, and Homebrew requires XCode be installed, so let's start there.
Homebrew has some notes about Python, so we set up the command line environment:
Now (if you already have homebrew installed):
You should see:
If you run into trouble, then brew doctor or brew link --overwrite python should sort things out.
Now upgrade the various package tools for Python:
Now that we've got Python installed, we can install dnschef:
Then, we need to use dnschef as a nameserver. An attacker would use rogue DHCP or ARP spoofing to fool your computer into accepting this, but we can just add it directly:
OS X - Open System Preferences and click on the Network icon.
Select the active interface and fill in the DNS Server field. If you are using Airport then you will have to click on Advanced… button and edit DNS servers from there.
Don't forget to click "Apply" after making the changes!
Now, we're going to use DNS to redirect https://www.howsmyssl.com to https://playframework.com.
We need to specify the IP address 188.8.131.52 as the fakeip argument.
Now that we've got dnschef working as a proxy, we can see whether various TLS clients notice that www.howsmyssl.com has started returning an X.509 certificate that says it came from "playframework.com":
Curl is not fooled. It knows the subjectAltName.dnsName is different.
Let's try Play WS:
Yep, it throws an exception.
Now let's try it with hostname verification off by setting the 'loose' option on the client:
Run the test:
It works! We have fooled WS into setting up a TLS connection with a different host, one that we have control over. If we were evil, we could then proxy https://playframework.com to the intended URL, and save off all the content or inject fake data.
Let's try Apache HttpClient 3.x:
Running this gives:
Nope. HttpClient 3.x was retired in 2007, but any code that's still using it under the hood is vulnerable to this attack.
Try this on your own code and see what it does. I'll bet it'll be interesting.
Odds and ends I couldn't cover elsewhere. And then best practices, and summing things up.