मेरी टीम ने हाल ही में खोजा
टीम नई कमजोरियों और दुर्भावनापूर्ण पैकेजों को खोजने के लिए ओपन-सोर्स परियोजनाओं की लगातार निगरानी करती है और उनकी समग्र सुरक्षा स्थिति को बेहतर बनाने में मदद करने के लिए उन्हें व्यापक समुदाय के साथ साझा करती है।
यह 2-भाग ब्लॉग श्रृंखला कमजोर Xpm फ़ाइल प्रारूप की आंतरिक कार्यप्रणाली का विवरण प्रदान करती है और इन कमजोरियों का फायदा उठाने के बारे में गहराई से बताती है।
Xorg X11, जिसे अक्सर X विंडो सिस्टम के रूप में जाना जाता है, एक ओपन-सोर्स ग्राफिकल सर्वर प्रोटोकॉल है जो यूनिक्स जैसे ऑपरेटिंग सिस्टम में ग्राफिकल यूजर इंटरफेस के निर्माण और प्रबंधन को सक्षम बनाता है। यह ग्राफ़िकल एप्लिकेशन चलाने, विंडोज़ प्रबंधित करने और नेटवर्क वाले वातावरण में उपयोगकर्ता इनपुट को संभालने के लिए एक रूपरेखा प्रदान करता है।
Libx11 पैकेज आवश्यक साझा लाइब्रेरी प्रदान करता है जो क्लाइंट एप्लिकेशन को आपके डेस्कटॉप पर डेटा प्रस्तुत करने और प्रस्तुत करने के लिए आवश्यक होती है।
libXpm X Pixmap (XPM) प्रारूप में छवियों को पढ़ने, लिखने और प्रदर्शित करने के कार्य प्रदान करता है।
एक्सपीएम का मुख्य उद्देश्य पारदर्शी पिक्सल के समर्थन के साथ आइकन पिक्समैप्स उत्पन्न करना है। यह XBM सिंटैक्स पर आधारित है, और या तो XPM2 प्रारूप में एक सादा पाठ फ़ाइल हो सकती है या C प्रोग्रामिंग भाषा सिंटैक्स का उपयोग कर सकती है, जो इसे C प्रोग्राम फ़ाइल में शामिल करने के लिए उपयुक्त बनाती है।
एक्सपीएम छवि प्रारूप - संस्करण
पूर्ववर्ती - एक्सबीएम
1989 में XPM (X PixMap) आने से पहले, XBM प्रारूप (X BitMap) था।
एक सादा-पाठ बाइनरी छवि प्रारूप, जिसका उपयोग एक्स जीयूआई में उपयोग किए गए आइकन और कर्सर बिटमैप्स को संग्रहीत करने के लिए किया जाता है।
XBM फ़ाइलें C स्रोत फ़ाइलों के रूप में संरचित हैं। यह आज के अधिकांश छवि प्रारूपों से उनका मुख्य अंतर है। इस तरह, उन्हें सीधे अनुप्रयोगों में शामिल किया जा सकता है। हालाँकि, इस और इस तथ्य के कारण कि कोई संपीड़न नियोजित नहीं किया जा सकता है (1 वर्ण से 1-बाइट मैपिंग), वे अपने कच्चे पिक्सेल डेटा से भी कहीं बड़े हैं।
एक्स बिटमैप (एक्सबीएम) डेटा में स्थिर अहस्ताक्षरित चार सरणियों का एक क्रम शामिल होता है जो कच्चे मोनोक्रोम पिक्सेल जानकारी को संग्रहीत करता है।
उदाहरण के लिए, निम्न C कोड छवि में hello
के लिए XBM फ़ाइल है:
#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
कथन होते हैं। मानों की पहली जोड़ी बिटमैप के पिक्सेल आयामों को परिभाषित करती है, जो इसकी ऊंचाई और चौड़ाई को दर्शाती है।
एक्स बिटमैप (एक्सबीएम) छवि डेटा को पिक्सेल मानों के निरंतर अनुक्रम के रूप में संरचित किया जाता है, जो एक स्थिर सरणी के भीतर संग्रहीत होते हैं। यह देखते हुए कि प्रत्येक पिक्सेल को एक बिट द्वारा दर्शाया गया है (0 सफेद को दर्शाता है और 1 काले को दर्शाता है), सरणी में प्रत्येक बाइट आठ अलग-अलग पिक्सेल के लिए डेटा को समाहित करता है। विशेष रूप से, पहले बाइट का सबसे कम महत्वपूर्ण बिट बिटमैप के भीतर ऊपरी-बाएँ पिक्सेल के लिए एंकर के रूप में कार्य करता है।
XPM संस्करण से मिलें
पहली बार 1989 में रिलीज़ किया गया, इसमें ऊपर उल्लिखित XBM प्रारूप के साथ बहुत सारी समानताएँ हैं। जबकि XBM मोनोक्रोम था, XPM1 ने छवियों में रंग पेश किए।
यह रंगों के लिए अतिरिक्त मैक्रोज़ और वेरिएबल्स का उपयोग करता है और पिक्सेल डेटा के लिए बाइट्स को वर्णों से बदल देता है।
उदाहरण के लिए, यहाँ XPM1 प्रारूप में वही 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 ", " ", " ", " ", " ", " ", " ", " ", " " };
यहां XFACE_ncolors
छवि में रंगों की संख्या को दर्शाता है, और XFACE_chars_per_pixel
प्रति पिक्सेल वर्णों की संख्या को दर्शाता है।
प्रत्येक a
सफेद रंग "#ffffff" से बदल दिया जाएगा, और प्रत्येक b
काले रंग "#000000" से बदल दिया जाएगा।
एक साल बाद, 1990 में, चीजों को बेहतर बनाने के लिए प्रारूप का दूसरा संस्करण तैयार किया गया। इसने चीजों को सरल बना दिया और छवि प्रारूप से सभी सी कोड हटा दिए।
सरलीकृत संरचना:
! XPM2 <Header> <Colors> <Pixels>
हेडर लाइन XPM1 के #define कथनों के समान छवि आयामों को दर्शाती है।
रंग अनुभाग वर्ण मानों को परिभाषित करता है।
एक नई प्रकार की अवधारणा पेश की गई:
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
पारदर्शिता का संकेत नहीं देता है।
यह 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 ", " ", " ", " ", " ", " ", " ", " ", " " };
जैसा कि चर्चा की गई है, एक्सपीएम प्रारूप अधिक परिष्कृत छवियों का भी प्रतिनिधित्व कर सकता है, जैसे कि जेफ्रॉग लोगो:
DoS भेद्यता - CVE-2023-43786
CVE-2023-43786 भेद्यता अनिवार्य रूप से एक गलत रिकर्सन स्टॉप स्थिति गणना के परिणामस्वरूप उत्पन्न होने वाला एक अंतहीन लूप है।
निश्चित प्रतिबद्धता:
https://gitlab.freedesktop.org/xorg/lib/libx11/-/commit/204c3393c4c90a29ed6bef64e43849536e863a86
XPutImage
libX11 में एक फ़ंक्शन है जो आपको छवियों को X ड्रॉएबल, आमतौर पर एक X विंडो पर रखने की सुविधा देता है। इस फ़ंक्शन के साथ, कोई व्यक्ति 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] में परिभाषित वैकल्पिक कोड ब्लॉक में प्रवेश करना पड़ेगा।
इसके बाद यह [3] पर BytesPerRow
गणना करता है और फिर इसे 8 से विभाजित करता है। हमारे उदाहरण में: BytesPerRow = 90000 * 32 / 8 = 360,0
उदाहरण में, [4] पर चेक पास नहीं होगा, क्योंकि 360000 अनुरोधित आकार 262116 से कम नहीं है, और अनुरोधित चौड़ाई की एक भी पंक्ति को एक अनुरोध में फिट करने में सक्षम नहीं है - यह [5 पर else
आरंभ करता है ].
यह उन पिक्सेल की संख्या निर्धारित करता है जिन्हें एक अनुरोध में शामिल किया जा सकता है। इसके बाद यह केवल उस सबसेट को पास करने के लिए PutSubImage
फ़ंक्शन के लिए एक पुनरावर्ती कॉल शुरू करता है, इसके बाद लाइन के शेष भाग को प्रबंधित करने के लिए बाद में पुनरावर्ती कॉल करता है। यदि आवश्यक हो, तो इस शेष भाग को अतिरिक्त पुनरावर्ती कॉल के माध्यम से भी विभाजित किया जा सकता है।
हालाँकि, [6] पर गणना प्रति पिक्सेल बिट्स को ध्यान में रखने में विफल रहती है, और पुनरावर्ती कॉल 2096928 बिट्स के बजाय 2096928 पिक्सेल भेजने का अनुरोध करता है - जो कि एक अनुरोध में फिट होने से बड़ा है।
इससे पिक्सल की लाइन को विभाजित करने के प्रयास का एक अंतहीन चक्र शुरू हो जाता है, जिसके परिणामस्वरूप लगातार इतनी बड़ी संख्या होती है कि वह फिट नहीं हो पाती है और उसी मान के साथ पुनः प्रयास करने की प्रक्रिया फिर से शुरू हो जाती है। यह पुनरावृत्ति तब तक बनी रहती है जब तक कॉल स्टैक समाप्त नहीं हो जाता।
बग फिक्स ने [6] पर गणना बदल दी और bits_per_pixel
को ध्यान में रखा। उदाहरण में, इसके परिणामस्वरूप एक पुनरावर्ती कॉल होगी जिसमें केवल 65529 पिक्सेल भेजने का अनुरोध किया जाएगा, जिसके परिणामस्वरूप 262116 का बाइट्सपेरो होगा जो उपलब्ध स्थान के भीतर पूरी तरह से फिट होगा, इस प्रकार पुनरावर्ती को आगे बढ़ने और केवल 2 कॉल में समाप्त करने की अनुमति मिलेगी।
बग को ट्रिगर करने के लिए उदाहरण प्रमाण-अवधारणा छवि: 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
कॉल करता है।