
CVE-2026-86476 के लिए प्रूफ़ ऑफ़ कॉन्सेप्ट: unixfd डीसेरियलाइज़ेशन के माध्यम से GStreamer GstVideoMeta हीप आउट-ऑफ़-बाउंड्स रीड/राइट, ASan रीप्रो और RCE चेन के साथ।
GStreamer के libgstvideo (gst-plugins-base) में एक हीप बफ़र ओवरफ़्लो। एक GstVideoMeta यह वर्णन करता है कि वीडियो फ़्रेम के पिक्सेल मेमोरी में कैसे व्यवस्थित होते हैं। जब उस वर्णन को किसी अविश्वसनीय unixfd पीयर से डीसेरियलाइज़ किया जाता है, तो कोई भी यह जाँच नहीं करता कि उसके द्वारा दावा की गई ज्यामिति उसे सहारा देने वाले बफ़र के भीतर फ़िट होती है या नहीं। एक पीयर अत्यधिक बड़े प्लेन स्ट्राइड सेट कर सकता है, और उपभोक्ता आवंटन से आगे पढ़ता या लिखता है।
| फ़ील्ड | मान |
|---|---|
| CVE | CVE-2026-86476 |
| वर्ग | CWE-125 (सीमा-से-बाहर पढ़ना), CWE-787 (सीमा-से-बाहर लिखना), CWE-502 (अविश्वसनीय डेटा का डीसेरियलाइज़ेशन) के माध्यम से पहुँचा गया |
| घटक | libgstvideo: gstvideometa.c, video-frame.c |
| प्रभाव | सीमा-से-बाहर पढ़ना (पुष्ट), सीमा-से-बाहर लिखना से कोड निष्पादन तक (प्रदर्शित) |
| ट्रिगर | गढ़े गए प्लेन स्ट्राइड वाला एक GstVideoMeta, जिसे किसी unixfd पीयर से डीसेरियलाइज़ किया गया |
| दोष स्थल | video_orc_convert_I420_BGRA, videoconvert ORC कर्नेल |
यह बग तीन अनुपस्थित जाँचें हैं जो मिलकर प्रभाव डालती हैं।
फ़्रेम मैप की मेटा शाखा में कोई आकार जाँच नहीं। video-frame.c में gst_video_frame_map_id() की दो शाखाएँ हैं। नो-मेटा शाखा if (frame->map[0].size < info->size) goto invalid_size; पर समाप्त होती है। मेटा शाखा, जो तब ली जाती है जब कोई GstVideoMeta मौजूद हो, केवल info->width <= meta->width और info->height <= meta->height लागू करती है। ये निचली सीमाएँ हैं। कोई भी प्लेन ज्यामिति की तुलना वास्तविक मेमोरी के आकार से नहीं करता।
default_map() प्रति प्लेन एक बाइट जाँचता है। gstvideometa.c में, प्लेन मेमोरी को gst_buffer_find_memory(buffer, offset, 1, ...) से हल किया जाता है। आकार तर्क 1 है। कोड पुष्टि करता है कि प्लेन के प्रारंभिक ऑफ़सेट पर एक बाइट मौजूद है, फिर स्ट्राइड को जैसा है वैसा ही कॉपी कर लेता है। प्लेन की वास्तविक सीमा, stride * height, की तुलना कभी आवंटन से नहीं की जाती। जाँच के ठीक ऊपर एक FIXME use plane size टिप्पणी है।
डीसेरियलाइज़र किसी भी चीज़ की रेंज-जाँच नहीं करता। video_meta_deserialize() केवल यह सत्यापित करता है कि बाइट रीडर का इनपुट समाप्त नहीं हुआ, कि n_planes <= GST_VIDEO_MAX_PLANES, और कि version != 0। ऑफ़सेट और स्ट्राइड सीधे स्ट्रीम से कॉपी किए जाते हैं। प्रत्येक स्ट्राइड को एक साइन्ड gint32 के रूप में पढ़ा जाता है, इसलिए एक ऋणात्मक स्ट्राइड स्वीकार किया जाता है।
प्राप्त करने वाली ओर, gst_unix_fd_src_create() मेटास को एक खाली बफ़र के विरुद्ध डीसेरियलाइज़ करता है और बाद में पीयर की मेमोरी संलग्न करता है। मेटा जो स्ट्राइड ले जाता है और बफ़र को सहारा देने वाली मेमोरी के आकार की तुलना कभी नहीं की जाती।
एक शत्रुतापूर्ण पीयर 320x240 I420 फ़्रेम के लिए एक GstVideoMeta सीरियलाइज़ करता है जिसमें प्रत्येक प्लेन स्ट्राइड 1000000 पर सेट है। उपभोक्ता इसे बिना किसी रेंज जाँच के डीसेरियलाइज़ करता है, छोटी वास्तविक मेमोरी संलग्न करता है, और फ़्रेम को मैप करता है। default_map() संतुष्ट हो जाता है क्योंकि प्रत्येक प्लेन का पहला बाइट मौजूद है। फिर videoconvert प्रत्येक इनपुट प्लेन को stride * height बाइट्स के लिए पढ़ता है और आवंटन से आगे बढ़ जाता है।
यह पढ़ना है। लिखना इसका दर्पण प्रतिबिंब है। gst_video_frame_copy_plane() stride * height बाइट्स का memcpy करता है, इसलिए जब झूठ बोलने वाला मेटा ले जाने वाला एक छोटा बफ़र किसी कॉपी का गंतव्य होता है, तो memcpy उसके सहारे से आगे लिखता है।
unixfd ट्रांसपोर्ट गैर-FD बफ़र्स को एक memfd में कॉपी करता है, इसलिए स्टॉक पाइपलाइन पढ़ने को एक mmap'd मैपिंग सीमा के विरुद्ध लाती है (क्रैश और सूचना लीक)। उसी डीसेरियलाइज़र को उन्हीं अविश्वसनीय बाइट्स के साथ हीप-समर्थित गंतव्य के विरुद्ध चलाना लिखने की प्राथमिकता का प्रयोग करता है। ट्रांसपोर्ट यह तय करता है कि ओवरफ़्लो किसी मैपिंग सीमा से टकराता है या हीप से। यह यह नहीं तय करता कि बग मौजूद है या नहीं।
हीप लिखने के साथ, प्रूफ़ ऑफ़ कॉन्सेप्ट एक पड़ोसी GstMiniObject के हेडर में free फ़ंक्शन पॉइंटर को अधिलेखित करता है, कमांड स्ट्रिंग को वहाँ रखता है जहाँ वह पहले तर्क रजिस्टर में पहुँचती है, और एक unref ट्रिगर करता है ताकि गढ़े गए पॉइंटर को system() के रूप में कॉल किया जाए।
कोई भी पाइपलाइन जो किसी ऐसी प्रक्रिया से unixfd के माध्यम से वीडियो बफ़र्स स्वीकार करती है जिस पर वह पूरी तरह भरोसा नहीं करती। सैंडबॉक्स किए गए मीडिया डिकोडर, पोर्टल-शैली की विभाजित पाइपलाइनें, और कोई भी चीज़ जो विशेषाधिकार या विश्वास सीमा (trust boundary) के पार फ़्रेम प्राप्त करने के लिए unixfdsrc का उपयोग करती है।
पुष्टि की गई:
libgstreamer-1.0.so.0.2804.0 (1.28.4-2) के विरुद्ध लिखने वाला पक्ष।unix सॉकेट पर दो प्रक्रियाएँ, केवल स्टॉक एलिमेंट्स।
fakesrc ! capsfilter ! unixfdsink, जो वास्तविक strides 320/160/160 वाला एक वैध 320x240 I420 बफ़र भेजता है, लेकिन एक GstVideoMeta के साथ जिसके plane strides 1000000 हैं।unixfdsrc ! videoconvert ! capsfilter ! fakesink, जहाँ capsfilter I420 को BGRA पर बाध्य करता है ताकि videoconvert को फ़्रेम को meta strides के अनुसार पढ़ना पड़े।| रन | Meta strides | परिणाम |
|---|---|---|
| 1 | 1000000 / 1000000 / 1000000 | video_orc_convert_I420_BGRA में ASan के अंतर्गत out-of-bounds read |
| 2 | 320 / 160 / 160 | सामान्य पूर्णता, बफ़र fakesink तक पहुँचता है |
पढ़ने वाले पक्ष के प्रमाण के लिए GStreamer को -Db_sanitize=address के साथ बनाएँ। RCE प्रूफ़ ऑफ़ कॉन्सेप्ट स्टॉक बिल्ड पर चलता है, क्योंकि ASan बिल्ड पहले ही अतिप्रवाहित (overflowing) memcpy पर निरस्त हो जाता है।
default_map() में, एक-बाइट वाले gst_buffer_find_memory(..., 1, ...) को stride * height plane विस्तार के विरुद्ध जाँच से बदलें।gst_video_frame_map_id() की meta शाखा को वही invalid_size जाँच दें जो no-meta शाखा में है।video_meta_deserialize() में, offsets और strides की बफ़र के विरुद्ध रेंज-जाँच करें, और ऋणात्मक (negative) strides को अस्वीकार करें।इनमें से कोई भी एक श्रृंखला को तोड़ देता है। तीनों मिलकर उस संरचना के लिए सही समाधान हैं जो एक विश्वास सीमा को पार करती है।