Under the Hood: SQL Injection

I witnessed someone trying to inject on a login form and what was expected and the actual result were night and day.  The idea that if we find an injection point by entering a single tick does not necessarily mean we are going to be able to successfully enter ' or '1'='1 and achieve a positive outcome.  In the example below, there at least two components to this injection, we have a PHP front-end with a MySQL back-end.  The latter may cooperate, and it does, but it's the former that is determining what we can do and where we can do it.

Below, we have a simple login form.  We enter a single tick:





When we select Submit, we get:





Excellent!  It appears as if we have an injection point.  This form is tied to a database titled:  website  and a table titled:  Logins

If we pull all of the records, we see the following:





When we look at our login form:





We don't really know what's behind it.  But if we look at the PHP:





We see that our input is taken directly into the query which is why we are able to inject.  The problem with the rest of this script could be obvious but if not, keep following along.

Back to our login, we're going to inject on the password field which in some cases would bypass the mechanism:





When we build this query in MySQL, we are not using the correct password but 1=1 (a true statement) and therefore, the results are returned:





And yet when we hit Submit on the login form, we get:





This time, let's inject on the username field:





If we run this same query in MySQL, the results are returned.  Again, 1=1 is true and that's where it ends.





When we hit Submit on the login form:





We have a successful login.  

Now let's remove all of the records except for one:





And then we're going to head back to the login and inject on password as we did previously:





When we hit submit:





At this point, the username and password are irrelevant with only one record in the table.  If we enter an incorrect username and password:





When we hit submit:





If we take these queries into MySQL:





Both yield results.  

Back when I said there are at least two components, the PHP aspect of this equation is where this is decided based on how the form was built:





While injecting on the username field of a login form may yield a positive outcome, in this case it will not because the result yields multiple records.  

if ($count ==1)

The above is counting the number of returned results, if it's not equal to one, we fail to login.  

I've changed the alert message to display the number of $count rather than the previous pass/fail messages.

We are going to enter the correct username and password:





When we hit submit:





$count == 1

Pass.

Now let's add an additional record back into the table:





Now we're going to inject on password:  





We hit submit:





Since $count is equal to 2, we fail.

Adding another record:





Repeating the injection:





How and where we can inject will be defined by the front and back end.  

I've been teaching introductory penetration testing classes and I've been avoiding tools like Metasploit, SQLMap, and the like.  Tools are awesome but learning and understanding manual techniques are invaluable.  Personally, that approach has served me well.  

That being said, let's fire up SQLMap and take this as far as we can.  First, we'll grab a post request from Burp:




Firing up SQLMap:





SQLMap finds an injection method and it returns the databases to us:





We re-point SQLMap to retrieve information on the "website" database:





It retrieves the Logins table and its columns:  





Now, we want to retrieve the data from the Logins table:





The results:





Jackpot!  

There are quite a few functions that won't work with more recent versions of Linux / MySQL.  The --os-shell function for one.  Basically, anything in MySQL will likely work but anything reaching out of MySQL will not.  This is easily tested from within MySQL itself.  Attempting to read /etc/passwd:




No joy.

And when we attempt to write into the file system:






No joy.  

Best we can do is to work with what we have and that's staying inside of MySQL.  When we launch into the --sqlmap-shell:





We execute the --passwords command:





We uncover yet another piece to the puzzle.  We move this over to Hashcat:





Maybe this moves the ball forward.  But then again, that wasn't really the point of this post.  ;)