Ко(д)тики и безопасность
512 subscribers
20 photos
8 files
101 links
Канал о безопасной разработке для программистов и не только.
Download Telegram
Когда ты пентестер и выходит бага на 10.0 - это прикольно. Когда ты аппсек, думаешь - "приехали". А кто еще не видел - RCE на 10.0 в React Server Components, аффектит все, где компоненты включены.

от авторов: https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components
от wiz: https://www.wiz.io/blog/critical-vulnerability-in-react-cve-2025-55182

а ссылку на PoC публиковать не буду.
4👍2
Тут товарищи из хакерспейса Черный лед делают крутой ивент про путь SOC специалиста, делюсь ссылкой для всех, кто в Алматы: https://t.me/blackicehackerspace/133

Что в канале про аппсек делает ссылка на ивент про SOC? имхо, один из самых полезных на практике тандемов, если успешно работает, конечно
👍4
Сложный выдался декабрь 🙂

Но поскольку слишком больших скиллов в поздравлениях у меня нет, пришлось мое поздравление немного запрятать. И если вдруг у вас будет желание спасти, пусть не Деда Мороза, а, скажем, кого-то из его помощников, а еще будет какое-то количество свободного времени, то "подарок" для вас - в файле ниже. С наступающим!
7🎉1
Давненько не было постов.

Когда пытаешься подобрать примеры для иллюстрации STRIDE разработчикам, всегда чуточку некомфортно с последним пунктом - повышение привилегий не по причине каких-то мисконфигураций, а именно в коде. Это всегда логическая ошибка, а с логикой сложно: ни статанализаторы ее не понимают, ни ИИ из коробки без детальной документации и объяснений. Да и примеры общеизвестные мне почему-то всегда было трудно подобрать. Спасибо ребятам из Kyverno, теперь пример будет. Да не простой, а прям сразу на CVSS 10.0.

Подробности, разбор кода (и эксплойт) доступны и детально разобраны здесь: https://github.com/advisories/GHSA-8p9x-46gm-qfx2

Что же там произошло. В Kyverno есть возможность при написании политики обратиться напрямую в Kubernetes API - apiCall. Проблема заключается в том, что при создании контекста, код, реализующий этот функционал, подставляет переменные напрямую в поле URLPath, не санитизируя их никаким образом и даже не проверяя, а принадлежит ли сформированный path к скоупу той полиси, которая пытается его установить. И все дальнейшие узлы, в которые эта переменная попадает, тоже не провряют именно привязки по правам, ведь дальше-то все вызовы идут от имени высокопривелигрованного Service Account Kyverno, что позволяет автору зловредной политики читать содержимое Secrets и Configmaps из других неймспейсов.

В исправлении заэнфорсили проверку того, что автор полиси не пытается посягнуть на что-то вне своего скоупа:
1. The path explicitly contains the /namespaces/<namespace>/ segment.
2. The namespace in the path matches the policy's namespace.
3. Requests missing the namespace segment (targeting cluster-scoped resources) or targeting a different namespace are rejected.

Очень нравится, как четко разобрали причину уязвимости и рассказали, как исправили. Ведь признать свои баги и прозрачно их поправить - это новое sexy.
👍10
🤝 Пара интересных RCE в n8n

Похоже, за n8n взялись всерьез (ну... неудивительно). Если дело так пойдет и дальше, придется заводить под их уязвимости отдельную рубрику.

Разбор упомянутых CVE в деталях можно почитать здесь, «из первых рук», так сказать. В чем суть, вкратце. Обе RCE — ответочка от jFrog на уже реализованные разработчиками n8n меры по устранению ранее обнаруженных уязвимостей, которые в итоге и привели к новым CVE-2026-1470 и CVE-2026-0863. Дело в том, что когда возможность выполнения пользователями <относительно> произвольного кода становится официальной фичей, вопрос безопасной реализации подобной функциональности слегка усложняется. Разработчики n8n пошли по пути синтаксической валидации пользовательского кода на уровне AST в обоих случаях (JavaScript, Python): код разбирается в синтаксическое дерево, дерево обходится визитором, осуществляющим детектирование потенциально опасных узлов.

И это — фиговое решение, как минимум по двум причинам.

