
रूट-कारण विश्लेषण और AddressSanitizer तथा GDB का उपयोग करके libtiff में CVE-2023-52356 का पैच सत्यापन।
यह लैब libtiff में CVE-2023-52356 का विश्लेषण करता है, जो TIFFReadRGBATileExt के व्यवहार पर केंद्रित है जब इसे मान्य छवि सीमाओं के बाहर के छवि निर्देशांक प्राप्त होते हैं।
विश्लेषण में मूल क्रैश का पुनरुत्पादन, AddressSanitizer और GDB के साथ डिबगिंग, मूल-कारण विश्लेषण, अपस्ट्रीम पैच की जांच, मूल रिप्रोड्यूसर का विश्लेषण, और फिक्स्ड संस्करण का सत्यापन शामिल है।
4d0329a451558511triger_input_47मूल रिप्रोड्यूसर को AddressSanitizer सक्षम के साथ कमजोर libtiff बिल्ड के विरुद्ध संकलित किया गया था।
रिप्रोड्यूसर को प्रदान की गई ट्रिगर फ़ाइल के साथ निष्पादित किया गया:
LD_LIBRARY_PATH="$PWD/libtiff/build-asan/libtiff" ./poc triger_input_47
निष्पादन ने एक अमान्य मेमोरी रीड के कारण होने वाले सेगमेंटेशन फॉल्ट को पुनरुत्पादित किया। AddressSanitizer स्टैक ट्रेस ने TIFFReadRGBATileExt से कॉल किए गए memmove ऑपरेशन के दौरान विफलता की पहचान की।
GDB का उपयोग करके, क्रैश स्थिति को निम्नलिखित रनटाइम मानों के साथ पुनरुत्पादित किया गया:
row = 34
img.height = 33
tile_ysize = 1
कमजोर कोड ने तब निष्पादित किया:
read_ysize = img.height - row;
देखे गए मानों के साथ, यह गणना है:
33 - 34 = -1
क्योंकि read_ysize अहस्ताक्षरित (unsigned) है, परिणाम लपेटकर बन गया:
read_ysize = 4294967295
जो UINT32_MAX है।
read_ysize का मान बाद में memmove के लिए स्रोत पॉइंटर गणना में उपयोग किया गया।
विफल memmove से ठीक पहले, GDB ने दिखाया:
read_ysize = 4294967295
read_xsize = 1
tile_ysize = 1
tile_xsize = 1
i_row = 0
raster = 0x7d0ff67e2d40
गंतव्य पॉइंटर का मूल्यांकन हुआ:
0x7d0ff67e2d40
जो raster बफर की शुरुआत थी।
स्रोत पॉइंटर का मूल्यांकन हुआ:
0x7d13f67e2d38
गणना किया गया स्रोत पॉइंटर raster बफर की शुरुआत से 17179869176 बाइट्स, लगभग 16 GiB, आगे था।
GDB में memmove निष्पादित करने से परिणाम मिला:
SIGSEGV, Segmentation fault
बैकट्रेस ने निम्नलिखित क्रैश पथ दिखाया:
__sanitizer_internal_memmove
__asan_memmove
TIFFReadRGBATileExt at tif_getimage.c:3345
LLVMFuzzerTestOneInput at poc.cc:61
यह पुष्टि करता है कि read_ysize में अहस्ताक्षरित अंडरफ्लो ने सीमा से बाहर का स्रोत ऑफसेट उत्पन्न किया। परिणामी अमान्य स्रोत पॉइंटर का उपयोग तब memmove द्वारा किया गया, जिससे अमान्य मेमोरी रीड और सेगमेंटेशन फॉल्ट हुआ।
अपस्ट्रीम फिक्स ने कमजोर read_ysize गणना से पहले एक स्पष्ट सीमा जांच जोड़ी:
if (col >= img.width || row >= img.height)
{
TIFFErrorExtR(tif, TIFFFileName(tif),
"Invalid row/col passed to TIFFReadRGBATile().");
TIFFRGBAImageEnd(&img);
return (0);
}
फिक्स्ड संस्करण पर GDB का उपयोग करके, निम्नलिखित रनटाइम मान देखे गए:
row = 34
img.height = 33
col = 0
img.width = 2047
इन मानों के लिए, नई सत्यापन शर्त सत्य का मूल्यांकन करती है क्योंकि:
row >= img.height
34 >= 33
इसलिए फ़ंक्शन ने रिपोर्ट किया:
Invalid row/col passed to TIFFReadRGBATile()
और 0 लौटाया।
परिणामस्वरूप, निष्पादन कमजोर गणना तक नहीं पहुंचा:
read_ysize = img.height - row;
यह कमजोर संस्करण में देखे गए अहस्ताक्षरित अंडरफ्लो को रोकता है और अमान्य मान को बाद की memmove स्रोत पॉइंटर गणना में उपयोग होने से रोकता है।
मूल रिप्रोड्यूसर Y अक्ष के साथ टाइलों की संख्या की गलत गणना करता है।
प्रासंगिक कोड TIFFGetField() के रिटर्न मान को सीधे TIFFComputeTile() में Y निर्देशांक के रूप में पास करता है:
TIFFComputeTile(
in_tif,
0,
TIFFGetField(in_tif, TIFFTAG_IMAGELENGTH, &tile_height),
0,
0)
GDB का उपयोग करके, tile_height में निम्नलिखित देखा गया:
tile_height = 33
हालाँकि, TIFFGetField() ने लौटाया:
1
रिटर्न मान सफलता को इंगित करता है; यह छवि ऊंचाई नहीं है। इसलिए, कॉल प्रभावी रूप से बन जाती है:
TIFFComputeTile(in_tif, 0, 1, 0, 0)
इस TIFF के लिए, GDB ने दिखाया कि यह कॉल लौटाता है:
2047
यह मान एक टाइल इंडेक्स है, Y अक्ष के साथ टाइलों की संख्या नहीं।
मूल रिप्रोड्यूसर ने तब इस मान का उपयोग अपनी num_tiles_y की गणना में किया, जिसके परिणामस्वरूप:
num_tiles_y = 63
हालाँकि, छवि आयाम और टाइल आयाम हैं:
image_width = 2047
image_height = 33
tile_width = 1
tile_height = 1
इसलिए, Y अक्ष के साथ टाइलों की सही संख्या है:
num_tiles_y = 33
गलत मान 63 के कारण लूप Y मानों के साथ 0 से 62 तक पुनरावृत्त करता है, भले ही छवि ऊंचाई 33 है और मान्य Y निर्देशांक केवल 0 से 32 हैं।
यह एक अमान्य मान जैसे:
row = 34
को TIFFReadRGBATileExt में पारित करने की अनुमति देता है।
कमजोर libtiff संस्करण ने अहस्ताक्षरित img.height - row गणना करने से पहले इस सीमा से बाहर के निर्देशांक को अस्वीकार नहीं किया। इसने रिप्रोड्यूसर द्वारा उत्पन्न अमान्य API इनपुट को मेमोरी-सुरक्षा विफलता बनने की अनुमति दी।
मेंटेनर-प्रदान किया गया सही किया गया रिप्रोड्यूसर छवि और टाइल आयामों से सीधे टाइलों की संख्या की गणना करता है।
उसी TIFF के लिए, सही गणना उत्पन्न करती है:
num_tiles_x = 2047
num_tiles_y = 33
सही किए गए रिप्रोड्यूसर का परीक्षण उसी कमजोर libtiff बिल्ड और उसी ट्रिगर फ़ाइल के विरुद्ध किया गया।
प्रोग्राम स्थिति 0 के साथ बाहर निकला, और कोई AddressSanitizer या SIGSEGV रिपोर्ट उत्पन्न नहीं हुई।
यह मेंटेनर के अवलोकन का समर्थन करता है कि मूल रिप्रोड्यूसर में एक गलत टाइल-गणना गणना शामिल है। हालाँकि, कमजोर libtiff संस्करण में अभी भी रक्षात्मक पंक्ति और स्तंभ सत्यापन का अभाव था, जिससे अमान्य API निर्देशांक मेमोरी-सुरक्षा विफलता का परिणाम बन सकते थे।
| रिप्रोड्यूसर | libtiff संस्करण | परिणाम |
|---|---|---|
| मूल PoC | कमजोर (4d0329a4) | memmove में अमान्य रीड और SIGSEGV |
| सही किया गया रिप्रोड्यूसर | कमजोर (4d0329a4) | निकास स्थिति 0, कोई ASan/SEGV नहीं |
| मूल PoC | फिक्स्ड (51558511) | अमान्य row/col अस्वीकृत, कोई ASan/SEGV नहीं |
TIFFReadRGBATileExt में क्रैश तब होता है जब एक सीमा से बाहर का छवि निर्देशांक कमजोर libtiff कार्यान्वयन तक पहुंचता है।
मूल रिप्रोड्यूसर में, एक गलत टाइल-गणना गणना के कारण num_tiles_y की गणना सही मान 33 के बजाय 63 के रूप में होती है। परिणामस्वरूप, रिप्रोड्यूसर मान्य छवि सीमा के बाहर की एक पंक्ति को TIFFReadRGBATileExt में पारित कर सकता है।
कमजोर libtiff संस्करण में, गणना से पहले पंक्ति और स्तंभ तर्कों को मान्य नहीं किया गया था:
read_ysize = img.height - row;
पुनरुत्पादित मामले के लिए, row 34 था जबकि img.height 33 था। क्योंकि read_ysize अहस्ताक्षरित है, घटाव अंडरफ्लो होकर UINT32_MAX बन गया। यह मान बाद में memmove के लिए स्रोत पॉइंटर गणना में उपयोग किया गया, जिससे अमान्य मेमोरी रीड और सेगमेंटेशन फॉल्ट उत्पन्न हुआ।
अपस्ट्रीम फिक्स इस गणना से पहले स्पष्ट पंक्ति और स्तंभ सीमा सत्यापन जोड़ता है। फिक्स्ड बिल्ड का परीक्षण पुष्टि करता है कि वही अमान्य निर्देशांक अंडरफ्लो और memmove होने से पहले अस्वीकार कर दिया जाता है।
मेंटेनर-प्रदान किए गए सही किए गए रिप्रोड्यूसर का भी उसी TIFF फ़ाइल का उपयोग करके कमजोर बिल्ड के विरुद्ध परीक्षण किया गया। इसने सही टाइल गणना की और बिना किसी AddressSanitizer या SIGSEGV रिपोर्ट के पूरा हुआ।
इसलिए, यह विश्लेषण यह स्थापित नहीं करता है कि API का सही उपयोग करने पर अकेले TIFF फ़ाइल मेमोरी-सुरक्षा विफलता को ट्रिगर करती है। मूल रिप्रोड्यूसर में एक गलत टाइल-गणना गणना शामिल है, जबकि ऐतिहासिक libtiff कार्यान्वयन में रक्षात्मक सीमा सत्यापन का अभाव था और परिणामी अमान्य API इनपुट को मेमोरी-सुरक्षा विफलता बनने की अनुमति दी।