paint-brush
Nix libX11'de 35 Yıllık Bir Güvenlik Açığı Ortaya Çıkarılıyor: Bölüm 1ile@yairjfrog
339 okumalar
339 okumalar

Nix libX11'de 35 Yıllık Bir Güvenlik Açığı Ortaya Çıkarılıyor: Bölüm 1

ile Yair Mizrahi18m2024/03/08
Read on Terminal Reader

Çok uzun; Okumak

Bu blog serisi, X.Org libX11'de bulunan CVE-2023-43786 ve CVE-2023-43787 güvenlik açıklarını ele alıyor, XPM dosya formatının inceliklerini araştırıyor ve bu güvenlik açıklarından nasıl yararlanıldığını gösteriyor. Sağlanan kapsamlı bilgiler ve düzeltmelerle sistemlerinizi nasıl koruyacağınızı öğrenin.
featured image - Nix libX11'de 35 Yıllık Bir Güvenlik Açığı Ortaya Çıkarılıyor: Bölüm 1
Yair Mizrahi HackerNoon profile picture
0-item


Ekibim yakın zamanda keşfetti X.Org libX11'deki iki güvenlik açığı , yaygın olarak popüler olan grafik kitaplığı – CVE-2023-43786 ve CVE-2023-43787 (yüksek NVD önem derecesi CVSS 7.8 ile). Bu güvenlik açıkları hizmet reddine ve uzaktan kod yürütülmesine neden olur. X11'in en son sürümleri bu güvenlik açıklarına yönelik düzeltmeler içerir.


Ekip, yeni güvenlik açıkları ve kötü amaçlı paketler bulmak için açık kaynak projelerini sürekli izliyor ve genel güvenlik duruşlarını iyileştirmeye yardımcı olmak için bunları daha geniş toplulukla paylaşıyor.


Bu 2 bölümlük blog serisi, güvenlik açığı bulunan Xpm dosya biçiminin iç işleyişine ilişkin ayrıntılar sağlıyor ve bu güvenlik açıklarından yararlanma konusunu derinlemesine ele alıyor.


LibX11 nedir?

Genellikle X Pencere Sistemi olarak anılan Xorg X11, Unix benzeri işletim sistemlerinde grafik kullanıcı arayüzlerinin oluşturulmasını ve yönetilmesini sağlayan açık kaynaklı bir grafik sunucu protokolüdür. Ağa bağlı bir ortamda grafik uygulamaları çalıştırmak, Windows'u yönetmek ve kullanıcı girişini yönetmek için bir çerçeve sağlar.


Libx11 paketi, istemci uygulamalarının masaüstünüzde veri oluşturmak ve sunmak için ihtiyaç duyduğu temel paylaşılan kitaplıkları sunar.


Client_Server X Mimarisi


Xterm penceresi - bir X11 istemci uygulaması


libXpm nedir?

libXpm, görüntüleri X Pixmap (XPM) formatında okumak, yazmak ve görüntülemek için işlevler sağlar.

XPM öncelikle şeffaf piksel desteğine sahip simge piksel haritaları oluşturmayı amaçlamaktadır. XBM sözdizimini temel alır ve XPM2 formatında düz bir metin dosyası olabilir veya bir C programlama dili sözdizimi kullanabilir, bu da onu bir C program dosyasına dahil edilmeye uygun hale getirir.


XPM görüntü formatı – versiyonlar

Önceki – XBM


XPM (X PixMap) 1989'da ortaya çıkmadan önce XBM formatı (X BitMap) vardı.


X GUI'de kullanılan simge ve imleç bit eşlemlerini depolamak için kullanılan düz metinli ikili görüntü formatı.


XBM dosyaları C kaynak dosyaları olarak yapılandırılmıştır. Bu, günümüzün çoğu görüntü formatından temel farkıdır. Bu şekilde doğrudan uygulamalara dahil edilebilirler. Bununla birlikte, bundan dolayı ve herhangi bir sıkıştırmanın kullanılamaması nedeniyle (1 karakterden 1 bayta eşleme), ham piksel verilerinden çok daha büyüktürler.