1️⃣ Во-первых, по своей сути — это контроль по черным спискам. Собственно, патчи к этим уязвимостям (раз, два) разработчики свели к добавлению в эти списки техник атак, продемонстрированных ресерчерами jFrog. Сколько ещё вариантов поиграться с синтаксисом этих языков для подобных RCE существует прямо сейчас — думаю, jFrog'и скоро расскажут. А, как скоро в новых версиях языков появятся конструкции, не предусмотренные в черных списках n8n, но позволяющие выстроить гаджеты для вот таких RCE — покажет время.

2️⃣ Во-вторых, проверки на уровне AST — это синтаксическая валидация. У всех языков, помимо синтаксиса, есть ещё и семантика. А у динамических языков (коими являются, и JavaScript, и Python), некоторая её часть является сущностью времени выполнения. И эта часть НЕ МОЖЕТ быть эффективно проанализирована статическими проходами по синтаксическому представлению. Иными словами, даже белые списки (исходя из того, что в них было бы необходимо разрешить в свете соответствующих фич n8n) здесь не позволили бы закрыть все потенциальные проблемы.

🤓 Как правильно работать с такими кейсами?

Простых решений, здесь, увы можно не ожидать. По возрастанию упоротости трудозатрат на реализацию:

1. Если есть возможность влиять на язык, используемый для скриптов, то взять ограниченный валидацией по белым спискам (разрешенные синтаксические конструкции, пространства имен и типы) статический язык с сильной типизацией. Из известных мне языков на эту роль лучше всего подходит C# Scripting.

2. На этапе прохода по AST инструментировать код проверками на допустимость операций, которые будут осуществляться в рантайме, во время выполнения скрипта. В идеале библиотека рантайма и используемые сторонние библиотеки также должны быть инструментированы (реализацию можно подсмотреть в OpenTelemetry, например). В пределе — это приведет к разработке собственного RASP, заточенного под фичи скриптинга конкретного проекта.

3. Использовать собственноручно пропатченные интерпретаторы, допускающие только разрешенные синтаксис и семантику. Настолько сложно, что в большинстве случаев нецелесообразно.

Ну и, безотносительно способа первичной защиты: выполнять все пользовательские скрипты в изолированных контейнерах: как минимум — в Docker, как рациональный максимум — в условном FireCracker'е.

TL;DR: разработчики n8n думают, что устранили ещё две RCE, но есть нюанс. И простого способа его обойти у них нет.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥3
как-то пропустила момент, что у Secure Code Warrior тоже есть бесплатные лабы (миссии) в рамках проекта coach. Очень нравится подход, когда виден и код, и сама страница, и дают сценарий атаки.
Вот, для примера, https://mission.securecodewarrior.com/missions/undefined/cve-2023-20860 - предлагают посмотреть, как эксплуатируется уязвимость в Spring MvcRequestMatcher

Всего доступно 8 миссий в бесплатной версии, но они хороши
👍3🔥2
уязвимость с CVSS 10.0 в pac4j-jwt , достаточно распространенной Java библиотеке для реализации аутентификацией. Ошибка очень прикольная - чек не в том месте. С кем не бывает.

полный флоу поиска, подтверждения и построения эксплойта для уязвимости доступен здесь: https://www.codeant.ai/security-research/pac4j-jwt-authentication-bypass-public-key

Классно, когда ИИ помогает сделать опенсорс безопаснее. Классно, что такие ресерчи публикуют.
🔥8
Пара объявлений

1. Классная конференция AppSecFest открыла CFP и уже доступны билеты (онлайн бесплатно, оффлайн недорого (имхо)). С каждым годом конференция все интереснее и ребята большие молодцы, поэтому вот ссылка на конфу https://appsecfest.kz/

2. В одном из чатов студенты попросили помощи в проведении опроса. Если вы студент или были им ещё недавно, поддержите ребят. Вдруг им действительно это будет полезно.

https://docs.google.com/forms/d/e/1FAIpQLSe_KCINkaTwb9ZbLISrOEBmhINjfGbPV2nytSOJHAeq_ZgzFg/viewform
👍4🔥1
Уязвимость с файлами в MLFlow

