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

Мы также поговорим о преимуществах использования биометрических считывателей в системах контроля и управления доступом (СКУД) и об их роли в обеспечении должного уровня безопасности в современных реалиях. Кроме того, мы рассмотрим уязвимости биометрического считывателя производства крупной международной компании, выявленные в ходе анализа его защищенности. Статья будет полезна как исследователям, так и архитекторам систем безопасности.

Мы уведомили производителя обо всех найденных уязвимостях и проблемах безопасности. Для каждого типа уязвимостей были зарегистрированы CVE: CVE-2023-3938, CVE-2023-3939, CVE-2023-3940, CVE-2023-3941, CVE-2023-3942, CVE-2023-3943.

Краткий обзор биометрических терминалов

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

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

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

Еще одно применение биометрических терминалов — учет рабочего времени сотрудников. Это позволяет повысить эффективность труда и уменьшить вероятность мошенничества со стороны работников.

С точки зрения безопасности можно выделить следующие преимущества биометрических терминалов.

Высокая точность идентификации: биометрические данные уникальны для каждого человека, что делает их надежным способом проверки личности.
Безопасность: биометрические данные трудно подделать или скопировать, что повышает безопасность системы.
Удобство: биометрическая идентификация не требует от пользователей запоминания паролей или наличия карт доступа.
Эффективность: биометрические терминалы могут быстро обрабатывать большое количество данных, что сокращает время ожидания.

При этом существуют и минусы использования подобных устройств.

Стоимость: биометрические терминалы обычно стоят дороже традиционных систем контроля доступа.
Риск ошибок: хотя биометрические данные уникальны, бывают случаи, когда система ошибочно идентифицирует человека (например, при повреждении отпечатков пальцев).
Приватность: некоторых людей может беспокоить, что их биометрические данные хранятся и используются без их согласия.
Ограниченность технологии: некоторые биометрические методы (такие, как распознавание лиц) могут быть менее эффективны в определенных условиях (например, при плохом освещении или при наличии маски).

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

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

обход аутентификации и нарушение физического доступа;
утечка биометрических данных пользователей;
получение сетевого доступа к устройству и использование его для дальнейшего развития атаки.

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

Краткий обзор исследуемого устройства

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

Внешний вид устройства

Устройство имеет несколько физических интерфейсов и поддерживает четыре способа аутентификации пользователя: биометрический (с помощью лица), аутентификацию по паролю, электронному пропуску или QR-коду.

В устройстве есть следующие физические интерфейсы:

RJ45;
RS232;
RS485 (не используется);
Wiegand In/Out.

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

Доступные кнопки на сенсорном экране

После нажатия кнопки на экране появится запрос на ввод некоего ПИН. В нашем случае его значение — уникальный идентификатор пользователя (ID).

Запрос на ввод ID пользователя

После ввода верного ID (под верным подразумевается ID существующего пользователя) на экране появятся возможные способы аутентификации для этого пользователя. В качестве примера рассмотрим пользователя с ID 1, которому доступно два способа аутентификации: биометрический и парольный.

Способы аутентификации, доступные пользователю с ID 1

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

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

Административное меню настройки устройства

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

Исследование устройства методом черного ящика (Black Box analysis)

Схемотехнический анализ устройства

Технический анализ устройства мы начнем с черного ящика, а именно со схемотехнического анализа. Ниже изображена плата устройства, на которой мы отметили интересные нам элементы:

SOC (HI 3516 DV300)
RAM (K4B4G16E-BCMA, 4 ГБ)
Флеш-память (THGBMJG6C1LBAI, BGA-153, 8 ГБ)
UART

Плата устройства

Можно заметить, что на плате много тестпоинтов. Однако нам интересны только те, что мы отметили цифрой 4, так как там находится универсальный асинхронный приемопередатчик (UART), с помощью которого мы можем взаимодействовать с устройством. Помимо этого, интерес представляет отмеченная цифрой 3 флеш-память, на которой находится вся прошивка устройства в незашифрованном виде.

Чтобы убедиться, что мы правильно определили UART, с помощью осциллографа мы подключились к предполагаемому порту TX, через который устройство передает данные вовне.

Подключение осциллографа к UART

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

Лог загрузки устройства

Далее мы подключились к UART с помощью компьютера, что позволило увидеть полноценный лог загрузки устройства и определить, что используется загрузчик U-Boot.

Подключение к UART с компьютера

