Три стороны систем ИБ и неочевидные риски
При внедрении и сопровождении систем информационной безопасности, часто участвуют три стороны: вендор (разработчик), интегратор (технический подрядчик) и заказчик (организация-клиент). Вендор отвечает за разработку, обновления и вторую линию поддержки. Интегратор выполняет внедрение, настройку, доработки и оказывает первую линию поддержки. Заказчик работает с системой на практике, формирует требования и может передавать логи диагностики через интегратора в вендорскую поддержку. При этом ответственность за обезличивание или фильтрацию чувствительных данных в логах часто лежит на стороне заказчика. На приктике заказчик не всегда проверяет, а какие на самом деле данные в логах выгружаются.
Что может скрываться внутри диагностических архивов
Недавно, работая с системой MaxPatrol VM и формируя диагностический архив для обращения в техническую поддержку, я заметил неприятную особенность: информация, которая выгружается для анализа, содержит пароли в открытом виде. Пароли от учетных записей компонентов, которые являются частью системы. Эти компоненты изолированы в Docker-контейнерах, но доступ к ним может осуществляться из сети через открытые порты. Среди компонентов — PgAdmin для доступа к СУБД с высокой привелегией, Grafana, RabbitMQ.
Сами логи представляют собой набор текстовых файлов с конфигурациями и событиями от различных компонентов системы. Кроме того, при сборе диагностической информации происходит выгрузка переменных окружения из операционной системы Linux, где в открытом виде указаны пароли, используемые для взаимодействия между компонентами. Среди прочего, в данных могут попадаться пароли сервисных учетных записей, например, для подключения к SMTP или Proxy-серверу, которые относятся к инфраструктуре.
Для того что бы проверить наличие паролей в логах диагноситческого отчета можно зайти в каталог с логами и выполнить команду:
grep -irnE 'var' .
где var — одно из следующих значений переменной:
Password=
Password:
Какие могут быть риски при передаче паролей
Незначительная утечка такой информации может стать началом серьезных проблем. Например, администратор может использовать один и тот же пароль для разных систем, что приведёт к компрометации других систем. Наличие паролей в открытом виде существенно упрощает сценарии повышения привилегий через MaxPatrol VM.
Сама по себе система MaxPatrol VM — это актив повышенного риска. С серверов системы осуществляются подключения в различные сегменты сети, а для выполнения сканирований используются учётные записи с повышенными правами. Компрометация такой платформы потенциально означает доступ ко всей инфраструктуре.
Про здравый подход к безопасности
Иногда достаточно поработать с разными системами вживую, чтобы понять простую вещь: без доступа к логам действительно сложно выявить суть проблемы. Эксперты в области ИБ, работавшие с иностранными решениями, как правило, знают это на собственном опыте и именно поэтому рассматривают логи как ключевой источник информации для диагностики.
Но с опытом приходит и другое понимание: в логах не должно быть того, что само по себе может стать уязвимостью. Открытые пароли, переменные окружения с ключами, технические учётки — всё это, оказавшись в архиве, а затем в чужих руках, превращается в реальный риск.
И это уже не вопрос “хорошо или плохо”. Это вопрос зрелого подхода к безопасности. Того самого, который формируется не в теории, а в практике там, где последствия всегда реальны.
В целом хранение и передача паролей в открытом виде противоречит базовым принципам безопасности и международным security best practices, на которых, в том числе, основаны и Российские нормативные требования в сфере ИБ. Ниже приведу некоторые ключевые источники, прямо указывающие на недопустимость хранения паролей в логах или конфигурационных файлах в открытом виде:
-
CWE-256: Plaintext Storage of a Password Зарегистрированная в списке MITRE Common Weakness Enumeration. Прямо указывает, что хранение паролей в незашифрованном виде — это нарушение архитектурной безопасности приложения.
-
OWASP: Password Plaintext Storage OWASP, где подчёркивается, что даже временное хранение пароля в логах может привести к его утечке.
-
OWASP Password Storage Cheat Sheet Практическое руководство по корректному обращению с паролями, включая рекомендации по хэшированию, защите в памяти и минимизации риска экспонирования.
Что делать в таком случае
В итоге я решил сам написать скрипт на Python, который рекурсивно обходит, выгруженный каталог с логами, находит строки с паролями и автоматически затирает их. На мой взгляд, это самое быстрое решение для очистки логов.
Исходный код скрипта выложил на GitHub: https://github.com/ErSilh0x/maxpatrolvm
#!/usr/bin/python3 import os import re import argparse #python3 sanitmpvmlogs.py --logs ./troubleshoot_dir #Arguments parser = argparse.ArgumentParser(description='Удаление паролей в логах диагностики Maxpatrol VM. Распакуйте архив' ' с логами, полученными от утилиты сбора логов в каталог' ' и задайте каталог скрипту.\n' 'Delete passwords in Maxpatrol VM diagnostic logs.') parser.add_argument('-l', '--logs', required=True, help='Enter directory with logs') args = parser.parse_args() catalog = args.logs pass_patterns = [r'(PgPassword=)\S*', r'(PostgrePassword=)\S*' r'(ConsulSecret=)\S*', r'(EventStorageAuthPassword=)\S*', r'(MetricsPassword=)\S*', r'(RMQAgentPassword=)\S*', r'(RMQPassword=)\S*', r'(RMQSiemPassword=)\S*', r'(RMQAdminPassword=)\S*', r'(RMQSiemPassword=)\S*', r'(SmtpPassword=)\S*', r'(ClickHouseUserPassword=)\S*', r'(GrafanaAdminPassword=)\S*', r'(TelemetryInstanceAccessToken=)\S*', r'(ProxyPassword=)\S*', r'(--httpAuth.password=)\S*', r'(METRICS_PASSWORD=)\S*', r'(GF_SECURITY_ADMIN_PASSWORD=)\S*', r'(LogManager_ClickhousePassword=)\S*', r'(VictoriaMetrics_Password=)\S*', r'(VictoriaMetricsSettings_BasicAuthPassword=)\S*', r'(Database_VictoriaMetricsPassword=)\S*', r'(FRONTEND__LOGSPACE__SECURITY__PASSWORD=)\S*', r'(FRONTEND__CLICKHOUSE__SECURITY__PASSWORD=)\S*', r'(FRONTEND__ELASTICSEARCH__SECURITY__PASSWORD=)\S*', r'(RABBITMQ_ADMIN_PASS=)\S*', r'(RABBITMQ_SIEM_PASS=)\S*', r'(COLLECTOR_METRICS_PASSWORD=)\S*', r'(RABBITMQ_AGENT_PASS=)\S*', r'(PGADMIN_DEFAULT_PASSWORD=)\S*', r'(COLLECTOR_POSTGRESQL_PASSWORD=)\S*', r'(POSTGRES_PASSWORD=)\S*', r'(CONTENT_POSTGRES_PASSWORD=)\S*', r'(CONSUL_SECRET=)\S*', r'(HMRabbitSettings_Password=)\S*', r'(CONTENT_HM_RMQ_AUTH_PLAIN_PASSWORD=)\S*', r'(RabbitMq_Password=)\S*', r'(CSPF_HM_RMQ_AUTH_PLAIN_PASSWORD=)\S*', r'(RABBITMQ_CORE_PASS=)\S*' r'(FlusProxy_ProxyPassword=)\S*', r'(Password=)\S*', r'(Password:) \S*', r'(Password\":) \S*' ] replace = '' stats = {} #Count statistics def count_stat(p): if p in stats: stats[p] += 1 else: stats[p] = 1 def clean_log(f): with open(f, 'r', encoding='utf-8', errors='ignore') as fr: lines = fr.readlines() with open(f, 'w', encoding='utf-8', newline='\n') as fw: for line in lines: for pattern in pass_patterns: #Search if re.search(pattern, line): replace = re.search(pattern, line) line = re.sub(pattern, replace.group(1), line, flags=re.IGNORECASE) count_stat(replace.group(1).rstrip('=')) print(line) #print(replace) fw.write(line) for current_dir, subdirs, files in os.walk(catalog): #Current Iteration Directory #print('Current Directory: ', current_dir) #Directories '''for dirname in subdirs: print('\tSub Directory: ' + dirname)''' #Files for filename in files: fullpath = os.path.join(current_dir, filename) print('Parsing: ', fullpath) clean_log(fullpath) #Statistics for i in stats: print('Strings found: ', i, stats[i])
ссылка на оригинал статьи https://habr.com/ru/articles/901894/
Добавить комментарий