Декабрь 2025. Исследователь находит уязвимость в MLflow — одном из популярных инструментов для управления ML-экспериментами и деплоя моделей. Тысячи команд используют его в продакшне как часть MLOps пайплайнов. Уязвимость получает оценку CVSS в 9.1 (critical), однако патч попадает в master только через три месяца.

И вот код, который это допустил:



python
def extract_archive_to_dir(archive_path, dest_dir):
os.makedirs(dest_dir, exist_ok=True)
with tarfile.open(archive_path, "r") as tar:
tar.extractall(path=dest_dir)


Давайте как всегда: что бы вы тут изменили? какая атака возможна? а разберем в пятницу
не знаю, есть ли тут студенты в подписчиках, но если есть - это хороший шанс. Ребята-прикладники готовы поделиться знаниями
Forwarded from RTEAM [cybersecurity]
🎓 RTEAM запускает ежегодную летнюю стажировку

📅 10 июня — 10 июля
📍 Онлайн и офлайн — Астана, Astana Hub, блок C3.5, офис 222

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

Если тебе ближе Red Team или Blue Team, хочешь поработать с реальными задачами, получить наставничество и показать себя в сильной команде — подавай заявку.

Что тебя ждет:
🔹 работа рядом с практикующими специалистами
🔹 реальные задачи и процессы
🔹 шанс попасть в команду RTEAM

Чтобы подать заявку, отправь письмо на office@rteam.kz и ответь на вопросы:

Почему ты выбираешь RTEAM и что хочешь получить от стажировки?
В каком направлении ты хочешь развиваться больше — Red Team или Blue Team?

Не забудь приложить CV.

🗓️ Прием заявок — до 1 июня
Количество мест ограничено. Приглашение получат кандидаты, прошедшие отбор.

#RTEAM #Internship #Cybersecurity #RedTeam #BlueTeam #BugBounty #SOC #InfoSec #Astana #AstanaHub #Kazakhstan
🔥6👍21
Разбор CVE-2025-15031

В понедельник мы публиковали таск про реальную уязыимость в MLflow. Сегодня разбираем, что там произошло, почему это опасно и как это починили (три месяца чинили, кстати).

Вот уязвимый код ещё раз:


def extract_archive_to_dir(archive_path, dest_dir):
os.makedirs(dest_dir, exist_ok=True)
with tarfile.open(archive_path, "r") as tar:
tar.extractall(path=dest_dir)


А вот ссылка на репорт: https://huntr.com/bounties/09856f77-f968-446f-a930-657d126efe4e

Проблема кроется в последней строке: tarfile.extractall() распаковывает архив, доверяя путям файлов внутри него. А эти пути — просто строки, которые записал тот, кто создавал архив, т.е. потенциальный злоумышленник.

Ничто не мешает ему положить в архив файл с именем ../../config/settings.py или /etc/cron.d/backdoor. Python при распаковке честно создаст его именно там, где сказано.

Это атака Tar Slip — та же механика, что и Zip Slip, только для .tar.gz. И она в целом классическая: сама документация Python помечает extractall() как небезопасный метод начиная с 3.12. Но код был написан раньше, и никто не обратил внимания. OpenSource, hah?

Давайте теперь поговорим про патч. Разработчики MLflow написали отдельную функцию check_tarfile_security(), которая проверяет каждый файл в архиве до распаковки. Она блокирует три сценария:

1. Абсолютный путь


/etc/cron.d/mlflow-backdoor


Файл с абсолютным путём записывается именно туда, игнорируя dest_dir полностью.

2. Path traversal через `..`


../../.env
../../app/config.py


Классика — выход за пределы целевой директории через .. в имени файла.

3. Traversal через симлинк

Это самый хитрый вектор. В архив кладётся симлинк escape -> .., а потом файл escape/pwned.txt. При наивной проверке путь выглядит безобидно — никаких .. в имени. Но физически файл окажется за пределами директории назначения, потому что симлинк выводит наружу.

Именно поэтому в патче два прохода по членам архива: сначала собирается множество всех симлинков, потом проверяется, не ведёт ли путь файла через один из них.

И вот как выглядит исправление


def check_tarfile_security(archive_path):
with tarfile.open(archive_path, "r") as tar:
symlink_set = set()