Конфигурация загрузчика не позволяет прервать его автозагрузку (bootdelay = -2) или взаимодействовать с ним каким-либо иным образом. Однако, немного подождав после загрузки устройства, мы обнаружили, что UART переключается на другую скорость — с 115 200 бод (бит в секунду) на 57 600 — а устройство начинает отправлять пакеты унифицированного формата, указывающего на использование неизвестного протокола.

Неопознанный протокол на UART

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

Сетевой анализ устройства

Еще один вид анализа при использовании черного ящика — это сканирование сетевых портов устройства. Используя Nmap (публичная утилита для сканирования сети), мы можем определить, какие порты открыты, и попытаться понять, какие сервисы и в какой версии на них запущены. На скриншоте ниже представлены открытые TCP-порты для нашего биометрического терминала.

Открытые порты на устройстве

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

Помимо этого, есть два сервиса, которые не определились автоматически. Сервис на порту 6668/tcp представляет собой Tuya Server, однако мы не смогли определить, для чего он нужен. Сервис на порту 4370/TCP оказался более интересным, так как он использует проприетарный протокол вендора, который поддерживают многие его устройства. Поискав этот протокол в интернете, мы выяснили, что для него существует документация, что сильно упрощает анализ.

Поиск протокола на порту 4370/TCP

Анализ камеры устройства и считывателя QR-кодов

В обзоре устройства мы упоминали, что оно поддерживает аутентификацию по QR-коду. Мы решили проверить, что произойдет, если в демонстрируемом коде будут некорректные данные, которые потенциально могут нарушить логику его обработки. Достичь результата удалось, показав устройству QR-код с SQL-инъекцией.

Простейшая SQL-инъекция привела к тому, что устройство посчитало нас корректным пользователем.

Получение доступа с помощью SQL-инъекции

Также мы выявили, что при демонстрации QR-кода с большим количеством данных (от 1 КБ) устройство экстренно перезагружается, что может намекать на переполнение в некоторых компонтентах. Подробнее об этом мы расскажем в разделе обратной разработки и исследования прошивки.

Получение и распаковка прошивки

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

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

Поиск прошивки в интернете

Чтобы начать поиск нужной нам прошивки, мы должны узнать ее название и примерную версию. Поскольку мы исследовали чистое устройство «из коробки», у нас был административный доступ. Соответственно, мы могли посмотреть информацию об устройстве и найти версию текущей прошивки.

Информация о прошивке в меню настроек устройства

В нашем случае версия была ZAM170-NF-1.8.25-7354-Ver1.0.0. Мы использовали для поиска эту строку, а также ее фрагменты.

Путем нескольких хитрых запросов в Google нам удалось обнаружить очень похожие на наш терминал устройства на сайтах международных дистрибьюторов.

Похожее устройство на сайте иностранного дистрибьютора

Там же мы обнаружили прошивку, но младшей версии.

Прошивка аналогичной серии

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

Текстовый файл с обновлением

Процесс довольно простой: текстовые шестнадцатеричные записи, указанные в переменных DataX, преобразуются в формат байтов, и на выходе получается прошивка.

Двоичный файл обновления

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

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

Архив с частичным обновлением

Файлы для частичного обновления

Изучив исполняемый файл standalonecomm, мы выяснили, что он отвечает за обработку запросов, приходящих на порт 4370/TCP. Также в этом исполняемом файле мы нашли функциональность, отвечающую за обновление прошивки. Обработчик обновления вызывает функцию для распаковки файла zkfp_ExtractPackage, которая является внешней и не представлена в исполняемом файле.

Код распаковки файла обновления

Внешняя функция распаковки образа обновления

Эту функцию мы не смогли найти и в других файлах обновлений, поэтому прибегли к поиску в интернете. Там мы сразу обнаружили репозиторий, в котором она присутствует в заголовочном файле.

Поиск функции распаковки

Функция распаковки в заголовочном файле

В этом же репозитории мы нашли библиотеку, в которой реализована эта функция.

Библиотека с функцией распаковки в репозитории

Поиск функции распаковки в библиотеке

Изучив функцию распаковки прошивки, мы выяснили, что она же и расшифровывает прошивку. На скриншоте ниже представлен код расшифровки.

Код расшифровки файла обновления

Для шифрования используется операция XOR по ключу, который формируется из последних 16 байт файла обновления и размера файла. Таким образом, мы получили все данные, необходимые, чтобы сформировать ключ и расшифровать прошивку.

После расшифровки файла оказалось, что это обновление не всей прошивки, а лишь некоторых исполняемых файлов, библиотек и файлов настроек.

Расшифрованный архив с обновлениями

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

