paint-brush
Раскрытие 35-летней уязвимости в nix libX11: часть 1к@yairjfrog
339 чтения
339 чтения

Раскрытие 35-летней уязвимости в nix libX11: часть 1

к Yair Mizrahi18m2024/03/08
Read on Terminal Reader

Слишком долго; Читать

В этой серии блогов рассматриваются уязвимости безопасности, обнаруженные в X.Org libX11, а именно CVE-2023-43786 и CVE-2023-43787, изучаются тонкости формата файлов XPM и демонстрируется использование этих уязвимостей. Узнайте, как защитить свои системы, с помощью подробной информации и исправлений.
featured image - Раскрытие 35-летней уязвимости в nix libX11: часть 1
Yair Mizrahi HackerNoon profile picture
0-item


Моя команда недавно обнаружила две уязвимости безопасности в X.Org libX11 , широко популярные графические библиотеки — CVE-2023-43786 и CVE-2023-43787 (с высокой степенью опасности NVD CVSS 7.8 ). Эти уязвимости приводят к отказу в обслуживании и удаленному выполнению кода. Последние версии X11 содержат исправления этих уязвимостей.


Команда постоянно отслеживает проекты с открытым исходным кодом, чтобы находить новые уязвимости и вредоносные пакеты, и делится ими с более широким сообществом, чтобы помочь улучшить общий уровень безопасности.


В этой серии блогов, состоящей из двух частей, подробно рассказывается о внутренней работе уязвимого формата файлов Xpm и подробно рассматриваются способы использования этих уязвимостей.


Что такое libX11?

Xorg X11, часто называемый X Window System, представляет собой протокол графического сервера с открытым исходным кодом , который позволяет создавать и управлять графическими пользовательскими интерфейсами в Unix-подобных операционных системах. Он обеспечивает основу для запуска графических приложений, управления Windows и обработки пользовательского ввода в сетевой среде.


Пакет libx11 предлагает основные общие библиотеки, необходимые клиентским приложениям для рендеринга и представления данных на рабочем столе.


Клиент_Сервер X Архитектура


Окно Xterm — клиентское приложение X11.


Что такое libXpm?

libXpm предоставляет функции для чтения, записи и отображения изображений в формате X Pixmap (XPM).

XPM в первую очередь нацелен на создание растровых изображений значков с поддержкой прозрачных пикселей. Он основан на синтаксисе XBM и может представлять собой либо простой текстовый файл в формате XPM2, либо использовать синтаксис языка программирования C, что делает его пригодным для включения в файл программы C.


Формат изображения XPM – версии

Предшественник — XBM


До появления XPM (X PixMap) в 1989 году существовал формат XBM (X BitMap).


Формат двоичного изображения в виде простого текста, используемый для хранения растровых изображений значков и курсоров, которые использовались в графическом интерфейсе X.


Файлы XBM имеют структуру исходных файлов C. В этом их главное отличие от большинства современных форматов изображений. Таким образом, их можно напрямую включать в приложения. Однако из-за этого, а также из-за невозможности использования сжатия (преобразование 1 символа в 1 байт), они также намного больше, чем их необработанные пиксельные данные.


Данные X BitMap (XBM) содержат последовательность статических массивов беззнаковых символов, в которых хранится необработанная информация о монохромных пикселях.


Например, следующий код C представляет собой файл XBM для hello на изображении:

привет, изображение Xbm, показанное в XnView

 #define hello_width 35 #define hello_height 25 static char hello_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x88, 0x00, 0x00, 0x10, 0x00, 0x88, 0x00, 0x00, 0x10, 0x00, 0x88, 0x00, 0x00, 0xD0, 0xE1, 0x88, 0x78, 0x00, 0x30, 0x13, 0x89, 0xC4, 0x00, 0x10, 0x12, 0x89, 0x84, 0x00, 0x10, 0xF2, 0x89, 0x84, 0x00, 0x10, 0x12, 0x88, 0x84, 0x00, 0x10, 0x12, 0x88, 0x44, 0x00, 0x10, 0xE2, 0x89, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

Вместо обычных заголовков формата файла изображения в файлах XBM есть операторы #define . Первая пара значений определяет размеры растрового изображения в пикселях, указывая его высоту и ширину.


Данные изображения X BitMap (XBM) структурированы как непрерывная последовательность значений пикселей, которые хранятся в статическом массиве. Учитывая, что каждый пиксель обозначается одним битом (0 соответствует белому цвету, а 1 — черному), каждый байт массива содержит данные для восьми отдельных пикселей. Примечательно, что младший бит первого байта служит привязкой для верхнего левого пикселя растрового изображения.


ХРМ1

Встречайте версию XPM


Впервые выпущенный в 1989 году, он имеет много общего с форматом XBM, описанным выше. В то время как XBM был монохромным, XPM1 придавал изображениям цвета.


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