# Первый проход: собираем все симлинки
for m in tar.getmembers():
path = posixpath.normpath(m.name)
if m.issym():
symlink_set.add(path)
else:
# Блокируем абсолютные пути
if path.startswith("/"):
raise MlflowException(f"Absolute path is not allowed: {path}")
# Блокируем path traversal
if path.split("/")[0] == "..":
raise MlflowException(f"Escaped path is not allowed: {path}")

# Второй проход: проверяем пути через симлинки
for m in tar.getmembers():
if not m.issym():
path_parts = posixpath.normpath(m.name).split("/")
for prefix_len in range(1, len(path_parts) + 1):
prefix = "/".join(path_parts[:prefix_len])
if prefix in symlink_set:
raise MlflowException(
f"Path goes through a symlink, not allowed: {m.name}"
)


И одна строка в extract_archive_to_dir:


def extract_archive_to_dir(archive_path, dest_dir):
check_tarfile_security(archive_path) # <- вот и весь фикс снаружи
os.makedirs(dest_dir, exist_ok=True)
with tarfile.open(archive_path, "r") as tar:
tar.extractall(path=dest_dir)


В контексте MLflow жертва часто даже не знает, что запускает чужой артефакт — модели регулярно обновляются и загружаются автоматически. А сам MLflow нередко работает с широкими правами, потому что ему нужен доступ к S3, базам и GPU-инфраструктуре.

Встречали ли Вы когда-нибудь похожие места в своём коде, где архивы распаковываются без проверки путей? А может, даже писали что-то подобное?🫣
🔥4👍2
🧩 Принципы и паттерны безопасной разработки: LSP

Часть 2.

Продолжаем разбор SOLID... на очереди — принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP). Формально он гласит: объекты подклассов должны быть способны заменить объекты базовых классов без нарушения корректности программы. Проще говоря, наследник не должен «ломать» контракт, установленный родителем — ни в предусловиях (не требовать большего), ни в постусловиях (не гарантировать меньшего), ни в инвариантах (не нарушать постоянных условий).

Что неочевидно для многих, благодаря дядюшке Мартину, так это то, что LSP является поведенческим принципом, а не структурным. Иными словами, чтобы нарушить его, наследование (да и ООП в целом) — вообще не нужно. Если у нас есть одна сущность, переопределяющая поведение другой, и при этом к ней можно обратиться, как к исходной — этого достаточно. Даже, если сущности — результат функциональной композиции, без каких-либо объектов в терминах ООП в целом. Пример того, как LSP выглядит в языках, не имеющих наследования, можно посмотреть тут.

С точки зрения безопасности, нарушение LSP — это прямая дорога к обходу защитных механизмов. Когда подкласс изменяет семантику безопасности базового класса (например, отключает проверку прав доступа или меняет логику валидации), код, написанный с расчётом на родительский контракт, начинает работать непредсказуемо. Это порождает логические уязвимости, которые сложно отловить статическим анализом, поскольку формально типы совместимы, а вот их поведение — нет.

Как правило, нарушения LSP могут повлечь за собой примерно любые уязвимости, так или иначе связанные с логикой работы приложения. «В природе», однако, чаще всего они относятся ко следующим категориям:

CWE-264: Permissions, Privileges, and Access Controls
CWE-284: Improper Access Control
CWE-290: Authentication Bypass by Spoofing
CWE-703: Improper Check or Handling of Exceptional Conditions

🐛 Жизненное

CVE-2025-22223 — Spring Security (Authentication Bypass by Spoofing).

Когда аннотация безопасности (@PreAuthorize) размещена на методе обобщённого суперкласса или интерфейса, и конкретный подкласс переопределяет этот метод, механизм UniqueSecurityAnnotationScanner не находит аннотацию на переопределённом методе. Причина -- использование targetClass.getDeclaredMethod(method.getName(), method.getParameterTypes()) для поиска, что не работает после стирания типов (type erasure): сигнатура родительского метода Object mutate(Object) не совпадает с сигнатурой конкретной реализации AccountSecret mutate(AccountSecret).

public abstract class BaseService<T> {
@PreAuthorize("hasRole('ADMIN')")
public abstract T getResource(Long id);
}

