FlawOpen

flawopen.com/Incidents/citrix-bleed-session-token-leak-october-2023

● Citrix · CVE-2023-4966 · October 2023 · CVSS 9.4 · Критический
Исследования · FlawOpen

Citrix Bleed: утечка токенов и обход MFA

CVE-2023-4966 раскрывала память NetScaler с токенами сессий, позволяя захватить сессию в обход MFA. Эксплуатация началась в конце августа 2023 года.[1], [2]

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

Вы просите помощника переписать сообщение на маленькую карточку и сказать, какой оно длины. На карточку помещается только 50 символов, и он пишет 50, но на вопрос о длине честно отвечает «800 символов», ведь столько было в оригинале. Вы отдаёте карточку кому-то и просите прочитать с неё 800 символов. Тот читает ваши 50 и продолжает читать всё, что написано на остальной части листа.[2]

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

snprintf
Функция C, форматирующая текст в буфер фиксированного размера. Если текст не помещается, она его обрезает, но возвращает длину, которую имел бы полный текст.[3]
NetScaler ADC / Gateway
Устройства Citrix для удалённого доступа и балансировки нагрузки на границе сети. Уязвимы были только те, что настроены как Gateway или виртуальный сервер AAA.[1]
Чтение за границей буфера (CWE-125)
Чтение за концом буфера. Ничего не перезаписывается, но раскрывается всё, что лежит в соседней памяти. Это описание конкретного механизма чтения за границей буфера (CWE-125); Citrix относит CVE-2023-4966 к общей категории CWE-119.[1], [3]
Токен сессии
Значение, подтверждающее, что пользователь уже вошёл в систему. Предъявивший его считается этим пользователем без запроса пароля или MFA.[2]

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

Хронология инцидента

2023-08

Начинается эксплуатация уязвимости как zero-day, согласно более поздним отчётам о реагировании на инциденты.[2]

2023-10-10

Citrix публикует бюллетень CTX579459 с исправленными сборками 14.1-8.50, 13.1-49.15 и 13.0-92.19.[1]

2023-10-18

CISA вносит CVE-2023-4966 в каталог Known Exploited Vulnerabilities.[4]

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

Шаг 1

Слишком длинный заголовок

Неаутентифицированный запрос с очень длинным заголовком Host попадает в код, который формирует на устройстве ответ OpenID Connect discovery.[2], [3]

Шаг 2

Длина понимается неверно

snprintf обрезает текст по размеру буфера, но возвращает полную предполагаемую длину, а код считает её числом записанных байтов.[3], [2]

Шаг 3

Отправляется память за буфером

Ответ отправляется с этой большей длиной, поэтому устройство вместе с обрезанным JSON возвращает соседнюю память.[2]

Шаг 4

Сессии перехватываются

В утекающей памяти могут оказаться действующие токены сессий. Повторно предъявив такой токен, злоумышленник получает сессию пользователя без пароля и MFA.[2], [1]

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

Иллюстративный код на C, а не исходный код NetScaler. Исправление проверяет возвращаемое значение snprintf — длину, которую имел бы полный результат. Если значение отрицательное или не меньше размера буфера, форматирование завершилось ошибкой либо результат был усечён, поэтому функция ничего не отправляет. Следовательно, длина передаётся в send_response только тогда, когда полный ответ помещается в buf.[3], [2]

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    /* n — это число байтов, которые были бы записаны; оно может превышать sizeof(buf) */
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    return send_response(buf, n);   /* отправляет n байтов, читая за пределами buf */
}
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    if (n < 0 || (size_t)n >= sizeof(buf))
        return -1;                  /* отклонить усечённый результат; не отправлять данные за границами буфера */
    return send_response(buf, n);
}

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

Источники