vov4ick
Green Team
- 11.12.2022
- 60
- 132
В данном райтапе речь пойдет о таске Цирковой трюк из категории веб.
Приложение банальное, есть форма авторизации, через которое нам нужно попасть на правах администратора.
Открываем код и находим интересные вещи:
Остановимся на этих ключевых моментах:
1. Если логин пользователя — administrator, то происходит попытка прочитать файл /opt/flag.txt:
2. Этот запрос представляет собой SQL-запрос, который выполняется в базе данных для извлечения хешированного пароля пользователя с указанным логином:
3. Если запрос выполнен успешно и возвращается хотя бы одна строка (пользователь найден), происходит проверка пароля:
Достаем из базы все, что есть:
sqlmap -u
Здесь нам нужен хеш администратора, если обратить на него внимание, то это даст нам нужный вектор.
На поле выходит новый игрок!
В PHP есть такая особенность (называемая PHP Type Juggling), когда нестрогое сравнение (==) приводит строки, начинающиеся с 0e, к числу 0. Это значит, что если хеш строки начинается с 0e, это может привести к тому, что хеш и введенный пароль, если оба приведены к числу 0, будут приравнены друг к другу при нестрогом сравнении.
В данном случае нам не обязательно знать пароль от учетки администратора. Если взять строку, которая при преобразовании в sha256 получает хеш, который начинается с 0e, то мы сможем использовать ее вместо пароля.
Пробуем авторизоваться и успешно забираем флаг:
Приложение банальное, есть форма авторизации, через которое нам нужно попасть на правах администратора.
Открываем код и находим интересные вещи:
PHP:
<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
$success = false;
$error = '';
$servername = "127.0.0.1";
$username = "<fake_username>";
$password = "<fake_password>";
$dbname = "users";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Ошибка подключения: " . $conn->connect_error);
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$login = $_POST['login'];
$password = $_POST['password'];
$sql = "SELECT password_hash FROM user_creds WHERE login = '$login'";
$result = $conn->query($sql);
if (!$result) {
$error = "Ошибка SQL: " . $conn->error;
} else {
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
if (hash('sha256', $password) == $row['password_hash']) {
$success = true;
if ($login === 'administrator') {
$flag = file_get_contents('/opt/flag.txt');
if ($flag === false) {
$error = "Не удалось прочитать флаг.";
}
} else {
$flag = null;
}
} else {
$error = 'Неверный логин или пароль';
}
} else {
$error = 'Неверный логин или пароль';
}
}
}
$conn->close();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Вход | Цирковой трюк</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
font-family: 'Arial', sans-serif;
}
.container {
max-width: 500px;
margin-top: 100px;
}
h2 {
text-align: center;
margin-bottom: 20px;
}
.alert {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container mt-5">
<?php if ($success): ?>
<?php if ($login === 'administrator'): ?>
<div class="alert alert-success">Успешная авторизация! Забирайте флаг - <?php echo htmlspecialchars($flag); ?>.</div>
<?php else: ?>
<div class="alert alert-info">Добро пожаловать, <?php echo htmlspecialchars($login); ?>!</div>
<?php endif; ?>
<?php else: ?>
<div class="row justify-content-center">
<div class="col-md-12">
<h2>Авторизация</h2>
<?php if (!empty($error)): ?>
<div class="alert alert-danger">
<?php echo htmlspecialchars($error); ?>
</div>
<?php endif; ?>
<form method="POST" action="">
<div class="mb-3">
<label for="login" class="form-label">Логин</label>
<input type="text" class="form-control" id="login" name="login" required autocomplete="off">
</div>
<div class="mb-3">
<label for="password" class="form-label">Пароль</label>
<input type="password" class="form-control" id="password" name="password" required autocomplete="off">
</div>
<button type="submit" class="btn btn-primary w-100">Войти</button>
</form>
</div>
</div>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Остановимся на этих ключевых моментах:
1. Если логин пользователя — administrator, то происходит попытка прочитать файл /opt/flag.txt:
PHP:
if ($login === 'administrator') {
$flag = file_get_contents('/opt/flag.txt');
if ($flag === false) {
$error = "Не удалось прочитать флаг.";
}
} else {
$flag = null;
}
PHP:
$sql = "SELECT password_hash FROM user_creds WHERE login = '$login'";
PHP:
if (hash('sha256', $password) == $row['password_hash']) {
$success = true;
}
Достаем из базы все, что есть:
sqlmap -u
Ссылка скрыта от гостей
--data='login=vov4ick&password=1' --dump --dbms='MySQL' -batchЗдесь нам нужен хеш администратора, если обратить на него внимание, то это даст нам нужный вектор.
На поле выходит новый игрок!
В PHP есть такая особенность (называемая PHP Type Juggling), когда нестрогое сравнение (==) приводит строки, начинающиеся с 0e, к числу 0. Это значит, что если хеш строки начинается с 0e, это может привести к тому, что хеш и введенный пароль, если оба приведены к числу 0, будут приравнены друг к другу при нестрогом сравнении.
PHP:
if (hash('sha256', $password) == $row['password_hash']) {
В данном случае нам не обязательно знать пароль от учетки администратора. Если взять строку, которая при преобразовании в sha256 получает хеш, который начинается с 0e, то мы сможем использовать ее вместо пароля.
Пробуем авторизоваться и успешно забираем флаг: