Capture the Flag: разбор взлома сервера на примере уязвимости SSRF

CTF (capture the flag или захват флага) — это командные соревнования по информационной безопасности и системному администрированию, проходящие в двух форматах.

В формате task-based (или jeopardy) игрокам предоставляется набор тасков (заданий), к которым требуется найти ответ и отправить его. За верно решённый таск команда получает определённое количество очков.

Другой формат CTF-соревнования — classic (либо attack-defense). Каждая команда получает выделенный сервер или небольшую сеть для поддержания её функционирования и защиты. Во время игры команды зарабатывают очки за корректную работу сервисов своего сервера и за украденную информацию (она же — «флаги») с серверов соперников.

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

В этой статье мы обсудим следующий таск:

Инженер сайта acme.org запустил новый сервер для панели администратора по адресу http://104.236.20.43/. Он абсолютно уверен, что сервер невозможно взломать, кроме того, когда считывается файл с флагом, ему приходит уведомление. Он отметил, что используется стандартная страница Apache, но она была оставлена намеренно и не доставит проблем.

Ваша цель? Прочитайте флаг!

Анализ веб-сервера

Прим. перев. Идёт разбор таска с h1-212 CTF 2017 года, поэтому ссылки уже не работают. Но если есть желание, можете развернуть сервер самостоятельно, взяв исходники отсюда.

Если взглянуть на веб-сервер, то можно заметить, что на адресе 104.236.20.43 крутится стартовая страница Apache. Исходя из этого мы точно знаем, что используется Apache и что флаг может находиться в одной из его директорий. Тогда самым логичным шагом будет проверить адрес 104.236.20.43/flag. Там лежит фальшивый флаг, который может пригодиться позже, так как разработчики тасков иногда оставляют своего рода подсказки, необходимые для дальнейшего решения.

Затем мы проверяем сайт acme.org, но это ни к чему не приводит.

Стоит упомянуть принцип работы виртуального хостинга: он проверяет значение заголовка host в HTTP-запросе, т. е. сопоставляется имя хоста с соответствующим сервером. При этом имя хоста у нас уже есть — acme.org.

Следующим шагом нужно найти панель администратора. В первую очередь стоит попробовать отослать запрос с именем хоста admin.acme.org, но сделав это, мы получаем пустую страницу. Альтернативный способ решения проблемы — отредактировать свой /etc/hosts, добавив в него 104.236.20.43 admin.acme.org. Таким образом, когда мы будем пытаться посетить admin.acme.org, сервер перенаправит нас на правильную страницу.

Исследование ошибочных конфигураций и их исправление

Верно настроив виртуальный хостинг и посетив нужный нам сайт, мы обнаружили пустую страницу и получили куки admin=no.

Самым очевидным следующим шагом будет подмена параметра admin у куки на yes, но в ответ мы получаем HTTP 405, т. е. метод не разрешён. Значит, нам нужно найти верный HTTP-глагол и послать его серверу.

HTTP-глагол или метод HTTP используется для указания действия, которое нужно совершить веб-серверу. При посещении сайта мы в первую очередь отправили самый распространённый запрос GET. Чтобы изменить запрос или используемый метод, можно использовать либо Burp Suite Repeater, либо cURL. В первую очередь попробуем самый очевидный запрос — POST.

 

В ответ на запрос придёт другая ошибка — HTTP 406 Not Acceptable («неприемлемо»). Это означает, что тип контента, который мы отослали, не принимается сервером. В Burp мы можем задать тип контента, добавив content-type: example/example к нашему запросу, а затем менять example/example на разные значения, пока не получим положительный ответ.

Немного погуглив разные типы контента (application/html, text/xml и т. д.), можно найти подходящий — application/json. На такой запрос мы получим следующий ответ сервера: {“error”:{“body”:”unable to decode”}} с ошибкой HTTP 418.

Использование уязвимостей

Наконец-то мы можем послать данные серверу. Чтобы понять, что именно нужно отправить, для начала попробуем запрос с данными {“test”:”test”}, на который сервер ответит следующим образом: {“error”:{“domain”:”required”}}.

Мы сдвинулись с мёртвой точки. Теперь мы знаем, что нужно отослать веб-адрес в параметре domain. Для начала попробуем отослать имя хоста acme.org — {“domain”:”admin.acme.org” }. В ответ получим следующее: {“error”:{“domain”:”incorrect value, .com domain expected”}}.