Например, вот та же hello -белая картинка в формате XPM1:


 /* XPM */ static char *_hello[] = { /* columns rows colors chars-per-pixel */ "35 25 2 1 ", " c white", "bc black", /* pixels */ " ", " ", " ", " ", " ", " ", " ", " bbb ", " bbb ", " bbb ", " b bbb bbb bb bbbb ", " bb bb bbbbb bb ", " bbbbbbbb ", " bb bbbbb bbbb ", " bbbbbbb ", " bbbbbbb ", " bb bbbb bb bbb ", " ", " ", " ", " ", " ", " ", " ", " " };

XFACE_ncolors здесь обозначает количество цветов в изображении, а XFACE_chars_per_pixel обозначает количество символов на пиксель.


Каждый a будет заменен белым цветом «#ffffff», а каждый b будет заменен черным цветом «#000000».


ХРМ2

Год спустя, в 1990 году, была выпущена вторая версия формата, призванная улучшить ситуацию. Это упростило ситуацию и удалило весь код C из формата изображения.


Упрощенная структура:

 ! XPM2 <Header> <Colors> <Pixels>


Строка заголовка обозначает размеры изображения, аналогичные операторам #define XPM1.

Раздел цветов определяет значения символов.


Была введена новая концепция типа:

c – для цветного пикселя

m – для монохромного

g – лед для оттенков серого

s – символическое


Символьная функция используется для назначения цветов в зависимости от контекста и создания для них имен переменных для облегчения чтения.


Пример изображения XPM2 с тем же hello , что и в предыдущих примерах:

 ! XPM2 35 25 2 1 ac #000000 bc #ffffff aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaabaaaaaaaaaaaaaabaaabaaaaaaaaaaa aaaabaaaaaaaaaaaaaabaaabaaaaaaaaaaa aaaabaaaaaaaaaaaaaabaaabaaaaaaaaaaa aaaababbbaaaabbbaaabaaabaaabbbbaaaa aaaabbaabbaabaaabaabaaabaabaaabbaaa aaaabaaaabaabaaabaabaaabaabaaaabaaa aaaabaaaabaabbbbbaabaaabaabaaaabaaa aaaabaaaabaabaaaaaabaaabaabaaaabaaa aaaabaaaabaabaaaaaabaaabaabaaabaaaa aaaabaaaabaaabbbbaabaaabaaabbbaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Помимо шестнадцатеричных кодов цветов, цвета также могут быть указаны с использованием любого из названий цветов X11 (например, red ), где None указывает на прозрачность.


ХРМ3

Это текущая версия формата XPM.


Версия, выпущенная в 1991 году, вернула код C, но вместо использования чистого стиля кода C значения внутри по существу такие же, как и в формате XPM2.


Пример:

 /* XPM */ static char *_hello[] = { /* columns rows colors chars-per-pixel */ "35 25 2 1 ", " c white", "bc black", /* pixels */ " ", " ", " ", " ", " ", " ", " ", " bbb ", " bbb ", " bbb ", " b bbb bbb bb bbbb ", " bb bb bbbbb bb ", " bbbbbbbb ", " bb bbbbb bbbb ", " bbbbbbb ", " bbbbbbb ", " bb bbbb bb bbb ", " ", " ", " ", " ", " ", " ", " ", " " };


Как уже говорилось, формат XPM также может представлять более сложные изображения, такие как логотип JFrog:

Логотип JFrog, изображенный с помощью XPM3


DoS-уязвимость — CVE-2023-43786.

Уязвимость CVE-2023-43786 по сути представляет собой бесконечный цикл, возникающий из-за неправильного расчета условия остановки рекурсии.


Исправлена фиксация:

https://gitlab.freedesktop.org/xorg/lib/libx11/-/commit/204c3393c4c90a29ed6bef64e43849536e863a86


XPutImage — это функция в libX11, которая позволяет размещать изображения в X Drawable, обычно в X Window. С помощью этой функции можно перенести информацию о пикселях из структуры XImage в назначенный объект рисования, например окно или растровое изображение, и расположить его по мере необходимости.


xpmCreatePixmapFromImage

Функция xpmCreatePixmapFromImage libXpm вызывает эту функцию XPutImage :

 void xpmCreatePixmapFromImage( Display *display, Drawable d, XImage *ximage, Pixmap *pixmap_return) { GC gc; XGCValues values; *pixmap_return = XCreatePixmap(display, d, ximage->width, ximage->height, ximage->depth); /* set fg and bg in case we have an XYBitmap */ values.foreground = 1; values.background = 0; gc = XCreateGC(display, *pixmap_return, GCForeground | GCBackground, &values); XPutImage(display, *pixmap_return, gc, ximage, 0, 0, 0, 0, ximage->width, ximage->height); XFreeGC(display, gc); }


В этой функции ximage — это отображаемые пиксельные данные исходного изображения, которые копируются в объект X Drawable (в данном случае pixmap_return ).


XPutImage

