HKCert20 CTF : LockPickDuck v3 (I) (II) (III)
===
(25 pts, 23 solves) - (25 pts, 17 solves) - (428 pts, 7 solves)
Solver: T0022 - HKUST Member 2
This challenge has part (I), (II), (III) and (IV). This is the writeup of (I), (II) and (III). For part (IV), please [click here](https://hackmd.io/@hoifanrd/SkIY868tD).
http:</span>//te</span>rtiary.pwn</span>able.h</span>k:50011
```php=
<?php
class SQZero3 extends SQLite3 {
private $user;
private $pass;
function __construct($user, $pass) {
$this->open(":memory:");
$this->exec("CREATE TABLE users (user text, pass text, hash text)");
$this->user = $user;
$this->pass = $pass;
}
function checkHash(){
return @($this->querySingle("SELECT hash FROM users WHERE user='{$this->user}' AND pass='{$this->pass}'") == md5($this->pass));
}
function checkUser(){
return @($this->querySingle("SELECT user FROM users WHERE user='{$this->user}' AND pass='{$this->pass}'") == $this->user);
}
function checkPass(){
return @($this->querySingle("SELECT pass FROM users WHERE user='{$this->user}'") == $this->pass);
}
function checkMate(){
return @($this->querySingle("SELECT hash FROM users WHERE user='{$this->user}' AND pass='{$this->pass}'") === md5($this->pass)) &&
@($this->querySingle("SELECT user FROM users WHERE user='{$this->user}' AND pass='{$this->pass}'") === $this->user) &&
@($this->querySingle("SELECT pass FROM users WHERE user='{$this->user}'") === $this->pass);
}
}
if (isset($_GET["user"]) && isset($_GET["pass"])) {
require("flag.php");
$sq = new SQZero3($_GET["user"], $_GET["pass"]);
if ($sq->checkHash()) {
echo "<p>Flag 1: $flag1</p>";
if ($sq->checkUser()) {
echo "<p>Flag 2: $flag2</p>";
if ($sq->checkPass()) {
echo "<p>Flag 3: $flag3</p>";
}
}
} else {
echo "No Flag";
}
if ($sq->checkMate()) {
echo "<p>Flag 4: $flag4</p>";
}
} else {
highlight_file(__FILE__);
}
?>
```
Okay. So we are going to get Flag 1-3. Let's analyze the php code first.
### Analyze
As we can see, the website use _GET_ to get the field _user_ and _pass_. If the both fieids are set, it will first create a **BLANK** SQLite table. Then it will do a single query from that black table, to see if the query return value equals a specfic value. A worth note point is that the table is a blank table. How could it return value other than NULL? So we know that this is a SQL Injection challenge (Reference: [CTF Wiki](https://ctf-wiki.github.io/ctf-wiki/web/sqli/)). We could also see that we have to satisfiy the situation of Flag 1 and 2 in order to get Flag 3. In other words, we get Flag 1 and 2 if we can get Flag 3. Let's see at what situation we can get Flag 3!
```sql
SELECT hash FROM users WHERE user='{$user}' AND pass='{$pass}' == md5($pass) AND
SELECT user FROM users WHERE user='{$user}' AND pass='{$pass}' == $user AND
SELECT pass FROM users WHERE user='{$user}' == $pass
```
If we could satisfy this statement, then we could get Flag 1, 2 and 3!
### `==` $\neq$ `===`
If you play Web Challenge much, this is actually a easy task. You could notice that the statement is using the `==` operator but not the `===` operator! By [PHP manual](http://php.net/manual/en/language.operators.comparison.php#language.operators.comparison), `$a === $b` will return TRUE if *\$a* is equal to *\$b*, and they are of the same type (identical). While`$a == $b` will return TRUE if *\$a* is equal to *\$b* **after type juggling**. It means that the type of the operand will change if they are not the same type. Therefore, when a `string` is compared with a `int` value by the `==` operator, the `string` will be **converted** to `int`! Following the [conversion rule](https://www.php.net/manual/en/language.types.string.php#language.types.string.conversion), if the `string` is not started with a numeric value, it will be converted to **0**.
In this task, `md5($pass)`, `$user` and `$pass` are all `string`. Therefore, if they are compared with a numeric value, all of them will be converted to **0**! So just do the injection to make the SQL query return 0 and we will get all 3 flags! Easy points, huh?
The final payload will be:
*\$user*: `' UNION SELECT 0 -- `
while *\$pass* can be anything (just don't make it and it's md5 start with numeric value).
</br>
And this will make the final query be:
```sql
SELECT something FROM users WHERE user='' UNION SELECT 0 -- ' AND pass='abc'
```
Payload URL: `http://tertiary.pwnable.hk:50011?user=' UNION SELECT 0 -- &pass=anything`
###### tags: `CTF`, `HKCert20CTF`