После успешного взлома сервера MCP я начал изучать атаки на стороне клиента и их применение. Существуют изящные приемы и атаки, которые можно использовать в работе с браузерами. Недавно я обнаружил интересные идеи, связанные с буфером обмена.

Эта атака осуществляется через браузер, когда вы копируете код со StackOverflow или ChatGPT. Я называю это clipjacking, что можно перевести как «перехват буфера обмена». Это похоже на clickjacking, но более продвинутый. Clipjacking может стать хорошей альтернативой clickjacking'у, если последний по каким‑то причинам вам не подходит. В этой статье я расскажу о нескольких полезных техниках, которые помогут вам создать Proof‑of‑Concept (PoC) для атак на стороне клиента.

Что ты копируешь?

Я экспериментировал с браузерами и разочаровался, когда некоторые идеи не работали. Вдруг я понял: что, если заставить кого‑то скопировать важные данные, а затем украсть их из буфера обмена? Это похоже на атаку clipboard hijacking, но есть отличие. Я не придумывал термин clipjacking специально, но важно понимать разницу между clipboard hijacking и clipjacking. Теперь к сути.

Необходимые условия

Для проведения этой атаки нужны те же условия, что и для clickjacking. Необходимо иметь возможность встраивать целевой сайт через iframe. Это возможно, если на сайте отсутствуют заголовки X‑Frame‑Options или Content‑Security‑Policy с параметром frame‑ancestors. Кроме того, cookie должны иметь атрибут SameSite=None.

Пример 1

Я сделал пару PoC. Первый использует Web API буфера обмена браузера, чтобы украсть конфиденциальные данные. Он работает так:

  • Мы встраиваем iframe на основной сайт.

  • Добавляем JavaScript, чтобы iframe всегда был в фокусе:

function focusIframe() {
    const iframe = document.getElementById('target-frame');
    if (iframe.contentWindow) {
        iframe.contentWindow.focus();
    }
}
... 
// Здесь добавить код, который вызывает функцию снова и снова 
  • Используем CSS, чтобы установить значение overflow: hidden и переместить iframe чтобы пользователь его не видел.

  • Добавляем «слушатель» событий, который следит за изменениями в буфере обмена и отправляет эти данные на наш сайт (или на сервер).

  • PROFIT

Вы могли подумать: «Я могу просто выделить текст и скопировать его через буфер обмена и API браузера», — нет. У вас не получится скопировать текст из iframe. По крайней мере у меня в опытах — не получилось. Также вы не сможете просто выделить весь контент на основном сайте и скопировать его, потому что содержимое iframe не попадёт в выделение.

Вот пример с демонстрацией:

Первый пример демонстрирует, как данный способ работает, во втором примере мы посмотрим на финальную версию.

Этот пример довольно простой, и вы можете найти его на GitHub, но давайте посмотрим на другой способ, который не требует доступа к API браузера, запрашивающего разрешения.

Пример 2

В этот раз пользователю нужно будет использовать комбинации клавиш Ctrl+a, Ctrl+c и Ctrl+v. Эти действия позволят выделить всю страницу, скопировать её содержимое и вставить его. Звучит просто, не так ли? Но есть нюанс. Как отследить события внутри iframe, чтобы правильно переключиться в область для вставки? Давайте разберёмся.

Хотя мы не можем напрямую определить комбинации для выделения и копирования внутри iframe, с помощью моих советов вы сможете это сделать.

Суть в том, что если выполнить действие, например, кликнуть или нажать клавишу внутри кросс‑доменного iframe, родительский элемент активируется и может это обнаружить. Я отслеживаю все действия и использую debounce, хотя сейчас уже не помню, зачем (вы можете изменить это по своему усмотрению).

Возможно, вы спросите: «Как выбрать конкретное событие, например, копирование?». Дело в том, что в большинстве случаев это и не нужно. Достаточно отслеживать действия на основной странице и игнорировать их, пока они длятся. Есть ещё несколько моментов, которые нужно учитывать, но это основная идея.

// Слушаем действия пользователя на основном сайте
['click', 'mousedown', 'mouseup', 'keydown', 'focusin'].forEach(eventType => {
    document.addEventListener(eventType, function(e) {
        // If the output box is focused, stop focusing the iframe
        if (e.target.id === 'output') {
            outputActive = true;
            focusOnIframe = false;
        } else if (e.target.id !== 'target-frame') {
            // Pause all PoC logic for a while if user interacts with main page
            mainPagePause = true;
            clearTimeout(mainPagePauseTimeout);
            mainPagePauseTimeout = setTimeout(() => {
                mainPagePause = false;
            }, MAIN_PAGE_PAUSE_TIME);
        }
    }, true);
});
...
function detectUserActivation() {
    // Detect user activation in iframe and count actions (debounced)
    if (focusOnIframe && !outputActive && !mainPagePause && navigator.userActivation.isActive) {
        const now = Date.now();
        if (now - lastActivation > ACTIVATION_DEBOUNCE) {
            actionCounter++;
            lastActivation = now;
            if (actionCounter === 2) {
                setTimeout(() => {
                    focusOutput();
                }, 100);
            }
        }
    }
}

Я попросил Claude помочь мне, чтобы разобраться с логикой, и он добавил лишнюю переменную, выполняющую ту же роль, что и focusOnIframe, но я её не трогаю). Пример этого PoC также доступен в том же репозитории на GitHub.

Вот как выглядит второй PoC (атакующий iframe можно скрыть):

На этом все, спасибо за внимание и не забывайте добавлять необходимые опции на сайте, чтобы не попадаться на такие трюки.

Спасибо за внимание, ваш Cloud4Y. Читайте нас здесь или в Telegram‑канале!

Комментарии (0)