Hackerlab Цирковой трюк(WriteUp)

vov4ick

Green Team
11.12.2022
60
132
В данном райтапе речь пойдет о таске Цирковой трюк из категории веб.

1746290859336.webp


Приложение банальное, есть форма авторизации, через которое нам нужно попасть на правах администратора.
Открываем код и находим интересные вещи:
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;
}
2. Этот запрос представляет собой SQL-запрос, который выполняется в базе данных для извлечения хешированного пароля пользователя с указанным логином:
PHP:
$sql = "SELECT password_hash FROM user_creds WHERE login = '$login'";
3. Если запрос выполнен успешно и возвращается хотя бы одна строка (пользователь найден), происходит проверка пароля:
PHP:
if (hash('sha256', $password) == $row['password_hash']) {
    $success = true;
}

Достаем из базы все, что есть:

sqlmap -u --data='login=vov4ick&password=1' --dump --dbms='MySQL' -batch

1746291524000.webp

1746291564089.webp


Здесь нам нужен хеш администратора, если обратить на него внимание, то это даст нам нужный вектор.

На поле выходит новый игрок!

Screenshot_1.webp


В PHP есть такая особенность (называемая PHP Type Juggling), когда нестрогое сравнение (==) приводит строки, начинающиеся с 0e, к числу 0. Это значит, что если хеш строки начинается с 0e, это может привести к тому, что хеш и введенный пароль, если оба приведены к числу 0, будут приравнены друг к другу при нестрогом сравнении.

PHP:
if (hash('sha256', $password) == $row['password_hash']) {

В данном случае нам не обязательно знать пароль от учетки администратора. Если взять строку, которая при преобразовании в sha256 получает хеш, который начинается с 0e, то мы сможем использовать ее вместо пароля.

1746306144523.webp


Пробуем авторизоваться и успешно забираем флаг:

Screenshot_2.webp
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!