ROOTME] PHP - Serialization

노션으로 옮김·2020년 3월 22일
1

wargame

목록 보기
21/59
post-thumbnail

문제

로그인 페이지가 주어지고 PHP 소스를 확인할 수 있다.
또한, 자동로그인 설정 버튼이 존재한다.

소스를 확인해보자

<?php
define('INCLUDEOK', true);
session_start();

if(isset($_GET['showsource'])){
    show_source(__FILE__);
    die;
}

/******** AUTHENTICATION *******/
// login / passwords in a PHP array (sha256 for passwords) !
require_once('./passwd.inc.php');


if(!isset($_SESSION['login']) || !$_SESSION['login']) {
    $_SESSION['login'] = "";
    // form posted ?
    if($_POST['login'] && $_POST['password']){
        $data['login'] = $_POST['login'];
        $data['password'] = hash('sha256', $_POST['password']);
    }
    // autologin cookie ?
    else if($_COOKIE['autologin']){
        $data = unserialize($_COOKIE['autologin']);
        $autologin = "autologin";
    }

    // check password !
    if ($data['password'] == $auth[ $data['login'] ] ) {
        $_SESSION['login'] = $data['login'];

        // set cookie for autologin if requested
        if($_POST['autologin'] === "1"){
            setcookie('autologin', serialize($data));
        }
    }
    else {
        // error message
        $message = "Error : $autologin authentication failed !";
    }
}
/*********************************/
?>



<html>
<head>
<style>
label {
    display: inline-block;
    width:150px;
    text-align:right;
}
input[type='password'], input[type='text'] {
    width: 120px;
}
</style>
</head>
<body>
<h1>Restricted Access</h1>

<?php

// message ?
if(!empty($message))
    echo "<p><em>$message</em></p>";

// admin ?
if($_SESSION['login'] === "superadmin"){
    require_once('admin.inc.php');
}
// user ?
elseif (isset($_SESSION['login']) && $_SESSION['login'] !== ""){
    require_once('user.inc.php');
}
// not authenticated ? 
else {
?>
<p>Demo mode with guest / guest !</p>

<p><strong>superadmin says :</strong> New authentication mechanism without any database. <a href="index.php?showsource">Our source code is available here.</a></p>

<form name="authentification" action="index.php" method="post">
<fieldset style="width:400px;">
<p>
    <label>Login :</label>
    <input type="text" name="login" value="" />
</p>
<p>
    <label>Password :</label>
    <input type="password" name="password" value="" />
</p>
<p>
    <label>Autologin next time :</label>
    <input type="checkbox" name="autologin" value="1" />
</p>
<p style="text-align:center;">
    <input type="submit" value="Authenticate" />
</p>
</fieldset>
</form>
<?php
}

if(isset($_SESSION['login']) && $_SESSION['login'] !== ""){
    echo "<p><a href='disconnect.php'>Disconnect</a></p>";
}
?>
</body>
</html>

POST 파라미터로 아이디, 패스워드값이 전달됬으면 그걸 사용하고
그게 아닌, autologin 쿠키값으로 계정정보가 전달됬으면 이 값을 사용한다.

$auth에 존재하는 값과 비교하게 되는데 아이디가 superadmin으로서 로그인되었을 때 플래그값을 출력해준다.


풀이

PHP Serialization 취약점이라고 하면 Object Injection이 대표적인데, 이에 대한 문제는 아니었다.

https://qkqhxla1.tistory.com/444
Objection Injection 취약점에 대한 블로그 글.
간략히 말하면, 어떤 클래스에 매직함수라는 것이 선언되있을 때 unserialization 하는 과정에서 해당 클래스 함수 값을 조작 및 실행할 수 있는 취약점이다.

Type Juggling

소스코드를 보면 flag를 출력해주기 위해 아이디를 확인하는 조건문은
===로 strict comparison을 수행하지만

아이디, 패스워드를 비교할 때는 ==로 loose comparison을 사용한다.

취약한 부분을 친절히 말해주고 있다.

php online에서 loose comparison을 통과할만한 값을 찾아보니

true == 문자열 일 때, 해당 조건을 통과한다.

if(true == "anythings")
	echo "1111111111"; //output is "1111111111"

serialization

문제소스에서 autologin을 이용하면 serialization화 시킨 값으로 계정 정보를 설정할 수 있었다.

serialization에서 boolean은 1과 0으로 다뤄지기 때문에
아이디를 superadmin 비밀번호를 1로 설정한다면, 인증 과정이 통과할 것이다.

https://www.php.net/manual/en/function.serialize.php
b:value; (does not store "true" or "false", does store '1' or '0')

사용될 페이로드는 다음과 같다.

a:2:{s:5:"login";s:10:"superadmin";s:8:"password";b:1;}

[https://www.php.net/manual/en/function.serialize.php(https://www.php.net/manual/en/function.serialize.php)
Array
a:size:{key definition;value definition;(repeated per element)}

해당 값을 쿠키로 설정하고 페이지에 접속하면
플래그를 확인할 수 있다.

0개의 댓글