Вот функция XPutImage libX11:

 int XPutImage ( register Display *dpy, Drawable d, GC gc, register XImage *image, int req_xoffset, int req_yoffset, int x, int y, unsigned int req_width, unsigned int req_height){ ..... PutSubImage(dpy, d, gc, &img, 0, 0, x, y, (unsigned int) width, (unsigned int) height, dest_bits_per_pixel, dest_scanline_pad); UnlockDisplay(dpy); SyncHandle(); Xfree(img.data); return 0; } }
 LockDisplay(dpy); FlushGC(dpy, gc); PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, (unsigned int) width, (unsigned int) height, dest_bits_per_pixel, dest_scanline_pad);......... }


Он вызывает функцию PutSubImage :

 static void PutSubImage ( register Display *dpy, Drawable d, GC gc, register XImage *image, int req_xoffset, int req_yoffset, int x, int y, unsigned int req_width, unsigned int req_height, int dest_bits_per_pixel, int dest_scanline_pad) { int left_pad, BytesPerRow, Available; if ((req_width == 0) || (req_height == 0)) return; Available = ((65536 < dpy->max_request_size) ? (65536 << 2) : (dpy->max_request_size << 2)) - SIZEOF(xPutImageReq); if ((image->bits_per_pixel == 1) || (image->format != ZPixmap)) { [1] left_pad = (image->xoffset + req_xoffset) & (dpy->bitmap_unit - 1); BytesPerRow = (ROUNDUP((long)req_width + left_pad, dpy->bitmap_pad) >> 3) * image->depth; } else { [2] left_pad = 0; BytesPerRow = ROUNDUP((long)req_width * dest_bits_per_pixel, [3] dest_scanline_pad) >> 3; } if ((BytesPerRow * req_height) <= Available) { [4] PutImageRequest(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, req_width, req_height, dest_bits_per_pixel, dest_scanline_pad); } else if (req_height > 1) { int SubImageHeight = Available / BytesPerRow; if (SubImageHeight == 0) SubImageHeight = 1; PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, req_width, (unsigned int) SubImageHeight, dest_bits_per_pixel, dest_scanline_pad); PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset + SubImageHeight, x, y + SubImageHeight, req_width, req_height - SubImageHeight, dest_bits_per_pixel, dest_scanline_pad); } else { [5] int SubImageWidth = (((Available << 3) / dest_scanline_pad) [6] * dest_scanline_pad) - left_pad; PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, (unsigned int) SubImageWidth, 1, dest_bits_per_pixel, dest_scanline_pad); PutSubImage(dpy, d, gc, image, req_xoffset + SubImageWidth, req_yoffset, x + SubImageWidth, y, req_width - SubImageWidth, 1, dest_bits_per_pixel, dest_scanline_pad); } }


Подробности технической уязвимости

Давайте возьмем следующий пример изображения:


 Available [the requested size] = (65,536 * 4) - 28 = 262,116 bits_per_pixel = 32 width = 90,000 pixels height = 1 pixel


Поскольку bits_per_pixel изображения равен 32, условный оператор в [1] не пройдет, что приведет нас к входу в альтернативный блок кода, определенный в [2].


Затем он вычисляет BytesPerRow для [3] и затем делит его на 8. В нашем примере: BytesPerRow = 90000 * 32/8 = 360,0.


В примере проверка на [4] не пройдет, так как 360000 не меньше запрошенного размера 262116 и не может уместить одну строку запрошенной ширины в один запрос – это инициирует else на [5 ].


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


Однако расчет в [6] не учитывает количество битов на пиксель, и рекурсивный вызов отправляет запросы, отправляющие 2096928 пикселей вместо 2096928 бит, что больше, чем может быть уложено в один запрос.


Это приводит к бесконечному циклу попыток разделить строку пикселей , что постоянно приводит к тому, что число становится слишком большим для размещения, и повторяется попытка повторить процесс с теми же значениями. Эта рекурсия сохраняется до тех пор, пока стек вызовов не будет исчерпан.


Исправление ошибки изменило расчет в [6] и приняло во внимание значение bits_per_pixel . В данном примере это приведет к рекурсивному вызову с запросом на отправку всего 65529 пикселей, в результате чего BytesPerRow будет равен 262116, что идеально вписывается в доступное пространство, что позволит рекурсии продолжить движение вперед и завершиться всего за два вызова.


Пример изображения для проверки концепции, вызывающего ошибку: https://github.com/jfrog/jfrog-CVE-2023-43786-libX11_DoS/blob/main/cve-2023-43786.xpm


Как может возникнуть ошибка

Примером приложения, которое вызывает уязвимую функцию библиотеки libXpm, является утилита CLI sxpm , которая используется для отображения изображений Xpm на экране.


Он вызывает уязвимую функцию xpmCreatePixmapFromImage Xpm, которая затем вызывает уязвимые функции libX11 XPutImage а затем PutSubImage .

xpmCreatePixmapFromImage_XPutImage_PutSubImage


Также опубликовано здесь.