로그인 페이지가 주어지고 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 하는 과정에서 해당 클래스 함수 값을 조작 및 실행할 수 있는 취약점이다.
소스코드를 보면 flag를 출력해주기 위해 아이디를 확인하는 조건문은
===
로 strict comparison을 수행하지만
아이디, 패스워드를 비교할 때는 ==
로 loose comparison을 사용한다.
취약한 부분을 친절히 말해주고 있다.
php online에서 loose comparison을 통과할만한 값을 찾아보니
true == 문자열
일 때, 해당 조건을 통과한다.
if(true == "anythings")
echo "1111111111"; //output is "1111111111"
문제소스에서 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)}
해당 값을 쿠키로 설정하고 페이지에 접속하면
플래그를 확인할 수 있다.