Identifying SQLi via Error-based Paylaods
We can start our vulnerability analysis using some basic PHP code:
Since both the uname and password parameters come from user-supplied input,
we can control the $sql_query variable and craft a different SQL query.
In some cases, SQL injection can lead to authentication bypass,
which is the first exploitation avenue we’ll explore.
By forcing the closing quote on the uname value and adding an OR statement
followed by a comment separator and two forward slashes,
we can prematurely terminate the SQL statement.
The SQL query assigned to the $sql_query variable results in the SQL query
being forwarded from the PHP application to the MySQL server.
Since we have appended an OR statement that will always be true,
the WHERE clause will return the first user id present in the database,
whether or not the user record is present.
Because no other checks are implemented in the application,
we are able to gain administrator privileges
by circumventing the authentication logic.
To experiment with this attack against a real application,
we can browse to the SQLi playground from our Kali machine,
and attempt to log in with “offsec” and “jam” as the username and password.
Because the offsec user is not valid,
we receive an Invalid Password error message.
As a next step, let’s try to insert any special character inside the
Username field to test for any interaction with the underlying SQL server.
We’ll append a single quote to the username and click Submit again.
We receive an SQL syntax error this time,
meaning we are able to interact with the database.
Let’s test the authentication payload we discussed earlier
by pasting it inside the Username field.
Nice! This time we received an Authentication Successful message,
meaning that our attack succeeded.
To further expand on our attack,
we could also take advantage of the error-based payload
by enumerating the database directly.
By prematurely terminating the implied SQL query again,
we can inject an arbitrary second statement:
In this case, we want to retrieve the MySQL version
via the @@version directive.
We can now paste the injection payload in the Username field
and verify the returned output.
The running MySQL version
is included along with the rest of the web application payload.
This means we can query the database interactively,
similar to how we would use an administrative terminal.
As it seems we have unbounded control over database queries,
let’s try to dump all the data inside the users table.
After inserting the value into the Username field and submitting the query,
we receive an error.
This means that we should only query one column at a time.
Let’s try to grab only the password column from the users table.
After submitting the payload,
we receive several errors along with values resembling MD5 password hashes.
This is somewhat helpful,
as we managed to retrieve all user password hashes;
however, we don’t know which user each password hash corresponds to.
We can solve the issue by adding a WHERE clause
specifying which user’s password we want to retrieve, in this case admin.
Once we submit the payload,
we receive the user’s password along with the usual error message.
Nice!
We managed to predictably fetch hashed user credentials
via the error-based SQL injection vulnerability we discovered.