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`