Загрузка данных
Рассмотрим первый вывод nmap:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu
80/tcp open http nginx/1.18.0
6379/tcp open redis Redis 7.0.11
8080/tcp open http-proxy Adminer 4.6.1
9000/tcp open fastcgi PHP-FPM 8.1.2
Порт 22 — SSH. Это стандартный способ удалённого управления Linux-сервером. Сам по себе проблемой не является, но странно, что он смотрит в интернет напрямую.
Порт 80 – веб-сервер, на котором располагается сайт
На порте 6379 видно, что светится сервис Redis. Это версия популярного хранилища данных типа «ключ-значение» с открытым исходным кодом. Это NoSQL система. Также подозрительно, что она доступна напрямую из сети.
Порт 8080 — Adminer. Это веб-интерфейс для управления базами данных, как phpMyAdmin. Тоже смотрит в интернет. Это административный инструмент, который не должен смотреть в интернет.
Порт 9000 — PHP-FPM. Внутренний компонент, который обрабатывает PHP-код. Nginx общается с ним внутри сервера через FastCGI-протокол. Он также не должен быть виден снаружи.
Следующий запрос отражает сразу же взаимодействие с Redis:
Взаимодействие с Redis (порт 6379):
$ redis-cli -h 2.12.85.6
2.12.85.6:6379> PING
PONG
Происходит подключение к redis-серверу, в ответе на PING получаем PONG. Значит, соединение установлено. Значит, подключение никак не защищено
2.12.85.6:6379> INFO server
# Server
redis_version:7.0.11
os:Linux 5.15.0-91-generic x86_64
...
В данном запросе запрашивается информация о сервере, на котором располагается Redis
Далее получаем все ключи в базе и видим сессию администратора сайта, а также видим конфигурацию базы данных. Содержимое этих ключей пока что не читается, только названия, но уже понятно, что там предположительно может лежать.
2.12.85.6:6379> KEYS *
1) "session:admin:9f3a2b"
2) "config:database"
3) "cache:page:main"
При обращении к http://2.12.85.6:8080/ открывается интерфейс Adminer 4.6.1
При открытие браузера видно форму входа в инструмент управления базами данных. Сам факт доступности этой страницы из интернета - проблема.
$ cgi-fcgi -bind -connect 2.12.85.6:9000
Connection refused? No - connection established
cgi-fcgi – это инструмент для общения по FastCGI-протоколу. Произошло успешное подключение к порту 9000 напрямую. PHP-FPM принял соединение.
$ python3 fcgi_exploit.py 2.12.85.6 9000 /var/www/html/index.php
X-Powered-By: PHP/8.1.2
Content-type: text/html; charset=UTF-8
<!DOCTYPE html>
<html><head><title>TargetCorp - Corporate Portal</title></head>
Скрипт отправил PHP-FPM запрос «выполни файл /var/www/html/index.php» — и PHP-FPM его выполнил и вернул результат. nginx обходится полность и доступно взаимодействие с PHP-движком напрямую.
$ curl -I http://2.12.85.6/
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
X-Powered-By: PHP/8.1.2
Set-Cookie: PHPSESSID=abc123; path=/; HttpOnly
curl -I запрашивает только заголовки ответа без тела страницы. Видим стандартный ответ. X-Powered-By: PHP/8.1.2 сервер раскрывает версию PHP
$ curl http://2.12.85.6/robots.txt
User-agent: *
Disallow: /admin/
Disallow: /backup/
robots.txt — файл для поисковых роботов, говорит им, какие страницы не индексировать. Но это также подсказка, где искать интересное. Видим два запрещённых пути: /admin/ и /backup/.
$ curl http://2.12.85.6/backup/
<title>Index of /backup/</title>
<a href="db_dump_2024-01-15.sql.gz">db_dump_2024-01-15.sql.gz</a> 2.4M
<a href="site_config.tar.gz">site_config.tar.gz</a> 156K
Перешли по адресу из robots.txt — и увидели открытый каталог с файлами. Пароль не запрашивается.
db_dump_2024-01-15.sql.gz — это резервная копия всей базы данных.
site_config.tar.gz — архив с конфигурационными файлами сайта. Оба файла доступны для скачивания любому желающему. О_о
Уязвимости:
1. Так как redis нам доступен и уже выдал названия ключей, их можно было бы прочитать и потенциально получить пароль к базе данных:
redis-cli -h 2.12.85.6 GET config:database
Из Redis можно получить credentials к MySQL, украсть сессию администратора сайта, а при определённой конфигурации сервера — вообще записать свой SSH-ключ и войти на сервер.
2. Папка /backup/ с резервными копиями открыта и доступна любому желающему.
db_dump_2024-01-15.sql.gz — это снимок всей базы данных на январь 2024. Внутри — все таблицы, все данные пользователей, все пароли (пусть и в виде хешей). site_config.tar.gz — конфиги приложения, а в них почти наверняка логины и пароли к базе данных, секретные ключи. А это по сути полный срез данных системы без какой-либо авторизации.
3. PHP-FPM открыт в интернет
Нормальная работа сайта организована так: клиент отправляет запрос на сервер (nginx), далее nginx передает запрос к PHP-FPM, а он уже выполняет код и отдает клиенту. PHPFPM – это внутренний компонент, он не должен быть доступен из интернета. Но в данном случает доступен и на нем даже можно выполнять произвольный php-код (видели, как он выполнил fcgi_exploit.py и отдал ответ).
То есть, потенциально это уязвимость Remote Code Execution. Получив возможность выполнять команды на сервере, можно читать любые файлы, скачивать данные, и в конечном счёте получить полноценный удалённый доступ к серверу.
4. Adminer — это веб-интерфейс для управления базой данных. Его открытость в интернет сама по себе плохая идея. Это административный инструмент. Версия 4.6.1 при этом устаревшая.
Версия Adminer 4.6.1 (и более ранние, до 4.6.3) подвержена серьезным уязвимостям, связанным с подключением к внешним серверам и чтением произвольных файлов. Основная проблема заключается в том, что атакующий может использовать уязвимый экземпляр adminer.php для чтения содержимого файлов на сервере, где он установлен.
То есть, даже без учета кредов из Redis, можно было бы попытаться поднять у себя фиктивный MySQL-сервер, указать его адрес в форме входа Adminer, а он бы подключился к нему и в процессе подключения отправил бы файлы с целевого сервера.
Но даже без этой технической уязвимости — если мы получили credentials из Redis или из backup-архива, мы просто вводим их в форму входа и получаем полный доступ к базе данных через удобный графический интерфейс.
Шаги атаки: скачиваем site_config.tar.gz из /backup/, находим в конфигах логин/пароль к MySQL, входим в Adminer с этими credentials, видим всю базу данных, забираем данные пользователей, либо через Redis читаем config:database, через PHP-FPM выполняем команды на сервере, получаем полный доступ к серверу.