Получение прошивки с флеш-памяти

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

Флеш-память на плате устройства

Это eMMC-флеш в корпусе BGA-153, под который в интернете можно найти колодку для программатора. После считывания памяти мы получили файл, содержащий различные разделы, как представлено на изображении ниже.

Структура флеш-памяти

В целом, названия разделов говорят сами за себя, но мы запустили утилиту binwalk (публично доступная утилита для анализа различных контейнеров с данными), чтобы удостовериться, что они верные. Результат ее работы представлен на скриншоте ниже.

Вывод утилиты binwalk на дампе флеш-памяти

Помимо всех исполняемых файлов и ядра Linux во флеш-памяти содержатся учетные данные пользователей (их в системе всего два).

Содержимое файла /etc/shadow

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

Вход по SSH с помощью учетных данных

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

Запущенные на устройстве исполняемые файлы

Основной сервис называется main. Он оперирует всем, что отображается на экране устройства, и общается с другими необходимыми ему сервисами через сервис hub. Последний представляет собой нечто вроде брокера сообщений, с помощью которого сервисы могут удобно взаимодействовать между собой. Также интерес представляет сервис pushcomm, так как это HTTP-клиент, который посылает запросы на сервер, прописанный в настройках устройства. Это значит, что существует возможность совершать атаки посредством клиента, если заставить устройство обратиться к подконтрольному атакующему веб-серверу. Какие атаки можно реализовать таким способом, мы покажем далее в статье. Стоит обратить внимание и на тот факт, что все сервисы запущены с максимальными привилегиями, что сильно упрощает полный захват устройства, так как любая уязвимость, приводящая к выполнению кода или команд, дает максимальные привилегии.

Исследование протокола на порту 4370/TCP

В качестве основной цели исследования мы выбрали сервис standalonecomm, так как он реализует проприетарный протокол производителя на порту 4370/TCP и содержит интересные с точки зрения атакующего команды, которые могут быть неверно имплементированы.

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

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

Авторизация по протоколу и ее проблемы

Из интересных вещей в протоколе есть авторизация пользователя, для которой необходимо знать пароль, устанавливаемый на устройстве. Для нашего устройства этот пароль называется COMKey и задается администратором. При этом по умолчанию он имеет значение 0, то есть не установлен, и все запросы могут быть выполнены без какой-либо авторизации.

Кроме того, пароль (COMKey) — это целое число от 0 до 999999, то есть существует достаточно ограниченное количество вариантов пароля, и их можно перебрать по сети. Это ограничение мы обнаружили при анализе кода установки пароля.

Код установки COMKey

Недостаточно безопасна и генерация так называемого MAC (Message Authentication Code) для прохождения авторизации по протоколу. Все дело в том, что при генерации используются обратимые операции. Таким образом, если мы можем прослушивать трафик в сети, то после успешной авторизации клиента сумеем восстановить пароль. Код генерации представлен на скриншоте ниже.

Код генерации MAC

Переменная SessionId представляет собой двухбайтовое значение, которое генерируется сервером и передается клиенту для того, чтобы он произвел расчет MAC на основе COMKey и отправил полученное значение на сервер.

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

Механизм авторизации по протоколу изображен ниже.

Механизм авторизации по протоколу

Клиент отправляет команду на подключение (CMD_CONNECT), после чего получает от сервера два байта, которые представляют собой SessionId и вместе с COMKey используются для расчета MAC. Значение MAC клиент отправляет с помощью команды CMD_AUTH, и сервер проверяет его корректность. Если все верно, то сервер отвечает командой CMD_ACK_OK и клиент может использовать все доступные команды на сервере в рамках текущей TCP-сессии.

Исследование обработчиков команд на наличие уязвимостей

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

Графическое представление обработчика команд протокола

Анализ этой функции не составляет большого труда — это лишь вопрос времени и внимательного чтения кода.

В качестве объекта анализа мы сразу же выделили команды, содержащие в своем названии слова DOWNLOAD, UPLOAD, DELETE, UPDATE.

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

Обработчик команды скачивания изображений

С помощью этой же команды мы можем получить файл /etc/shadow, так как сервис standalonecomm запущен с максимальными привилегиями.

Мы нашли и другие команды, в которых имя файла передавалось без каких-либо фильтров, что позволило выявить несколько уязвимостей чтения файлов. Также мы нашли функцию, позволяющую загружать файлы по произвольному пути. Учитывая уровень привилегий сервиса, ее можно использовать для получения неограниченного доступа к устройству.

