Update [12/10/2010]: Since writing this back in October 2009, I’ve written a Proof of Concept Python tool to exploit the vulnerabilities discussed. More information can be found here.
A few days back I was alerted to a new website that was offering a new way to hide your email address online. It sounded interesting so I headed over to the scr.im website and had a quick poke around. As you can guess, I’m a bit suspicious of these kind of services and web application security is what I do (at least recently). So there were a few things that really jumped out at me straight off in reference to the sites use of CAPTCHA.
Now I’m pretty sure a big portion of people reading this are already saying WTF just by looking at the above screenshot. This is not the way to use CAPTCHA. For a visitor to the site this is nice and quick, however for a script this is just a matter of playing the odds. The correct CAPTCHA has to be one of the 9 possible links displayed, certainly much better odds than attempting to crack the CAPTCHA itself. If a link is selected and it’s incorrect, the CAPTCHA screen can be reloaded (with new CAPTCHA options of course) and you can try again. Sure for a human it’s tedious to do this, it took me 11 tries to get through by simply always selecting the first CAPTCHA option (top left corner, Y9VJJ in the above screenshot). However for a scripted attack, this is a no brainer and shouldn’t take more than a few seconds. The slowest part would be the page reload. There doesn’t seem to be any timeout, lockout or any other such protections to prevent this kind of attack. Still the page reload is the bottleneck here.
As with most web applications the POST request isn’t strictly enforced on the server side. As such you can easily change this to a single GET request (e.g. http://scr.im/test?captcha=46UU8&action=view&token=e6f0006403ab0c714034f25bc571aa93&ajax=y) which makes things a little easier when scripting a solution.
The screenshots below show the responses from the scr.im server (both negative and positive).
scrim negative response
scrim positive response
Don’t get me wrong here, the idea is sound, and I’d really like to use something like this myself under certain circumstances (not for my primary accounts, but for some things certainly). So what can be done to fix things up .:
- Add protection to prevent multiple options from being selected from the same CAPTCHA options (one-time token) *
- Resolves: The possibility to select ALL 9 CAPTCHA options and scraping the responses
- Issue: Doesn’t stop an attacker reloading and trying again
- Add protection to stop more than X number of requests per URL, per second/minute *
- Resolves: Brute-Force style attacks, automated attacks (to some extent)
- Issue: Could cause DoS against peoples links
- If the wrong CAPTCHA is selected X times, revert to traditional CAPTCHA entry *
- Resolves: Brute-Force style attacks, automated attacks
- Issues: Same issues as traditional CAPTCHA, breakable
* Won’t prevent all possible attacks if implemented alone
By mixing and matching the above solutions the attack vectors could easily be reduced, but never truly removed completely. However with the use of the 3×3 grid CAPTCHA there is always that 1 in 9 chance of the attacker choosing the correct CAPTCHA on the first try. Given these odds a single attacker could (in theory) harvest 11.11% of email addresses on the first attempt purely by guessing (even with the above implemented restrictions). Depending on how/what protections are implemented, the attacker could then come back 8 more times (within minutes, hours, days) to scrape the remaining addresses. There will certainly be email addresses not scraped, but for a spammer, this isn’t a big issue.
With this in mind, the only workable solution is to alter the way CAPTCHA protection is implemented to go with the more traditional “typing” option. It’s tried and tested, and although it’s not perfect, it does resolve a number of the issues faced by scr.im currently.
Disclaimer: I’m not a developer, and I have the utmost respect for those who take time to put up services like this online. Hopefully people can learn from what I’ve explained here, so we don’t all make the same mistakes next time.