FlawOpen

flawopen.com/Каталог/226

● CWE-367 / CWE-829 · Высокий
Исследования · FlawOpen

Marimo GHSL-2026-226: TOCTOU в workflow тестового релиза

Гонка временных меток в пределах одной секунды позволяла изменению pull request пройти одобрение тестового релиза и попасть в задания публикации TestPyPI и npm. Уязвимый workflow удалили; CVE не назначали.

💡 Простыми словами (ELI5)

Руководитель лаборатории одобряет конкретный образец, но лаборатория сверяет только секунду на часах. Если подменить образец в ту же секунду, замена выглядит одобренной. Workflow Marimo допустил такую ошибку с commit pull request, а затем передал собранный пакет в задания публикации.

Ключевые понятия и термины

TOCTOU
Time-of-check to time-of-use: ресурс меняется после проверки безопасности, но до его использования.
Публикация через OIDC
CI-задание обменивает удостоверение GitHub Actions на временные учётные данные реестра пакетов вместо хранения постоянного пароля.
Артефакт workflow
Набор файлов, передаваемый между заданиями GitHub Actions. Следующее задание должно считать артефакты недоверенных workflow недоверенными данными.
GHSL-2026-226
Идентификатор advisory GitHub Security Lab для этой ошибки workflow Marimo. CVE в advisory не назначен.
CWE-367 / CWE-829
В advisory указаны гонка между проверкой и использованием, а также выполнение функциональности из недоверенной области управления.

Связи со стандартами

  • Ссылка CWE для слабого места, описанного в этом руководстве: CWE-367 CWE-829

Пошаговый сценарий атаки

Шаг 1

Сопровождающий разрешает тестовый релиз

Сопровождающий оставляет комментарий в pull request, запуская workflow тестового релиза Marimo. Разрешение должно относиться к проверенному состоянию pull request.

Шаг 2

Head pull request меняется в ту же секунду

Workflow сравнивает pushed_at репозитория с created_at комментария после преобразования в целые секунды. Push в ту же секунду даёт равные значения.

Шаг 3

Уязвимая проверка принимает равенство

Удалённый workflow отклонял только pushed_at, строго большее времени комментария. Равенство проходило, и обновлённый commit сохранялся.

Шаг 4

Недоверенный код собирает артефакты пакетов

Задание получало этот commit и запускало actions и скрипты сборки из репозитория. Результаты загружались как артефакты и передавались заданиям публикации без повторной сборки из доверенного commit.

Шаг 5

Полномочия публикации достигают недоверенного результата

Задания TestPyPI и npm использовали права публикации OIDC. Advisory описывает возможность передачи артефакта в публикацию; о реальной компрометации пакета не сообщается.

Исходный код: Уязвимый vs Защищённый вариант

GHSL-2026-226 сообщает, что уязвимый workflow удалён в PR #10765; это не утверждение, что проект применил показанную замену сравнения. В старой проверке использовалось -gt, поэтому равные секундные значения проходили. Иллюстративная проверка -ge отклоняет равенство, но лучше связывать разрешение с точным проверенным commit. Отделяйте недоверенные сборки от OIDC-заданий, пересобирайте из разрешённого SHA и проверяйте происхождение перед публикацией. Эксплойт-payload не приводится.

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
# Удалённый workflow marimo-bot: сравнение целых секунд
if [[ $(date -d "$pushed_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
    exit 1
fi
# Полученный head_sha checkout-ился, а артефакты
# передавались отдельным заданиям публикации.
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
# Только иллюстративный контроль; GHSL сообщает, что уязвимый
# workflow удалили, а не исправили на месте.
if [[ $(date -d "$pushed_at" +%s) -ge $(date -d "$COMMENT_AT" +%s) ]]; then
    exit 1
fi
# Более надёжная схема: разрешить проверенный неизменяемый SHA,
# пересобрать его в доверенном задании и опубликовать проверенный результат.

Чек-лист по защите системы для инженеров

Источники