Ekibim yakın zamanda keşfetti , yaygın olarak popüler olan grafik kitaplığı – (yüksek NVD önem derecesi 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. X.Org libX11'deki iki güvenlik açığı CVE-2023-43786 ve CVE-2023-43787 CVSS 7.8 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 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. açık kaynaklı 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. libXpm nedir? libXpm, görüntüleri (XPM) formatında okumak, yazmak ve görüntülemek için işlevler sağlar. X Pixmap 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 XBM dosyasıdır: hello #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 ifadeleri bulunur. İlk değer çifti, bitmap'in yüksekliğini ve genişliğini belirten piksel boyutlarını tanımlar. #define 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ı beyaz resim: hello /* 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 görüntüdeki renk sayısını belirtir ve piksel başına karakter sayısını belirtir. XFACE_ncolors XFACE_chars_per_pixel Her beyaz renk “#ffffff” ile, her ise siyah renk “#000000” ile değiştirilecektir. a b 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ı: – renkli piksel içindir c – monokrom içindir m – gri tonlama için buz g – sembolik içindir s 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ı sahip bir XPM2 görüntüsü örneği: 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 Onaltılı renk kodlarının yanı sıra renkler, X11 renk adlarından herhangi biri (örneğin ) kullanılarak da belirtilebilir; şeffaflığı belirtir. red None 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: 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 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. XPutImage xpmCreatePixmapFromImage libXpm işlevi şu işlevini çağırır: xpmCreatePixmapFromImage 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); } Bu fonksiyonda , görüntülenecek kaynak görüntü piksel verisidir ve nesnesine (bu durumda ) kopyalanır. ximage X Drawable pixmap_return XPutImage İşte libX11 işlevi: XPutImage 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);......... } işlevini çağırır: 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); } } 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ü 32 olduğundan, [1]'deki koşullu ifade geçmeyecektir, bu da bizi [2]'de tanımlanan alternatif kod bloğuna girmeye yönlendirecektir. bits_per_pixel Daha sonra [3]'te hesaplar ve 8'e böler. Örneğimizde: BytesPerRow = 90000 * 32 / 8 = 360,0 BytesPerRow Ö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 başlatır. ] else Bu, tek bir isteğe dahil edilebilecek piksel sayısını belirler. Daha sonra, yalnızca bu alt kümeyi iletmek için 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. PutSubImage 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, 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. piksel satırını bölme girişiminden oluşan sonsuz bir döngüye [6]'daki hesaplamayı değiştirdi ve 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. Hata düzeltmesi bits_per_pixel 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ı verilebilir. sxpm Güvenlik açığı bulunan Xpm işlevini çağırır ve bu işlev daha sonra güvenlik açığı bulunan libX11 işlevlerini ve ardından çağırır. xpmCreatePixmapFromImage XPutImage PutSubImage Burada da yayınlandı.