FlawOpen

flawopen.com/Teardowns/cve-2023-6246-glibc-syslog-heap-buffer-overflow

● CVE-2023-6246 · CVSS 7.8 · Высокий
Исследования · FlawOpen

CVE-2023-6246: переполнение кучи в glibc syslog

CVE-2023-6246 — переполнение кучи в syslog glibc из-за длинного заголовка. Установите исправленный пакет glibc из вашего дистрибутива.

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

Представьте сотрудника, который измеряет посылку вместе с наклейкой короткой линейкой. Наклейка не помещается, поэтому расчёт не учитывает нужное место. Сотрудник заказывает крошечную коробку и пытается напечатать на ней всю наклейку и содержимое. В 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.

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

Шаг 1

Локальное условие

Локальный процесс должен вызвать syslog() или vsyslog() через программу с уязвимой glibc. Для влияния на безопасность нужен привилегированный вызывающий процесс и путь, не заменяющий префикс имени программы безопасным ограниченным идентификатором.

Шаг 2

Длинный заголовок

Заголовок превышает начальный буфер|SYSLOG_HEADER включает длинное имя программы (__progname, полученное из basename argv[0]) или длинный идентификатор openlog(). По данным Qualys, значение должно превышать 1 024 байта.

Шаг 3

Пропуск расчёта

Пропускается ветка расчёта размера|Если заголовок не помещается в стековый буфер на 1 024 байта, уязвимый код пропускает ветку, вычисляющую bufsize. В последующем выделении в куче недостаточно места для заголовка и форматированного сообщения.

Шаг 4

Повреждение памяти

Запись выходит за границы выделения|Журналирование записывает заголовок и сообщение в слишком маленький буфер, повреждая кучу. 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;

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

Источники