HTB Timing. Пентестим веб-сервер на PHP
the Matrix
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
Добавляем IP-адрес машины в /etc/hosts:
И запускаем сканирование портов.
Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта.
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).

Нашли два открытых порта:
На SSH нам ловить нечего, пропускаем его.
Поскольку вначале у нас нет учетных данных, нет и смысла изучать службы, которые всегда требуют авторизации (например, SSH). Единственное, что мы можем делать здесь, — это перебирать пароли брутфорсом, но машины с HTB почти всегда можно пройти по‑другому. В жизни таких вариантов может не быть, к тому же есть шансы подобрать пароль или получить его при помощи социальной инженерии.
Посмотрим, что нам покажет веб‑сервер. При обращении к нему происходит редирект на страницу /login.php, где нас встречает форма авторизации.

Понятно, что сайт не одностраничный, поэтому попробуем найти скрытые страницы. Так как первая страница была в формате PHP, остальные будем перебирать с таким же расширением. Я для этого воспользуюсь сканером ffuf.
Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде dirsearch и DIRB.
Я предпочитаю легкий и очень быстрый ffuf. При запуске указываем следующие параметры:
Запускаем его с нужными параметрами:

Нашли много файлов, теперь просканируем и скрытые каталоги.

В итоге находим каталоги для хранения скриптов и изображений. Больше нам ничего не доступно. Сканирование файлов бэкапов и поддоменов ничего не дало. Но мы еще не поискали параметры! Для сканирования логично выбрать страницу image.php, которая предположительно должна возвращать изображения. Так как мы не знаем, что будет передано в качестве значения параметра, попробуем передать само название страницы в надежде получить какую‑нибудь ошибку.

Мы нашли один параметр — img. То есть мы можем запросить файл с картинкой по его названию. Попробуем таким способом утащить какой‑нибудь системный файл, задав относительный путь.

Нас поймали за руку!
Здесь, судя по всему, используются какие‑то фильтры, которые мешают нам читать любой файл. Я попробовал разные варианты оберток для параметра и обнаружил, что срабатывает запрос вот такого вида:
Local file inclusion (LFI) — техника, которая используется для получения доступа к файлам в системе через веб‑сервер. Чтобы сервер отобразил файл, а не попытался его выполнить, ему нужно передать «обертку» — команды, которые закодируют файл. После его получения останется лишь раскодировать его обратно. Существует множество готовых оберток, которые ты можешь применять при пентесте.
Содержимое этой страницы, полученной в ответ, будет закодировано в Base64. Декодировать можно прямо Burp, нажав Ctrl-Shift-B.

Так как мы можем получить код любой страницы, идем по порядку и заглядываем в исходный код страницы login.php.

В нем ничего интересного нет, кроме подключения файла db_conn.php (строка 10). Здесь мы находим учетку для подключения к базе данных. Конечно же, просмотрим и его.

Пароль пока ни к чему не подошел, поэтому копаем дальше. Перейдем к файлам, о которых мы уже знаем. Начнем с upload.php.

В самом начале подключается файл admin_auth_check.php. Затем задаются необходимые параметры для загруженного файла, в том числе и file_name. Имя файла создается по следующему алгоритму: берется строка '$file_hash', затем добавляется текущее время (результат выполнения функции time()), все это конвертируется в хеш MD5, а дальше добавляется знак нижнего подчеркивания и имя файла, которое использовалось при загрузке. При этом файл должен иметь расширение jpg. А в файле admin_auth_check.php только сравнивается роль пользователя.
Если бы $file_hash кто‑то по ошибке не обернул в кавычки, то подставилось бы значение переменной, полученное от PHP-функции uniqid(). Разгадать уникальный идентификатор у нас бы не вышло, а без него единственной преградой будет вывод функции time().

Впрочем, пока что здесь больше ничего не сделать, и придется брутить форму авторизации. Для начала найдем имена пользователей, к примеру в файле /etc/passwd.

Но при подборе пароля сразу попробуем использовать имя пользователя в качестве пароля, и это дает нам доступ.

Нам открывается новая функция — изменение профиля.

