The Target Intranet
This appeared to be an entirely custom application, and we had no prior knowledge of the application nor access to the source code: this was a “blind” attack. A bit of poking showed that this server ran Microsoft’s IIS 6 along with ASP.NET, and this suggested that the database was Microsoft’s SQL server: we believe that these techniques can apply to nearly any web application backed by any SQL server.
The login page had a traditional username-and-password form, but also an email-me-my-password link; the latter proved to be the downfall of the whole system.
When entering an email address, the system presumably looked in the user database for that email address, and mailed something to that address. Since my email address is not found, it wasn’t going to send me anything.
So the first test in any SQL-ish form is to enter a single quote as part of the data: the intention is to see if they construct an SQL string literally without sanitizing. When submitting the form with a quote in the email address, we get a 500 error (server failure), and this suggests that the “broken” input is actually being parsed literally. Bingo.
We speculate that the underlying SQL code looks something like this:
SELECT fieldlist FROM table WHERE field = '$EMAIL';
Here, $EMAIL is the address submitted on the form by the user, and the larger query provides the quotation marks that set it off as a literal string. We don’t know the specific names of the fields or table involved, but we do know their nature, and we’ll make some good guesses later.
When we enter [email protected]’ – note the closing quote mark – this yields constructed SQL:
SELECT fieldlist FROM table WHERE field = '[email protected]'';
when this is executed, the SQL parser find the extra quote mark and aborts with a syntax error. How this manifests itself to the user depends on the application’s internal error-recovery procedures, but it’s usually different from “email address is unknown”. This error response is a dead giveaway that user input is not being sanitized properly and that the application is ripe for exploitation.
Since the data we’re filling in appears to be in the WHERE clause, let’s change the nature of that clause in an SQL legal way and see what happens. By entering anything’ OR ‘x’=’x, the resulting SQL is:
SELECT fieldlist FROM table WHERE field = 'anything' OR 'x'='x';
Because the application is not really thinking about the query – merely constructing a string – our use of quotes has turned a single-component WHERE clause into a two-component one, and the ‘x’=’x’ clause is guaranteed to be true no matter what the first clause is (there is a better approach for this “always true” part that we’ll touch on later).
But unlike the “real” query, which should return only a single item each time, this version will essentially return every item in the members database. The only way to find out what the application will do in this circumstance is to try it. Doing so, we were greeted with:
Your login information has been mailed to [email protected].
Our best guess is that it’s the first record returned by the query, effectively an entry taken at random. This person really did get this forgotten-password link via email, which will probably come as surprise to him and may raise warning flags somewhere.
We now know that we’re able to manipulate the query to our own ends, though we still don’t know much about the parts of it we cannot see. But we have observed three different responses to our various inputs:
- “Your login information has been mailed to email“
- “We don’t recognize your email address”
- Server error
The first two are responses to well-formed SQL, while the latter is for bad SQL: this distinction will be very useful when trying to guess the structure of the query.