X BitMap (XBM) verileri, ham monokrom piksel bilgilerini depolayan bir dizi statik imzasız karakter dizisinden oluşur.


Örneğin aşağıdaki C kodu, görseldeki hello XBM dosyasıdır:

merhaba Xbm resmi XnView'da gösteriliyor

 #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 dosyalarında olağan görüntü dosyası formatı başlıkları yerine #define ifadeleri bulunur. İlk değer çifti, bitmap'in yüksekliğini ve genişliğini belirten piksel boyutlarını tanımlar.


X BitMap (XBM) görüntü verileri, statik bir dizi içinde depolanan sürekli bir piksel değerleri dizisi olarak yapılandırılmıştır. Her pikselin tek bir bit ile sembolize edildiği göz önüne alındığında (0 beyazı ve 1 siyahı temsil eder), dizideki her bayt sekiz ayrı piksele ait verileri kapsar. Özellikle, ilk baytın en az anlamlı biti, bitmap içindeki sol üst piksel için bağlantı görevi görür.


XPM1

XPM sürümüyle tanışın


İlk olarak 1989'da piyasaya sürülen bu format, yukarıda özetlenen XBM formatıyla pek çok benzerliğe sahiptir. XBM monokrom iken XPM1 görüntülere renk kattı.


Renkler için ek makrolar ve değişkenler kullanır ve piksel verileri için baytları karakterlerle değiştirir.