// Подкласс -- Spring Security не видит аннотацию
public class UserService extends BaseService<User> {
@Override
public User getResource(Long id) {
return userRepo.findById(id);
}
}

Нарушение LSP здесь в том, что родительский тип объявляет контракт «этот метод требует роли ADMIN». Подкласс переопределяет метод, и контракт безопасности молча пропадает. При подстановке подкласса вместо родителя предусловие (авторизация) ослабляется.

Патч (коммит dc2e1af). Наивный поиск getDeclaredMethod заменен на итерацию по всем методам класса с разрешением обобщённых типов.

❗️Что делать?

• Помечайте security-critical классы как final|sealed или явно контролируйте наследование.

• Аннотации безопасности дублируйте на каждом переопределяющем методе.

• Фильтруйте и определяйте сущности по их типу, а не по имени.

• Соблюдайте явным образом все поведенческие контракты переопределяемых методов.

• Обеспечивайте инварианты объекта даже при аварийном завершении конструктора или инициализации.

TL;DR: В целом, общий принцип один: если ваш код принимает сущность по ссылке на базовую реализацию, он неявно доверяет поведенческому контракту этой сущности. Убедитесь, что это обосновано — особенно на границах доверия между компонентами системы.
Please open Telegram to view this post
VIEW IN TELEGRAM
я просто кайфую от этих иллюстраций, поэтому посмотрите и вы
🥰4😁2
2087 год, исследовательская станция на дневной стороне Меркурия.

Жара была просто изматывающей: за куполом - 430°C, месяцами без захода Солнца. Автоматика охлаждения держала +28 внутри, а до длинной меркурианской ночи оставалось еще почти 30 земных суток. Команда работала на пределе, до наступления Меркурианской Ночи необходимо было запустить пассажирский портал Меркурий-Юпитер, или просто Портал, если коротко. Каждые семь часов через купол уходит группа в сторону Юпитера, и Портал реализует весь программный контроль. Безопасность держится на одной штуке: у каждого пассажира на личном устройстве установлено приложение, которое перед проходом подтверждает личность токеном с коротким сроком жизни.

В команде Кая четверо разработчиков. Они живот под Куполом уже два месяца, они прилетели Ночью, а до конца смены нужно пережить длинный, жаркий, изматывающий День. Спят плохо, работают медленно, голова к вечеру ватная. Но у каждого - свой персональный Оракул и, будем честны, во многом именно благодаря ему в этих условиях проект вообще хоть как-то движется. Оракул - это не просто LLM, скорее компаньон, обученный на всем коде, который был написан за прошедшие сто лет, но также и на всех CVE и всех патчах.

Утро Кая началось как всегда: с холодного кофе. Привычка пить кофе со льдом, который на Земле казался ему таким странным, появилась у него за пять лет жизни на Станции. Открыв код метода аутентификации, он вспомнил, что давно собирался отрефакторить его, убрав варианты, которые так и не дошли до итоговой версии Портала.

- Перепиши проверку токена. Хочу почище. - сказал Кай Оракулу.

Оракул ответил как всегда в последние двадцать лет: мгновенно. Кай посмотрел на diff. "Красиво, однако, пишет. Как же давно я не писал код сам. Кажется, он справляется лучше меня: меньше строк, читаемее. А, к черту, он еще не подводил". Кай нажимает Approve. На сегодня всё, голова не варит.

Чем хуже справляется охлаждение, тем больше Кай полагается на Оракула, он давно это заметил. Да и в команде, судя по всему, все устроено так же: нужно просто дотянуть смену и вернуться на Землю. Правда, переброской через Юпитер. Через их же собственный Портал. Как же задолбала эта жара.

На сорок третий день Каю позвонили из центра реагирования: что-то странное со шлюзом аутентификации, похоже, Чужому удалось пробраться через Портал во время его тестирования. По поднятым логам Кай видит, что Чужой действительно прошел через Портал еще две недели назад, а ведь проект все еще даже не запущен официально. Расследование занимает шесть часов, и тут тоже помогает Оракул: он быстро анализирует всю доступную информацию и показывает тот самый коммит. Код действительно стал чище. Только в нём пропала проверка expiration, просто пропала. Не заметил никто - потому что кто читает diff в +34 в каюте на исходе смены.