Сейчас можно попробовать сменить домен верхнего уровня на .com. В ответ на новый запрос мы получим следующее: {“error”:{“domain”:”incorrect value, sub domain should contain 212"}}.

Мы можем сменить admin на 212.acme.com, и наконец-то получим в ответ {“next”:”\/read.php?id=307"}.

Откроем новую страницу сайта, введя её полный адрес admin.acme.org/read.php?id=307. В ответе мы увидим {“data”:““}.


Это указывает на то, что если мы подберём правильное значение поддомена, то сможем получить другой ответ от сервера. Перейдём на google.com и попытаемся найти обманку для сайта по маске 212.*.com. Для этого в строке поиска введём site:212.*.com. Появится несколько вариантов, из которых в итоге был использован 212.njzhuoding.com. Отправляем запрос на адрес admin.acme.org и в ответ получаем: {“next”:”\/read.php?id=308"}.


Посетив новую PHP-страницу, обнаружим закодированный в Base64 ответ, расшифровав который получим HTML-код. Это означает, что, во-первых, эта конечная точка запрашивает информацию у сервера, а затем кодирует всё в Base64 перед тем, как разместить её на веб-странице admin.acme.org. Во-вторых, у нас есть возможность использовать SSRF-уязвимость. В-третьих, нам нужно обойти требования к поддомену и к домену высокого уровня .com, если мы хотим использовать эту конечную точку для получения флага.

SSRF (server side request forgery) — это эксплойт, с помощью которого можно заставить уязвимое приложение сделать запрос на предоставленный URL. Так можно получить доступ к внутренним службам или данным, недоступным для обычного пользователя.

Для начала нужно найти способ обойти требования к поддомену и домену высокого уровня. Одним из способов будет использование символов перевода или разрыва строки (CRLF — carriage return line feed), чтобы в запросе было несколько строк. Это позволит удовлетворить требованиям и заставит сервер обрабатывать каждую строку запроса в отдельности.

CRFL-символы используются для обозначения конца строки. Так как сервер использует дистрибутив Linux, мы можем просто использовать LF или \n для создания новой строки.

Пример такого запроса: 212.\nacme.org\n.com. Сервер это расшифрует следующим образом: сначала он отправит запрос на поддомен 212, затем запрос на acme.org, а затем на домен верхнего уровня .com. Важно отметить, что каждый запрос заставляет id-номер увеличиваться на единицу. Запрос, упомянутый ранее, сформирует три новых id, по одному на каждый запрос. Если мы пошлём запрос {“domain”:”212.\nacme.org\n.com”}, то в ответ получим {“next”:”\/read.php?id=311"}.

Чтобы увидеть ответ от acme.org, нам нужно вычесть единицу из id-номера id=310. Посетив эту страницу, мы обнаружим, что она закодирована Base64, как и ожидалось. Сейчас мы можем перейти к более лёгкой части таска — использованию SSRF.

Использование SSRF и получение флага

Так как теперь нам не нужно беспокоиться о требованиях к домену верхнего уровня или поддомену, можно попробовать обратиться к локальному хосту по порту 80, например, вот так: {“domain”:”212.\nlocalhost:80\n.com”}.

Декодировав ответ из Base64, мы, как и ожидалось, получим дефолтную страницу Apache. Более того, это подтверждает, что мы получили доступ к локальному окружению. Возможно, флаг находится на том же месте, что и фальшивый флаг, но на внутреннем порту. В первую очередь стоит попробовать распространённые порты, такие как 8443, 8080, 443 и 22.

Для проверки портов будем использовать тот же формат, что и для 80-го порта. В расшифрованном ответе с 22-го порта получаем версии SSH и OS, а также сообщение о несовпадающих протоколах. Все порты, кроме 22-го и 80-го, приводят к ответу 404, значит, скорее всего, используется какой-то нестандартный порт. Учитывая, что это CTF-соревнования, есть смысл попробовать 1337 порт, так как это хорошо известный хакерский лексикон (1337 означает «элита»). Попробовав его, получаем следующий ответ: {“data”:”SG1tLCB3aGVyZSB3b3VsZCBpdCBiZT8K”}.


Это расшифровывается как «Hmm, where would it be?» («Хммм, где же он может быть?»). Мы уже близко, давайте попробуем localhost:1337/flag, так как об этом была подсказка ранее.



Это даст нам следующее:

{“data”:”RkxBRzogQ0YsMmRzVlwvXWZSQVlRLlRERXBgdyJNKCVtVTtwOSs5RkR7WjQ4WCpKdHR7JXZTKCRnN1xTKTpmJT1QW1lAbmthPTx0cWhuRjxhcT1LNTpCQ0BTYip7WyV6IitAeVBiL25mRm5hPGUkaHZ7cDhyMlt2TU1GNTJ5OnovRGg7ezYK”}

Декодировав сообщение, получаем наш флаг:

FLAG: CF,2dsV\/]fRAYQ.TDEp`w”M(%mU;p9+9FD{Z48X*Jtt{%vS($g7\S):f%=P[Y@nka=<tqhnF<aq=K5:BC@Sb*{[%z”+@yPb/nfFna<e$hv{p8r2[vMMF52y:z/Dh;{6

Мы получили флаг и успешно решили таск. Ещё можно было найти порт, просканировав все порты и проверив их ответы. Это было бы долго и довольно утомительно, но вполне реально с помощью Burp Intruder.

CTF-соревнования иногда содержат преднамеренные подсказки для участников, что может значительно облегчить поиск флагов. В целом этот таск был достаточно прямолинеен и не требовал каких-то специальных знаний об уязвимостях веб-приложений.

Если вам после этого райтапа захотелось попробовать свои силы в CTF, то список площадок с тасками можно найти здесь.

Перевод статьи «H1–212 Capture the Flag Write up»

Варвара Николаева

Подобрали два теста для вас:
— А здесь можно применить блокчейн?
Серверы для котиков: выберите лучшее решение для проекта и проверьте себя.