I was pen testing a web application last week, when I fired up ‘wfuzz’ using a custom large dictionary for file and directory brute forcing. To the non technical readers, this means that whilst there might be links on the site to say /login, /register, /contact-us and so on, I’m looking for files and directories on the web server (site) that don’t have links to them. Perhaps hidden functionality or testing and debugging files that the developers left behind and so on. I often find ‘phpinfo.php’ or ‘test.php’ type files and I once even remember finding a ‘psd.zip’ which was a zip file containing PSD files for the entire site layout.
Another common issue I find, is that while ‘index.php’ will be interpreted on the server side and the resulting data sent to the client as expected, ‘index.php.old’ and ‘index.bak’ will be sent directly to the client. This is down to the server being configured that .php files are interpreted by php, whilst unknown extensions such as .old and .bak are assumed to be plain text assets. The problem with this, is that these files will contain all kinds of goodies such as variable names, paths, business information and possibly database or other credentials. Whilst under development, pages will often undergo editing and revisions, and developers often forget to remove old versions, test and backup files. This inadvertently leaves them available to the public through the web server with just a little poking around.
Last week was something entirely different when I found ‘/nickreport’. This directory contained scripts allowing me to download a full report of customer signups and sales stats for the past 14 days for, you guessed it, Nick the sales director. The authentication prompt was defeated with credentials of ‘nick/nick’. When I confronted the application developer about this complete fail, his response was that the password authentication wasn’t for security but was just to prevent Google from crawling the site, and that there was no way that anyone would guess the URL anyway. He didn’t seem to understand the link between that statement and the fact that a) I HAD ‘guessed’ it and b) his password authentication was an attempt to prevent Google from indexing it. This alone implies that he was aware that search engines had or may in future have ‘guessed’ it.
Once he understood the immediate severity of the issue, he then asked, “what if I change it to something better, like a random alphanumeric string?” Well, certainly I can’t deny that it would be better however as I explained, the URL request string is not intended as a security mechanism. If we decide to treat the URL ‘/b8ghf6d32’ as a ‘password’, we are faced with at least the following issues:
- It appears in the server log files in plain text, there’s no transport or storage security.
- No system admin or piece of software expects directory names to be used as passwords, no one will protect it or treat it as such.
- As a URL, it will appear in the browser history and probably also address auto complete for any one to access
- Unless specifically configured with additional modules, a web server won’t ‘lock out’ a user who’s hit too many 404 errors due to file or directory brute forcing.
- A web server is designed to serve users and requests as quickly and efficiently as possible. Directory brute forcing is usually lightning fast as many concurrent threads can be used – the exact feature we don’t want when dealing with password authentication. Most password services will restrict the number of failed logins to an account and throttle the number of threads allowed at the failed login stage. They would certainly not optimize for allowing brute forcing as quickly as possible.
There are probably more problems with this approach than I’ve even thought of – just don’t do it. Any security measure that relies on the premise that, ‘no one will find it’, or ‘no one will figure it out’ is bound to be defeated in time. Why not just implement a proper authentication mechanism?