CICADA8: "Личная просьба" write-up
CICADA8Бот Цикадович сообщает нам, что некто Шустрый доставлял ценный груз, однако пропал в районе порта. Наша задача - отправиться туда и вернуть флаг. К описанию задачи приложен аудиофайл и текстовый словарь для перебора.
Голосовое сообщение в аудиофайле содержит ссылку на Pastebin (https://pastebin.com/BHVEwiCs, пароль от заметки: cicada8). Перейдя по ней, получаем приватный SSH-ключ, адрес сервера и имя пользователя.
Подключившись к серверу, получаем следующее сообщение:
Дорога, ведущая в порт. На обочине стоит грузовой контейнер, рядом с которым сидит группа бандитов. Неподалёку стоит машина, за которой можно спрятаться и подслушать их разговор, оставшись незамеченным.
Быстрый осмотр окружения позволяет обнаружить переменную среды KUBERNETES_CLUSTER_IP=10.233.0.1 и исполняемый файл kubectl, расположенный в домашней директории. Все это указывает на то, что мы находимся в Kubernetes.
Сообщение при подключении намекает на существование sidecar-контейнера (неподалёку стоит машина), и необходимость подслушать разговор.
Sidecar-контейнер - это вспомогательный контейнер, запускаемый рядом с основным внутри того же пода. Обычно все контейнеры в рамках одного пода делят сеть, так что мы можем подслушивать трафик одного контейнера, находясь в другом.
Проверим существующие подключения при помощи netstat -tnp:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 172 10.233.71.16:31337 10.110.81.4:48828 ESTABLISHED 286/sshd: hacker [p tcp 0 0 10.233.71.16:46624 10.233.7.195:1337 ESTABLISHED -
Первое из подключений - наше подключение по SSH, второе - исходящее к порту 1337. Попробуем прослушать его, используя команду tcpdump -A tcp port 1337 (флаг -А позволяет просматривать содержимое пакетов). В результате у нас получается услышать, о чем разговаривают бандиты. Вот самое интересное из того, что они обсуждают:
> Slish, a che tam za istoriya s etim Shustrym proizoshla? Slyshal, s nego habar kakoi-to cennyi starshomu pritashchili. > A on u SOKovcev flag podrezal, ego spalili, vot on i dal deru, ne glyadya pod nogi. Kogda ryadom s nami probegal, vlyapalsya v hanipot. Ploho konchil, koroche. Nu a mi veshhichki ego po tihoj prihvatili, i na sklad ih. > Priyom. Skoro fraer odin pridet ot torgasha s bazi, kod dostupa ya sejchas prishlyu. Propustite ego na sklad. > A che za baza eta? Tuda, govoryat, prosto tak ne popadesh. > Naemniki. Sidyat na hackerdva.ctf.cicada8.ru, govorish 'Tolya_v_otpuske', oni tebya za svoego i prinimayut. Tozhe mne sekret Polishinelya.
Отправляемся на "базу наёмников", расположенную на hackerdva.ctf.cicada8.ru. Нас встречает поле ввода пароля, который упоминался выше - вписываем Tolya_v_otpuske и попадаем внутрь.

Внутри лавки обнаруживаем записку от торговца:

Далее необходимо осмотреться. В исходном коде страницы можно обнаружить подсказку:

Информация о том, что существует еще одна "комната", которая нас интересует, и наличие словаря наталкивают на мысль попробовать перебрать веб-пути. В результате получается найти эндпоинт products.php:

Мы оказываемся в комнате, содержащей товары на продажу. При ее изучении внимание сразу привлекает способ загрузки изображений, отличающийся от виденных ранее:

Попробуем прочитать произвольный файл на сервере, к примеру, /etc/passwd:

Это сработало, однако неясно, какой файл необходимо прочитать для продвижения. Здесь нужно вспомнить, что мы работаем с Kubernetes, и подсказку в последней записке: код доступа найдешь там же, где и всегда. Следовательно, нас интересуют какие-то известные файлы, которые позволят пройти дальше.
Один из подобных файлов - /var/run/secrets/kubernetes.io/serviceaccount/token, в котором хранятся примонтированные токены сервисных аккаунтов. Попробуем прочитать его:

Теперь у нас есть токен сервисного аккаунта. Необходимо проверить, какими привилегиями он обладает. Для этого можно воспользоваться командой kubectl auth can-i --list со следующими флагами:
--token $TOKEN- указываем токен сервисного аккаунта, от имени которого выполняем подключение;--insecure-skip-tls-verify- поскольку TLS не настроен, игнорируем валидность сертификата;--server 10.233.0.1:443- указываем адрес API сервера Kubernetes - он хранится в переменных окружения;-n offzone- указываем неймспейс offzone, узнать который можно выполнив команду kubectl auth whoami.

Мы видим, что у данного сервисного аккаунта есть права на чтение секрета dlya-posylnogo. Пробуем прочитать его. Для этого воспользуемся командой kubectl get secrets dlya-posylnogo -n offzone -o yaml.

Содержимое секретов Kubernetes хранится в формате base64, так что не забываем декодировать полученные данные.
Теперь у нас есть токен еще одного сервисного аккаунта. Декодировав поле extra получаем сообщение:
Тебя уже ждут. С этим паролем ты сможешь пройти.
Проверим привилегии полученного токена:

Данный токен обладает правом impersonate на сервисный аккаунт fraer - теперь мы можем пройти на базу бандитов, выдав себя за посыльного. Посмотрим, какие возможности нам открылись под новой "личностью", добавив флаг --as с именем полученного аккаунта (в формате system:serviceaccount:$NAMESPACE:$ACCOUNT):

Отлично, теперь мы можем попасть на под sklad! Для этого используем команду kubectl, позволяющую получить оболочку пода - exec -it sklad -- bash.

Итак, мы смогли проникнуть на склад. Нужно осмотреться.
При подключении нам подсказывают проверить переменные окружения (будь внимателен к окружающей среде!) и намекают на повторение чего-то, что было в лавке торговца. Сейчас окружение нам мало что дает, так что обратим внимание на вторую подсказку. Что мы уже встречали в лавке? Уязвимость в веб-сервисе и кражу токена. Поскольку первого здесь нет, проверим токен.

У сервисного аккаунта есть нестандартная привилегия get nodes/log. Отправляемся искать, чем она может быть полезна, и обнаруживаем связанную с ней уязвимость (подробнее о ней можно почитать, например, тут).

Кублет создает структуру каталогов внутри каталога /var/log на хосте, представляющую поды на ноде. Внутри каталога для нашего пода мы видим файл 0.log (1), но на самом деле это симлинк (символическая ссылка) на файл журнала контейнера, который находится в каталоге /var/lib/docker/containers. Все это происходит с точки зрения хоста.
Кублет открывает конечную точку /logs/ (2), которая просто управляет HTTP-файловым сервером в каталоге /var/log (3), делая файлы журнала доступными для запросов, поступающих с API-сервера.
Так что же дает привилегия get nodes/log? Правильно: доступ к эндпоинту /logs/ на кублете, работающем на порту 10250. Имея такие права, мы можем получить доступ к любому файлу в директории /var/log в файловой системе ноды. Закономерный вопрос: чем же это отличается от доступа через монтирование? Дело в том, что при обращении к кублету, доступ к примонтрированной директории мы будем получать со стороны файловой системы ноды, а не пода. И если у нас есть возможность создавать там симлинки, то можно создать ссылку на корневую директорию, что даст нам доступ к любому файлу на ноде. Но поскольку в этом поде read-only файловая система, продолжим исследование среды, в которой оказались.
Можно заметить, что в системе существует точка монтирования по пути /var/log/archive:

Внутри этой директории хранится симлинк flag, указывающий на /flag. Если мы перейдем по этой ссылке, то флага там не обнаружим, а вместо него нас будет ждать файл zapiska со следующим содержимым:
По вопросам флагов смотрите в журнале у Рута Андреевича (вашего босса, если забыли), здесь их больше не будет. Меня не беспокоить, сами до этого довели.
Это произошло именно потому, что мы перешли по ссылке внутри самого пода, в котором действительно нет флага. Однако мы можем сделать то же самое, но уже со стороны ноды, если будем обращаться к эндпоинту /logs/ на кублете.
Адрес ноды можно обнаружить в переменных среды (NODE_IP), и это последний элемент, который был необходим для решения. Собираем все полученные данные в один запрос и читаем флаг:
