Ekibim yakın zamanda keşfetti
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.
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.
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:
#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.
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.
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.
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
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); } }
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
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.