Örneğin, XPM1 formatındaki aynı hello beyaz resim:


 /* 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 ", " ", " ", " ", " ", " ", " ", " ", " " };

Buradaki XFACE_ncolors görüntüdeki renk sayısını belirtir ve XFACE_chars_per_pixel piksel başına karakter sayısını belirtir.


Her a beyaz renk “#ffffff” ile, her b ise siyah renk “#000000” ile değiştirilecektir.


XPM2

Bir yıl sonra, 1990'da, işleri iyileştirmek için formatın ikinci versiyonu belirlendi. İşleri basitleştirdi ve tüm C kodunu resim formatından kaldırdı.


Basitleştirilmiş yapı:

 ! XPM2 <Header> <Colors> <Pixels>


Başlık satırı, XPM1'in #define ifadelerine benzer görüntü boyutlarını belirtir.

Renkler bölümü karakter değerlerini tanımlar.


Yeni bir tip konsepti tanıtıldı:

c – renkli piksel içindir

m – monokrom içindir

g – gri tonlama için buz

s – sembolik içindir


Sembolik özellik, renkleri bağlama göre atamak ve daha kolay okunması için bunlara değişken adlar oluşturmak için kullanılır.


Önceki örneklerde gösterilenle aynı hello sahip bir XPM2 görüntüsü örneği:

 ! 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

Onaltılı renk kodlarının yanı sıra renkler, X11 renk adlarından herhangi biri (örneğin red ) kullanılarak da belirtilebilir; None şeffaflığı belirtir.


XPM3

Bu, XPM formatının güncel sürümüdür.


1991 yılında piyasaya sürülen sürüm, C kodunu geri getirdi ancak saf bir C kod stili kullanmak yerine içindeki değerler esasen XPM2 formatındaki değerlerle aynı.


Örnek:

 /* 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 ", " ", " ", " ", " ", " ", " ", " ", " " };


Bahsedildiği gibi, XPM formatı JFrog logosu gibi daha karmaşık görüntüleri de temsil edebilir:

XPM3 kullanılarak gösterilen JFrog logosu


DoS güvenlik açığı – CVE-2023-43786

CVE-2023-43786 güvenlik açığı aslında yanlış yineleme durdurma koşulu hesaplamasından kaynaklanan sonsuz bir döngüdür.


Sabit taahhüt:

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


XPutImage libX11'de görüntüleri bir X Drawable'a, genellikle bir X Penceresine yerleştirmenizi sağlayan bir işlevdir. Bu fonksiyonla, piksel bilgisi bir XImage yapısından pencere veya piksel haritası gibi belirlenmiş bir çizilebilir öğeye aktarılabilir ve gerektiğinde konumlandırılabilir.


xpmCreatePixmapFromImage

xpmCreatePixmapFromImage libXpm işlevi şu XPutImage işlevini çağırır:

 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); }


Bu fonksiyonda ximage , görüntülenecek kaynak görüntü piksel verisidir ve X Drawable nesnesine (bu durumda pixmap_return ) kopyalanır.


XPutImage

İşte XPutImage libX11 işlevi:

 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 işlevini çağırır:

 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); } }


Teknik Güvenlik Açığı Ayrıntıları

Aşağıdaki örnek görüntüyü ele alalım:


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


Görüntü bits_per_pixel 32 olduğundan, [1]'deki koşullu ifade geçmeyecektir, bu da bizi [2]'de tanımlanan alternatif kod bloğuna girmeye yönlendirecektir.


Daha sonra [3]'te BytesPerRow hesaplar ve 8'e böler. Örneğimizde: BytesPerRow = 90000 * 32 / 8 = 360,0


Örnekte, 360000, talep edilen 262116 boyutundan daha az olmadığından ve talep edilen genişlikte tek bir satırı tek bir talebe sığdıramadığından [4] üzerindeki kontrol geçmeyecektir - bu, [5'teki else başlatır. ]


Bu, tek bir isteğe dahil edilebilecek piksel sayısını belirler. Daha sonra, yalnızca bu alt kümeyi iletmek için PutSubImage işlevine özyinelemeli bir çağrı başlatır ve ardından satırın geri kalan kısmını yönetmek için bir sonraki özyinelemeli çağrı başlatır. Gerekirse, bu kalan kısım ayrıca ek yinelemeli çağrılar yoluyla daha da bölünebilir.


Bununla birlikte, [6]'daki hesaplama piksel başına bit sayısını hesaba katmaz ve özyinelemeli çağrı, tek bir isteğe sığabilecek olandan daha büyük olan 2096928 bit yerine 2096928 piksel gönderen isteklerde bulunur.


Bu, piksel satırını bölme girişiminden oluşan sonsuz bir döngüye yol açar, bu da sürekli olarak sığamayacak kadar büyük bir sayıyla sonuçlanır ve aynı değerlerle yeniden denemek için işlemin yeniden denenmesine neden olur. Bu yineleme, çağrı yığını bitene kadar devam eder.


Hata düzeltmesi [6]'daki hesaplamayı değiştirdi ve bits_per_pixel hesaba kattı. Örnekte, yalnızca 65529 piksel göndermeyi talep eden özyinelemeli bir çağrıyla sonuçlanacak, bu da mevcut alana mükemmel şekilde uyan 262116'lık bir BytesPerRow ile sonuçlanacak ve böylece özyinelemenin yalnızca 2 çağrıda ileri ilerleme kaydetmesine ve bitirmesine olanak tanıyacaktır.


Hatayı tetiklemek için örnek kavram kanıt görüntüsü: https://github.com/jfrog/jfrog-CVE-2023-43786-libX11_DoS/blob/main/cve-2023-43786.xpm


Hata nasıl tetiklenebilir?

Güvenlik açığı bulunan libXpm kitaplık işlevini çağıran uygulamaya örnek olarak, Xpm görüntülerini ekranda görüntülemek için kullanılan CLI yardımcı programı sxpm verilebilir.


Güvenlik açığı bulunan xpmCreatePixmapFromImage Xpm işlevini çağırır ve bu işlev daha sonra güvenlik açığı bulunan libX11 işlevlerini XPutImage ve ardından PutSubImage çağırır.

xpmCreatePixmapFromImage_XPutImage_PutSubImage