-3

Trying to learn about various SQL injection techniques for work and I'm stuck on the following. I'm trying to craft a SQL injection for the following code. My goal is to enter just the username for a known registered user (Example: test) and attach extra input to it that bypasses the following filter and will ultimately be injected into the SQL statement in the last line that would make it true and log me in as the registered user. I'm a bit lost on how to bypass the series of filters (though I'm guessing that I can use alternatives to whitespace characters to get past one of the checks?) What sort of input would work to bypass this? Thanks!

function sqli_filter($string) {
    $filtered_string = $string;
    $filtered_string = str_replace("--","",$filtered_string);
    $filtered_string = str_replace(";","",$filtered_string);
    $filtered_string = str_replace("/*","",$filtered_string);
    $filtered_string = str_replace("*/","",$filtered_string);
    $filtered_string = str_replace("//","",$filtered_string);
    $filtered_string = str_replace(" ","",$filtered_string);
    $filtered_string = str_replace("#","",$filtered_string);
    $filtered_string = str_replace("||","",$filtered_string);
    $filtered_string = str_replace("admin'","",$filtered_string);
    $filtered_string = str_replace("UNION","",$filtered_string);
    $filtered_string = str_replace("COLLATE","",$filtered_string);
    $filtered_string = str_replace("DROP","",$filtered_string);
    return $filtered_string;
}
function login($username, $password) {
    $escaped_username = $this->sqli_filter($username);
    // get the user's salt
    $sql = "SELECT salt FROM users WHERE eid='$escaped_username'";
    $result = $this->db->query($sql);
    $user = $result->next();
    // make sure the user exists
    if (!$user) {
        notify('User does not exist', -1);
        return false;
    }
    // verify the password hash
    $salt = $user['salt'];
    $hash = md5($salt.$password);
    error_log(print_r($escaped_username));
    $sql = "SELECT user_id, name, eid FROM users WHERE eid='$escaped_username' AND password='$hash'";

2 Answers2

2

Please don't build your own filters. You will regret it when you realize you overlooked something.

Here's an example of a string that would inject your filter:

'   union   all select  password    from    users   where   type    =   'Admin

(Note those are tabs, not spaces after the single quote)

Here's a demo https://3v4l.org/o8ClJ. Your string comes out as:

SELECT salt FROM users WHERE eid='' union   all select  password    from    users   where   type    =   'Admin'

Which would be executable SQL (assuming columns exist).

Use parameterized queries and prepared statements. It will handle everything you need to do.

Additional reading: How can I prevent SQL injection in PHP?
https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet

user3783243
  • 5,368
  • 5
  • 22
  • 41
  • Thanks! This is just test code to manipulate for SQL injection (don't worry, I wouldn't actually do this in real life). I don't see how this would bypass the filter though, given the whitespace filter? For more info (will edit above), the db is stored as follows: Example row: id | name | eid | password | salt | 6 | Test2 | test2 | 07d59d3c2be6ec41b795c4fbd18fc1b0 | 92992 – citrusman1234 Jul 23 '18 at 04:38
  • This doesn't use the ascii space, it uses a tab. Your filter doesn't look for tabs. See the demo link. – user3783243 Jul 23 '18 at 04:39
  • An additional note. You shouldn't use `md5` hashing anymore. See http://php.net/manual/en/function.password-hash.php and http://php.net/manual/en/function.password-verify.php – user3783243 Jul 23 '18 at 04:40
  • Ah I see. This would bypass the query for the salt, but it would return all of the salts though, right? – citrusman1234 Jul 23 '18 at 04:44
  • You can make it do whatever it needs to do for successful execution, just might need to tweak the query a bit. e.g. maybe a left join with a faulty `on` clause so the last bit doesn't matter. In any event, yes it is injectable. – user3783243 Jul 23 '18 at 04:48
-1

change the way you handle the SQL injection. Use parameter instead.

$stmt = $this->db->prepare("SELECT salt FROM users WHERE eid= ?");
$stmt->bind_param("si", $username);

For more information, check this link.

david
  • 3,225
  • 9
  • 30
  • 43
  • 1
    I think the OP is asking what code he should input as the username to achieve an sql injection with the filter that is shown in the function. – Joseph_J Jul 23 '18 at 04:05
  • @david What I was looking for was what input could be attached to the username that would bypass the filtering and UN/PW check in the SQL query. I'm not looking to fix it :) – citrusman1234 Jul 23 '18 at 04:06
  • oh, i thought you want to fix it. but, really, the possibility for that is endless. even if you cover a lot of things that would bypass the filtering, there is no guaranteed that you can cover all possibility. – david Jul 23 '18 at 04:08
  • `bind_param("si",` also is incorrect. I'd think just `i` should be there, but possibly `s` (not both). – user3783243 Jul 23 '18 at 04:17
  • aren't the first parameter can be anything? – david Jul 23 '18 at 04:17
  • @david No, it is the data type of the variables being passed to it. – user3783243 Jul 23 '18 at 04:19
  • True, I agree with you. In this case, I'm not sure what would bypass this since it covers basic whitespace and comment filtering. I'm not sure what crafted input would be added to the username to bypass each filter in succession so the password isn't needed to bypass the login. – citrusman1234 Jul 23 '18 at 04:19