FlawOpen

flawopen.com/Teardowns/cve-2024-7646-ingress-nginx-annotation-code-injection

● CVE-2024-7646 · CVSS 8.8 · Высокий
Исследования · FlawOpen

CVE-2024-7646: обход проверки Ingress-NGINX

Обход проверки через возврат каретки позволял внедрять команды и получать данные контроллера. Исправлено в 1.11.2/1.10.4; проект закрыт.

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

Представьте привратника, который проверяет, содержит ли форма разрешённое слово, но не проверяет значение целиком. Возврат каретки может изменить то, как следующая система прочитает оставшийся текст. Здесь аннотация прошла частичную проверку и затем попала в созданную конфигурацию NGINX. Исправление проверяет всё значение и безопасно обрабатывает возвраты каретки.

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

Объект Ingress
Ресурс Kubernetes API, описывающий маршрутизацию с хоста или пути на Service. Пользователям могут разрешить создавать его без прав администратора кластера.
Обход проверки аннотаций
Значение аннотации прошло проверку, обнаружившую разрешённую подстроку, без проверки полного точного совпадения.
Возврат каретки (`\r`)
Управляющий символ переноса строки в некоторых форматах. Разная трактовка при проверке и генерации конфигурации может изменить границы строк.
Учётная запись службы контроллера
Идентификатор Kubernetes, используемый контроллером ingress-nginx. По умолчанию его права могут включать доступ ко всем Secrets кластера.

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

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

Анализ первопричины

CVE-2024-7646 — это обход проверки аннотаций ingress-nginx, а не неограниченное использование пользовательского Lua-snippet. Проверка auth-tls-verify-client использовала регулярное выражение Go без якорей начала и конца, поэтому MatchString могла принять длинное значение, если оно содержало допустимый вариант как подстроку. Возврат каретки в аннотации затем мог интерпретироваться иначе при очистке созданной конфигурации NGINX. Исправление upstream требует совпадения всего значения и безопасно обрабатывает одиночные возвраты каретки в cleanConf. Согласно уведомлению Kubernetes, субъект с правом создавать объекты Ingress может обойти проверку, внедрить команды и получить учётные данные контроллера; в конфигурации по умолчанию они дают доступ ко всем Secrets кластера. Это аутентифицированный путь через API Kubernetes высокой серьёзности (CVSS 8.8), а не анонимный сетевой запрос. Проект закрыт в марте 2026 года; обновление до исторического исправления — только срочная мера, далее следует мигрировать на поддерживаемый контроллер или реализацию Gateway API.

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

Шаг 1

Пользователь может создать Ingress

Атакующему нужно право создавать объект Ingress через Kubernetes API. Особенно уязвимы многопользовательские кластеры, где это право делегировано командам приложений.

Шаг 2

Аннотация проходит частичную проверку

В регулярном выражении auth-tls-verify-client не было якорей. Поэтому Go MatchString могла принять длинное значение с допустимым вариантом, а не только точное разрешённое значение.

Шаг 3

Возврат каретки меняет границу конфигурации

Аннотация включается в созданную конфигурацию NGINX. Серия исправлений устраняет как частичное совпадение, так и небезопасную обработку одиночных возвратов каретки при очистке конфигурации.

Шаг 4

Учётные данные контроллера раскрываются

Уведомление Kubernetes указывает, что внедрение команд может раскрыть учётные данные контроллера. В конфигурации по умолчанию эта service account имеет доступ ко всем Secrets; воздействие зависит от RBAC.

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

Это фрагменты upstream-исправлений CVE-2024-7646: PR #11719 добавляет якоря к разрешённым значениям auth-tls-verify-client, PR #11721 обрабатывает одиночные возвраты каретки в cleanConf. В ветку 1.10 вошли соответствующие изменения PR #11718 и #11722. Не относящийся к примеру контекст функции опущен.

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
// internal/ingress/annotations/authtls/main.go (до исправления)
authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`)

// internal/ingress/controller/template/template.go (до исправления)
switch c {
case ' ', '\t':
    needOutput = lineStarted
case '\n':
    needOutput = !(!lineStarted && emptyLineWritten)
    nextLineStarted = false
}
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
// internal/ingress/annotations/authtls/main.go (исправлено)
authVerifyClientRegex = regexp.MustCompile(`^(on|off|optional|optional_no_ca)$`)

// internal/ingress/controller/template/template.go (исправлено)
switch c {
case '\r':
    rest := in.Bytes()
    if len(rest) > 0 && rest[0] != '\n' {
        c = ' '
        needOutput = lineStarted
    }
case '\n':
    needOutput = !(!lineStarted && emptyLineWritten)
    nextLineStarted = false
}

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

Источники