Write-up CTFZone Quals 2019: Chicken

    Несмотря на перенос конференции OFFZONE 2020, финалу соревнований CTFZone быть! В этом году он впервые пройдет в онлайн-режиме и будет активно транслироваться в социальных сетях.

    О подробностях мы объявим позже, а пока предлагаем изучить райтап веб-таска с отборочного этапа. Разбор решения нам прислал Devand MacLean из Канады. Специально для «Хабра» мы перевели текст райтапа и приглашаем узнать, с какой цепочкой уязвимостей столкнулись участники и при чем здесь курица.



    Общая информация


    Автор таска: Павел Сорокин
    Баллы: 470
    Количество команд, решивших таск: 2

    Полезные ссылки:



    Описание таска


    На первый взгляд, веб-приложение содержало:


    • домашнюю страницу (/Home/Index);
    • страницу про куриц (/Home/Hens);
    • страницу с контактами (/Home/Contact);
    • страницу для входа (/Auth/Login).

    Позднее, в процессе анализа, также были обнаружены:


    • страница для изменения пароля пользователя;
    • второй интерфейс с API для входа, восстановления и изменения пароля.

    Решение таска


    На странице /Home/Hens содержатся ссылки на загрузку «паспортов» куриц.


    После декодирования Base64 находим параметр filename: 1.txt. С помощью CyberChef кодируем /etc/passwd в Base64 и переходим по ссылке:

    http://web-chicken.ctfz.one/File/Download?filename=L2V0Yy9wYXNzd2Q=

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

    При попытке отыскать другие файлы в системе можно заметить ошибку, которая появляется каждый раз при запросе несуществующего файла (или файла, который нельзя прочитать с правами пользователя приложения):


    Увидев, что ошибка ссылается на переменную среды ASPNETCORE_ENVIRONMENT, мы начинаем изучать макеты веб-приложений ASP.NET Core. И видим следующее:


    Чтобы преобразовать нужные пути и имена файлов и извлечь их в Base64, пишем небольшой скрипт на Python (fetch.py):


    Используя эти знания о структуре веб-приложения MVC, созданного в ASP.NET Core, с помощью fetch.py получаем исходный код следующих файлов:


    • ../Views/Shared/_Layout.cshtml
    • ../Views/Home/Index.cshtml
    • ../Views/Home/Contact.cshtml
    • ../Views/Home/Hens.cshtml
    • ../Views/Auth/Login.cshtml

    Сделав это и изучив исходный код каждой страницы, мы находим интересную деталь в Hens.cshtml


    В строке 1 есть оператор включения @ using StackHenneryMVCAppProject, который в ASP.NET означает ссылку на DLL-файл.

    Открываем ../StackHenneryMVCAppProject.dll — но не через утилиту Python в Kali Linux, а в браузере на Windows, потому что собираемся декомпилировать этот файл в Windows. Используем для этого такой URL:

    http://web-chicken.ctfz.one/File/Download?filename=Li4vU3RhY2tIZW5uZXJ5TVZDQXBwUHJvamVjdC5kbGw=

    Открыв DLL-файл в dnSpy (декомпиляторе .NET), сразу видим, что в методе Initialize() класса Config.Configuration, который выполняется при запуске веб-приложения, считывается содержимое файла chicken_domains_internal.txt. С помощью скрипта Python извлекаем содержимое этого файла:

    web-chicken-flag
    web-chicken-auth

    Метод Initialize() подключается к web-chicken-flag через порт 4321 и получает несколько параметров: secret_token, RSA_key и flag.

    К сожалению, инициировать подключение к web-chicken-flag не получается. Но, когда мы задаем локальную DNS-запись hosts, чтобы перевести web-chicken-auth на внешний IP-адрес сервера (34.89.232.240), нам удается открыть http://web-chicken-auth/ с интерфейсом Swagger UI, который используется для описания и выполнения команд через REST API.


    Еще покопавшись в декомпилированном DLL-файле, можно заметить, что у контроллера AuthController был не только метод Login(), но и метод Change_Password_Test():


    При переходе к /Auth/Change_Password_Test мы видим такую форму:


    При изучении метода Change_Password_Test() мы понимаем, что он обрабатывает четыре параметра типа String через запрос HTTP POST:


    • username;
    • new_password;
    • old_password;
    • base_url.


    Чуть ниже в методе Change_Password_Test() видим: не получив значения для base_url, метод берет значение conf.auth_server, то есть web-chicken-auth, а затем добавляет /auth/login в конец значения base_url, назначая получившуюся строку переменной requestUri:


    В следующей важной части кода строковые значения параметров username и old_password из запроса HTTP POST, а с ними и secret_token из конфигурации вставляются в строку JSON. Затем из этих данных создается объект JSON StringContent, который отправляется как данные HTTP POST в переменную requestURI, созданную на предыдущем шаге.


    Теперь сервер ждет, чтобы запрос HTTP POST к requestUri вернул объект JSON с параметром code, равным 0. Если этого не происходит, мы попадаем в ветвь кода, которая начинается со строки 6 и возвращает представление /Views/Auth/Login с ошибкой Invalid login/password.

    Если запрос HTTP POST все-таки возвращает объект JSON с параметром code, равным 0, мы переходим в ветку кода со строки 11. Тогда переменная requestUri2 получает значение http://web-chicken-auth, создается еще один объект JSON StringContent с параметрами из исходного запроса HTTP POST, как в предыдущей части, и эти данные отправляются на адрес requestUri2 в запросе HTTP PUT. Наконец, клиенту возвращается сообщение Password changed.


    В двух словах: весь процесс начинается с запроса HTTP POST к /Auth/Change_Password_Test, который ожидает определенное число параметров. Он берет эти значения и создает объект JSON, чтобы отправить его в <base_url>/auth/login через HTTP POST. Если в ответ он получает JSON с параметром code, равным 0, то создает еще один объект JSON и отправляет запрос HTTP PUT на http://web-chicken-auth/auth/change_password.

    При изучении REST API в Swagger UI мы видим, что в декомпилированном DLL-файле были не только маршруты /auth/login и /auth/change_password, но и маршрут для /auth/password_recovery.


    Этот API-маршрут ожидал объект JSON с двумя параметрами: email и secret_token. Если он его получал, то возвращал объект JSON со свойством code, равным 0, и строками message и token.


    Получается, что если мы как-то заставим метод Check_Password_Test() сразу запросить маршрут /auth/password_recovery вместо /auth/login, то для входа в ветку смены пароля достаточно будет ввести действительный электронный адрес.

    Мы снова изучаем часть кода, в которой метод Check_Password_Test() присваивает значение requestUri в исходном запросе HTTP POST, и понимаем, что нужно отправить значение http://web-chicken-auth/auth/password_recovery# как параметр base_url, и requestUri в конечном итоге получит значение http://web-chicken-auth/auth/password_recovery#/auth/login.

    Символ # в HTTP URL указывает, что часть адреса с запросом закончилась. Поэтому сервер, который получит запрос, просто проигнорирует часть /auth/login.


    Это, конечно, хорошо, но нам осталось придумать, как REST endpoint, куда мы теперь перенаправляем запрос, получит значение email. Без этого значения запрос завершится ошибкой, и дальше мы не продвинемся.

    Внимательно приглядываемся к тому, как метод Check_Password_Test() создает данные JSON, и видим, что входные данные совсем никак не очищаются, а значит, можно запросто внедрять свои параметры. Достаточно отправить следующие параметры в запросе HTTP POST:

    username = admin
    old_password = pwned","email":"admin@chicken.ctf.zone","lol":"
    new_password = p0tat0
    base_url = http://web-chicken-auth/auth/password_recovery#

    Созданный в результате объект JSON отправляется в /auth/password_recovery:


    С этими данными JSON мы выполнили требования, по которым /auth/password_recovery должен вернуть ответ, после чего сервер войдет в ветку изменения пароля (объект JSON содержит свойство email). Теперь значение old_password нам уже не нужно. В этой ветке кода мы предоставили все значения, необходимые для изменения пароля (свойство username имеет значение admin, а свойство passwordp0tat0). Запрос выполнен, и мы получаем Password changed в ответ.


    Осталось только ввести новое имя пользователя и пароль на странице входа!


    И вот наш флаг:

    BI.ZONE
    Компания

    Похожие публикации

    Комментарии 0

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

    Самое читаемое