2

I am struggling exploiting this vulnerability in the code below:

<?php
ini_set('display_errors', 0);

define("INDEX", 1);
include '../db.php';

if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  $query = "SELECT * FROM banned_ip WHERE ip='$ip'";

  $res = mysql_query($query);

  if(mysql_num_rows($res) > 0) {
      die('<h2>Banned!</h2>');
  }
}
?>

There is blind sql injection in X-Forwarded-For header in the code above. I tried different payloads from OWASP Testing Guide and read this course on Pentesterlab but I still find it impossible to exploit this.

My assumptions:

  • banned_ip table is empty so this check: if(mysql_num_rows($res) > 0) returns false. There need to be a way to insert into this table or perform select from another table to return non-zero response.
  • Backend database seems to be MySQL $res = mysql_query($query); so there might be some commands that I am unaware of specific to MySql that can help
  • I was a bit surprised to find that we do not seem to already have a general blind sql injection question. This is also the first question on sec.SE that I could find via google. Do you mind if the question is edited to be a bit more general? It doesn't really seem to matter that the attack vector is a HTTP header, but that the injection is blind and into a select clause (there would be other possibilities in update/insert/delete). – tim May 26 '17 at 12:26
  • Sure, if you want to edit this post you are free to do so. Please do not edit or delete existing php code and do not delete information about sqli in X-Forwarded-For header. – jpiechowka May 26 '17 at 13:33
  • Those were actually exactly the two things I wanted to edit, so I'll keep the question as-is for now. I would have changed the title to Manually exploiting blind SQL injection in SELECT statement because that would make it more applicable to other users (the attack vector is mostly irrelevant, the important part is that it's blind and into select; and also that it's PHP and MySQL, but that seemed too long for a title), and I would have removed the second code block to shorten the question (it's just a repetition). – tim May 26 '17 at 13:47
  • 1
    I edited the question and title. I would like to keep information about the attack vector. – jpiechowka May 26 '17 at 14:01
1

Non-working approaches (multi-query, insert/delete/update)

mysql_query doesn't support multi-query:

multiple queries are not supported

This means that appending another query via ; to delete, edit, or insert data will not work. You are stuck with the select query you have.

We can also see that the selected data is not displayed, so you indeed have a blind injection.

Unlikely approach: Write a Shell File

Theoretically, you could try to write data to a file, for example to get a PHP shell. It might look something like this:

' UNION SELECT '<?php passthru($_GET['x']);' INTO OUTFILE '/var/www/404.php' -- -

It is however unlikely that you have sufficient permissions for this. You would not only need an SQL user that has the permission to write to a file and a directory that the mysql user can actually write to, but the mysql server would also need to be running without the --secure-file-priv option, which isn't the case by default in modern systems.

Read Data Blind: Error Based

The above means that the only attack-vector is reading data from the database. If you are lucky, errors will be displayed to you.

If errors are displayed, you will simply need to create an error which contains the information you want to read. One way is to use extractvalue:

' or extractvalue(1,version()) -- -

Instead of simply asking for the version, you can select table- and column-names from the INFORMATION_SCHEMA. Once you find interesting tables, read the data.

Read Data Blind: Content Based

Maybe you were unlucky, and errors are not shown. You can still extract data by asking yes/no questions:

' AND substring(version(),1,1)='5

As you can see, the whole query including your injection will return true if the MySQL version is 5, and false otherwise. So depending on what happens - the script dies or it doesn't - you know what version MySQL is.

Instead of asking for the version, you can ask for other data, for example the first character of the password hash of the first user in the mysql users table.

You can optimize the approach by using ascii and asking if the value is larger or smaller than a certain number, and then narrow the window of possible character.

You will probably want to use a tool or custom script for actual exploitation though, as you will need a lot of requests.

Read Data Blind: Timing Based

Just for completeness sake: In case that you get no feedback at all (no banned message), you could still exploit injections into select based on the time it takes for a request to complete.

The idea is the same as content based exploitation, but instead of getting a different response, you will either perform a slow task or not:

' AND IF(SUBSTRING(version(), 1, 1)='5',BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null) %23
0

What do you expect to be returned from the query? Its blind so you can't probably see anything.

If you have deployed it in your testing environment you can check if you are able to manipulate records in the db. From what I can see you definitely can.

In this case, you can i.e. do:

X-Forwarded-For: '; DELETE * FROM banned_ip; '

during next request the IP will not be banned anymore if DELETE is allowed to the user connecting the DB.

Its hard to do anything else if you don't know the table/database structure. If you would know it you can insert IP there and then check if its banned.

You can use some kind of automated tool which will try to probe various tables and columns the value to be inserted to (so called brute force attack of course) then check if the IP has been banned.

Moreover, using this vulnerability you will have access to whole database. If there is table named users (what is 99,9% of cases) with columns username and password, you can try to insert an user there with pass hashed with MD5, then SHA, then SHA2... There are so many oportunities...

But don't expect you'll be able to see anything. Thats why its called "blind".


Edit: By the way, I can see in the example what you have to achieve. Its exactly written there. And also, it is exactly written there how to achieve that. Basically, you should obtain access to admins table and grab a password hash from there. Consequently, you have to attack the application using admin credentials.

  • A good example of why correct access control should be applied to database connections. It may be in this case connecting with a read only connection could have prevented the example given by @Fis – ISMSDEV May 26 '17 at 11:48
  • Thats hard to achieve but I agree ;) But as this seems to be an example of blind from the testing example guide so it will be possible. – Fis May 26 '17 at 11:51
  • I really like the idea to insert new user into database. I will try it out. – jpiechowka May 26 '17 at 12:00
  • Have you actually tried this? Because it shouldn't work. According to the documentation, mysql_query does not support multi-queries. This means any injection which tries to append another query via ; will fail. – tim May 26 '17 at 12:03
  • @tim Not yet. I will not be able to test this right now. – jpiechowka May 26 '17 at 12:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.