flawopen.com/Teardowns/cve-2023-6246-glibc-syslog-heap-buffer-overflow
CVE-2023-6246: переполнение кучи в glibc syslog
CVE-2023-6246 — переполнение кучи в syslog glibc из-за длинного заголовка. Установите исправленный пакет glibc из вашего дистрибутива.
Представьте сотрудника, который измеряет посылку вместе с наклейкой короткой линейкой. Наклейка не помещается, поэтому расчёт не учитывает нужное место. Сотрудник заказывает крошечную коробку и пытается напечатать на ней всю наклейку и содержимое. В glibc длинный заголовок syslog мог оставить требуемый размер неопределённым, из-за чего журналирование выделяло слишком мало памяти в куче.
Ключевые понятия и термины
glibc- GNU C Library предоставляет основные функции C runtime, используемые большинством программ Linux.
__vsyslog_internal()- Внутренняя реализация glibc, которую
syslog()иvsyslog()используют для форматирования сообщения иSYSLOG_HEADERперед передачей системному журналу. SYSLOG_HEADER- Префикс приоритета и идентификатора перед сообщением syslog. Он может содержать имя программы или идентификатор, заданный через
openlog(). Heap-based buffer overflow (CWE-122)- Запись выходит за границы выделенного в куче буфера. Здесь расчёт не учитывал заголовок с длинным именем, не поместившийся в начальный фиксированный буфер.
Связи со стандартами
- Ссылка CWE для слабого места, описанного в этом руководстве: CWE-122
Анализ первопричины
В уведомлении glibc сказано, что __vsyslog_internal() не обновляла требуемый размер буфера при форматировании SYSLOG_HEADER с длинным именем программы. В уязвимом коде длина заголовка l могла быть не меньше стекового буфера размером 1 024 байта, поэтому пропускался участок, который форматировал тело сообщения и задавал bufsize. Последующее выделение в куче использовало неопределённый или нулевой размер, после чего заголовок и сообщение записывались в слишком маленькую область. По данным Qualys, длинное имя может быть basename управляемого вызывающим argv[0] или идентификатором openlog() длиннее 1 024 байт; уязвимая setuid-программа, журналирующая через этот путь, может сделать дефект значимым для безопасности. Qualys продемонстрировала локальное повышение привилегий через su в Fedora 38. Риск зависит от локального доступа, конфигурации и пути вызова; это не универсальная удалённая атака на syslog.
Пошаговый сценарий атаки
Локальное условие
Локальный процесс должен вызвать syslog() или vsyslog() через программу с уязвимой glibc. Для влияния на безопасность нужен привилегированный вызывающий процесс и путь, не заменяющий префикс имени программы безопасным ограниченным идентификатором.
Длинный заголовок
Заголовок превышает начальный буфер|SYSLOG_HEADER включает длинное имя программы (__progname, полученное из basename argv[0]) или длинный идентификатор openlog(). По данным Qualys, значение должно превышать 1 024 байта.
Пропуск расчёта
Пропускается ветка расчёта размера|Если заголовок не помещается в стековый буфер на 1 024 байта, уязвимый код пропускает ветку, вычисляющую bufsize. В последующем выделении в куче недостаточно места для заголовка и форматированного сообщения.
Повреждение памяти
Запись выходит за границы выделения|Журналирование записывает заголовок и сообщение в слишком маленький буфер, повреждая кучу. Qualys показала локальное повышение привилегий через su в Fedora 38; не переносите этот вывод на все приложения и дистрибутивы.
Исходный код: Уязвимый vs Защищённый вариант
Старая ветка оставляла bufsize равным нулю, если заголовок не помещался в стековый буфер; поэтому последующее выделение в куче могло быть слишком маленьким. Исправление помечает этот буфер непригодным, использует его для измерения тела сообщения, записывает l + vl и продолжает существующий путь выделения с нужным размером. Это целевые фрагменты upstream-кода, а не полные автономные функции.
Фрагмент исходного кода: misc/syslog.c
/* Фрагмент misc/syslog.c до коммита 6bd0e4efcc78. */
char bufs[1024];
char *buf = NULL;
size_t bufsize = 0;
...
if (0 <= l && l < sizeof bufs)
{
va_list apc;
va_copy (apc, ap);
int vl = __vsnprintf_internal (bufs + l, sizeof bufs - l, fmt, apc,
mode_flags);
if (0 <= vl && vl < sizeof bufs - l)
buf = bufs;
bufsize = l + vl;
va_end (apc);
}
/* Длинный заголовок пропускает расположенную выше ветку задания размера. */
/* Фрагмент misc/syslog.c после коммита 6bd0e4efcc78. */
char bufs[1024];
char *buf = bufs;
size_t bufsize;
...
if (0 <= l && l < sizeof bufs)
{
pos = bufs + l;
len = sizeof (bufs) - l;
}
else
{
buf = NULL;
pos = bufs;
len = sizeof (bufs);
}
vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags);
if (!(0 <= vl && vl < len))
buf = NULL;
bufsize = l + vl;
Чек-лист по защите системы для инженеров
- ✓Обновите glibc до исправленного пакета для вашей версии дистрибутива; не определяйте статус пакета только по upstream-версии.
- ✓Сверьтесь с бюллетенем дистрибутива: исправления upstream — glibc 2.39, 2.38-42, 2.37-57 и 2.36-126.
- ✓Найдите привилегированные программы, вызывающие
syslog()илиvsyslog(), и проверьте, могут ли управляемые имена программ или идентификаторыopenlog()попасть в заголовок. - ✓При форматировании журналов учитывайте полный размер заголовка и тела даже при нехватке начального фиксированного буфера; добавьте тест длинного имени программы и проверьте арифметику размера выделения.