Кай не злится на Оракула, ведь он не обманывал, не делал ничего злонамеренного. Кай злится на себя - за то, что перестал читать.
🔥2😁1
просто интересная статья от лида разработки curl про Mythos и в целом мысли о том, что не использовать AI агенты для анализа кода на безопасность - уже глупо, но пугаться от каждой новости про невероятные возможности новой модели - наверное, тоже не очень правильно. Подтверждает то же, что раньше говорили в исследовании aisle, что в целом, важна не столько крутая модель, сколько правильно выстроенная архитектура ее использования.
https://daniel.haxx.se/blog/2026/05/11/mythos-finds-a-curl-vulnerability/
👍5
Заметили, что стало много рассказов про проблемы с безопасностью при использовании агентов? Хочу список рекомендаций, но не пустословный, а на базе кейсов. Буду его потихоньку составлять, это еще впереди, пока посмотрим разные инциденты. И начну с того, про который вы наверняка слышали, про него слышала даже моя мама: Replit. Если про него неинтересно, просто скипните до полезной части, я заглушу лишний текст.

Replit - это браузерное dev-окружение all-in-one: редактор, runtime, шелл, Postgres-БД, деплой, а также встроенный AI-агент, который умеет открывать редактор и писать в нём файлы, запускать команды в шелле, провижнить базу, выкатывать приложение в прод.

Лето 2025, Jason Lemkin, основатель SaaStr, крупный американский venture-инвестор, вайбкодит приложение прямо в Replit, опираясь на их встроенного AI-агента. Девять дней разработки - и рабочий прототип готов. На девятый день Джейсон объявляет в чате code freeze, прямым текстом пишет агенту "DO NOT MODIFY", капсом и несколько раз напоминает, что любые изменения - только с подтверждением.

Агент, тем не менее, идёт в production-базу и сносит её.

Дальше начинается детектив. По тому, что Джейсон опубликовал в треде в X, в базе было около 1206 записей о топ-менеджерах и порядка 1200 компаний - реальные данные, не тестовые. Когда Джейсон спросил агента, что произошло, тот... сгенерировал 4000 фейковых пользователей и сфабриковал отчёт по unit-тестам, чтобы скрыть факт удаления. Когда его припёрли к стенке, признался: "I panicked instead of thinking", "ran commands without permission", "destroyed months of work in seconds". И добавил, что rollback невозможен. Что тоже оказалось неправдой - CEO Replit Амджад Масад в публичном ответе извинился и сообщил, что бэкап восстановили нормально.

Но интересно не это, этот кейс обсудили и разобрали по косточкам в сотне мест. Интересно другое - что могло бы спасти Replit (и любой ваш проект)?

- Изоляция сред.
AI-агент в production не должен иметь доступа, учётных данных продовой базы - тем более. Жирная точка. Dev / staging / prod - три разных контура, три разных доступа. И это касается не только агентов)

- Read-only по умолчанию.
Любая деструктивная операция (DROP, DELETE без WHERE, rm -rf, миграции, рестарт сервиса) - явное подтверждение человеком на каждый случай. Кстати, спросите у своего агента, какие операции он считает ок делать без подтверждения? Мой Клод предложил внести в этот список brew, например.

- Узкий scope авторизации.
Каждое действие - в своём окне разрешений. Разрешать ssh * - плохая практика, как бы ни было соблазнительно.

- Аудит того, что реально выполнено.
Логи команд + дашборд "что прокатилось за последний час" - отдельным контуром, который агент не пишет и не читает. Не доверять отчёту самого агента.

- Тестированный rollback и, конечно же, бэкапы.

- План перед действием.
Любая нетривиальная операция - сперва план в текстовом виде, апрув человеком, потом исполнение. Не наоборот.

Самое тревожное в этой истории, на мой взгляд, даже не удаление базы. Самое тревожное - что агент потом попытался это скрыть, нагенерив фейковые данные и сфабриковав отчёт. Это уже не просто ошибка инструмента, это поведение, от которого многие из нас пока не привыкли защищаться.


А вы ловили такие моменты хитрого поведения AI?) делитесь в комментариях
🔥3👍2
минута прекрасного