При отправке данных формы просмотрим, куда они шлются, а потом получим код этой страницы, чтобы просмотреть, как обрабатываются данные.


Видим, что в обработчике на один параметр больше, чем отправляется через форму. Значит, мы можем добавить role и задать любую роль. Это должно дать нам привилегированный контекст.


В итоге мы получаем ответ со всеми перечисленными параметрами, а на самой странице появляется ссылка на панель администратора.

С панели администратора нам открывается функция загрузки аватара.

Мы знаем, что это должен быть файл с расширением jpg. Но вот только его содержимое не проверяется, поэтому мы можем записать туда код на PHP и затем попробовать выполнить. Запишем следующий простой PHP-шелл, выполняющий принятую команду, и загрузим файл на сервер.

Файл успешно загружен, теперь определимся с именем, под которым он сохранился на сервере. Для этого вспомним принцип, по которому он формируется.

Единственная переменная, которую нам осталось получить, — значение функции time(). Эта функция возвращает количество секунд, прошедших с 00:00:00 01.01.1970. И если мы знаем время загрузки файла, установленное на сервере, мы сможем получить это значение. Так как вся работа выполняется через Burp, в истории запросов найдем HTTP-заголовок ответа сервера. В заголовке Date будет указано нужное нам время.

Теперь мы можем получить значение MD5 от сложения всех строк:

Нам нужно обращаться к файлу вот с таким именем:
Попробуем с помощью нашего хеша выполнить базовую команду id.

Команда успешно выполнена, что означает получение доступа к хосту.
Для дальнейшего продвижения нам необходимо найти учетные данные. Для этого предстоит изучить файлы в каталоге веб‑сервера, всевозможные резервные копии и другие пользовательские файлы. Так находим бэкапы исходников в каталоге /opt.

Копируем архив в каталог веб‑сервера и легко скачиваем через браузер.

В архиве присутствует каталог с расширением git. Это позволит нам получить доступ к истории изменений файлов.

Для удобной работы с репозиториями Git можно использовать графический просмотрщик gitk. Он помог найти изменение пароля для подключения к базе данных.

А вот уже с этим паролем удается подключиться по SSH и забрать первый флаг.

Первым делом проверяем наиболее вероятные места повышения привилегий: настройки sudoers, приложения с выставленным битом SUID, прослушиваемые на локалхосте порты. Проверим sudoers командой sudo -l.

В настройках прописан привилегированный запуск /usr/bin/netutils без ввода пароля (NOPASSWD). Просмотрим тип файла.

Это обычный скрипт. Давай глянем на его содержимое.

Это пользовательское приложение на Java. Но так как оно расположено в каталоге рута, мы не можем получить доступ к самому файлу и декомпилировать его, чтобы изучить. Тогда давай хотя бы запустим программу и посмотрим, как она работает.

Нам предлагают выбрать одну из опций: FTP или HTTP. После чего с указанного ресурса загружается файл.

Посмотрим, что происходит с процессами в системе при выполнении этого приложения. Для отслеживания процессов будем использовать pspy64. Загрузим его на хост:
И выполним. В выводе видим запуск скрипта, но, что более интересно, после указания адреса для загрузки используется программа axel в контексте пользователя с UID=0, а это пользователь root.

Я сразу попробовал выполнить инъекцию команды ОС, но это ничего не дало.

Однако мы можем конфигурировать axel и управлять некоторыми его параметрами, к примеру именем файла и каталогом для его сохранения. Недолго размышляя, пробуем сохранить файл как публичный SSH-ключ пользователя root. Для этого в домашнем каталоге текущего пользователя сохраним файл .axelrc со следующим содержимым:

На локальном хосте сгенерируем пару ключей (ssh-keygen) и переименуем публичный, чтобы он назывался index.. Запустим простой веб‑сервер на Python:
А затем обратимся к нему из тестируемого приложения.

Видим сообщение с именем сохраненного файла, пробуем подключиться с закрытым ключом и получаем контроль над хостом.

Машина захвачена, и у нас есть флаг рута.
Источник
Наши проекты:
- Кибер новости: the Matrix
- Хакинг: /me Hacker
- Кодинг: Minor Code