Capture the Flag: разбор взлома сервера на примере уязвимости SSRF
С помощью CTF можно усовершенствовать свои навыки пентеста. Представляем вашему вниманию разбор одного из таких соревнований.
16К открытий17К показов
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”:““}
.
Посетив новую 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, так как об этом была подсказка ранее.
Декодировав сообщение, получаем наш флаг:
Мы получили флаг и успешно решили таск. Ещё можно было найти порт, просканировав все порты и проверив их ответы. Это было бы долго и довольно утомительно, но вполне реально с помощью Burp Intruder.
CTF-соревнования иногда содержат преднамеренные подсказки для участников, что может значительно облегчить поиск флагов. В целом этот таск был достаточно прямолинеен и не требовал каких-то специальных знаний об уязвимостях веб-приложений.
Если вам после этого райтапа захотелось попробовать свои силы в CTF, то список площадок с тасками можно найти здесь.
16К открытий17К показов