При анализе команды CMD_DELETE_PICTURE мы обнаружили возможность внедрения команд оболочки, так как имя удаляемого изображения подставлялось напрямую в команду, которая передавалась в функцию system.

Обработчик команды удаления изображения

Мы создали PoC-скрипты для подтверждения эксплуатации этой уязвимости. Результат их работы представлен на скриншоте ниже.

Результат работы PoC

Также мы обнаружили несколько уязвимостей переполнения буфера, связанных с использованием небезопасных функций strcpy/sprintf, а также с отсутствием проверки корректности размера копируемого буфера в функции memcpy. Эту проблему мы разберем на примере обработчика команды CMD_CHECKUDISKUPDATEPACKPAGE.

Обработчик команды CMD_CHECKUDISKUPDATEPACKPAGE

Уязвимость заключается в том, что при копировании данных из пользовательского сетевого пакета берется размер, указанный пользователем. Буфер, куда происходит копирование, находится на стеке и имеет размер 1028 байт. Если пользователь укажет в пакете большой размер данных, буфер переполнится. При этом защиты от переполнения стека в исполняемом файле нет. Эту уязвимость злоумышленники могут использовать для последующего вызова ROP-цепочки и выполнения произвольного кода, приводящего к получению удаленного доступа к устройству.

Наконец, мы обнаружили SQL-инъекции практически везде, где строковое значение, передаваемое пользователем в сетевом пакете, подставлялось напрямую в запрос к базе данных.

Исследование сервиса pushcomm

Сервис pushcomm, как мы уже упоминали, отправляет запросы на прописанный в настройках устройства сервер. Настраивает его администратор в меню «Связь» в разделе «Настройки облачного сервиса». Для этого он задает IP-адрес подключения и порт, а также при необходимости включает другие опции. Меню и раздел настроек изображены ниже.

Меню «Связь»

Раздел настроек облачного сервиса

При анализе исполняемого файла мы выяснили, что он подвержен тем же проблемам, что и сервис standalonecomm. Однако для эксплуатации этих уязвимостей необходимо поднять свой веб-сервер и заставить устройство к нему обратиться. Это можно сделать различными способами: поменять настройки в базе данных или в меню администратора, или же осуществить ARP-спуфинг.

Стоит отметить, что одна из команд pushcomm называется SHELL и отвечает за выполнение любых команд на устройстве.

Обработчик команды SHELL

Для выполнения этой команды достаточно поднять свой веб-сервер и реализовать обработчик, как на примере ниже.

Пример обработчика для вызова команды SHELL

В целом код сервиса pushcomm сильно пересекается с кодом сервиса standalonecomm, особенно в разделе запросов к базе данных.

Анализ обработчика QR-кода

Мы уже упоминали, что демонстрация QR-кода с SQL-инъекцией приводит к ее выполнению и позволяет пройти аутентификацию от имени другого пользователя. Однако при анализе кода мы обнаружили, что размер данных, которые могут содержаться в QR-коде, ограничен 20 байтами. Это мешает выполнять сложные инъекции с использованием ключевого слова UNION и SELECT-запросов для получения произвольных данных из различных полей базы. Запрос к базе устройства, который формируется при чтении QR-кода (в данном случае — нашего кода с инъекцией) представлен на скриншоте ниже.

Запрос к базе при использовании QR-кода

Также мы заметили, что при демонстрации QR-кода с большим количеством данных происходит перезагрузка устройства. При изучении кода выяснилось, что это связано с тем, что код, ожидающий данные от камеры, не смог получить их за заданный промежуток времени (2 секунды)посчитал, что с устройством происходит что-то не то, и перезагрузил его, используя команду reboot.

Код ожидания данных от камеры

Код ожидания данных от камеры

Заключение

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

В ходе исследования биометрического терминала производства ZkTeco мы обнаружили 24 уязвимости. Многие из них оказались похожи друг на друга, потому что возникали из-за ошибки в одном месте внутри библиотеки, служащей «оберткой» для базы данных. Мы обобщили эти уязвимости как множественные, указав тип и причину возникновения, поэтому конкретных CVE в итоге вышло меньше.

Сухая статистика выглядит следующим образом:

6 SQL-инъекций;
7 переполнений буфера на стеке;
5 инъекций команд;
4 записи произвольных файлов;
2 чтения произвольных файлов.

С описанием выявленных уязвимостей можно ознакомиться в GitHub-репозитории команды «Лаборатории Касперского», проводившей исследование.

​  

​Securelist

Read More

Ваша реакция?
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x