
CVE-2020-25578 और CVE-2020-25579: कुछ FreeBSD सूचना लीक बग्स जो मुझे 2020 में मिले।
ufs_create का ऑडिट करने में कुछ समय बिताया और 0 बग पाएstruct dirent ऑब्जेक्ट्स में पैडिंग बाइट्स के माध्यम से सूचना लीक होने के बारे में हैmsdosfs_readdir का पैच अधूरा है। उन्होंने बग के एक उदाहरण को ठीक किया, लेकिन दूसरे को नहीं।mqueuefs, autofs, smbfs, और tmpfs में वेरिएंट पाए जो मुझे पूर्ण 8 बाइट पॉइंटर लीक करने देते हैं। पुष्टि करने के लिए PoC लिखा।जैसा कि ऊपर बताया गया है, मूल बग जो मुझे मिला वह msdosfs_readdir में था जब मैं ऊपर लिंक किए गए कमिट के पैच का विश्लेषण कर रहा था।
FreeBSD पर readdir कॉल करने का मूल प्रवाह इस प्रकार है:
#include <dirent.h>
int main(void) {
struct dirent *dp;
DIR *dirp;
dirp = opendir("./somedir");
dp = readdir(dirp);
}
somedir जिस फ़ाइलसिस्टम में स्थित है, उसके आधार पर FreeBSD कर्नेल में कई *_readdir फ़ंक्शनों में से कोई भी कॉल किया जा सकता है।
ऊपर दिए गए पैच में dirent_terminate नामक एक फ़ंक्शन जोड़ा गया है जिसका उद्देश्य struct dirent ऑब्जेक्ट को यूज़रस्पेस में वापस भेजने से पहले कॉल करना है (अक्सर uiomove फ़ंक्शन का उपयोग करके किया जाता है)। यह फ़ंक्शन पैडिंग बाइट्स के साथ-साथ स्ट्रक्ट के d_name फ़ील्ड में शेष बाइट्स को शून्य कर देगा। struct dirent की परिभाषा यहाँ है।
पैच को देखते हुए, लाइन 1562 पर, आप देख सकते हैं कि dirent_terminate को dirbuf वेरिएबल के साथ तर्क के रूप में कॉल किया जा रहा है। इसके बाद, uiomove को dirbuf की सामग्री को वापस यूज़रस्पेस में कॉपी करने के लिए कॉल किया जाता है। हालांकि, ध्यान दें कि कोड की ये पंक्तियाँ इस if स्टेटमेंट के ब्लॉक के भीतर हैं। इस if स्टेटमेंट के ऊपर टिप्पणी बताती है कि यह शाखा केवल तभी ली जाती है जब readdir को MSDOS फ़ाइलसिस्टम के रूट पर कॉल किया जाता है, इसलिए हम फ़ाइलसिस्टम के रूट से आगे किसी भी उप-निर्देशिका में readdir कॉल करके इस if स्टेटमेंट को आसानी से छोड़ सकते हैं।
नीचे, हम लाइन 1691 पर uiomove का एक और कॉल देखते हैं। हालांकि, कोड को ध्यान से पढ़ने पर, आप देखेंगे कि इस उदाहरण में dirent_terminate को कॉल नहीं किया गया है, जिसका अर्थ है कि पैडिंग बाइट्स अप्रारंभित रहेंगी। दुर्भाग्य से, इस फ़ंक्शन की शुरुआत में d_name फ़ील्ड को शून्य कर दिया गया था (यहाँ), इसलिए हम बड़ा लीक नहीं प्राप्त कर सकते।
पहले, मेरे पास USB ड्राइव नहीं था, इसलिए मुझे MSDOS फ़ाइलसिस्टम माउंट करने का एक तरीका ढूंढना पड़ा। निम्नलिखित काम करता है:
$ dd if=/dev/zero of=test.img bs=512 count=256000
$ sudo mdconfig -a -t vnode -f test.img
$ sudo newfs_msdos -s 131072000 /dev/md1 # My mdconfig returned md1
$ mkdir ./temp
$ sudo mount -t msdosfs /dev/md1 ./temp
$ mkdir ./temp/test_dir
PoC original_poc.c में पाया जा सकता है। बस clang के साथ कंपाइल करें और इसे उपरोक्त कमांड के समान निर्देशिका से चलाएं, और आप लीक हुए बाइट्स को प्रिंट होते देखेंगे।
मैंने इसके वेरिएंट की तलाश शुरू की। मुझे लगता है कि मैंने बस uiomove\(&.*, के लिए grep किया, जिसमें लगभग 15-20 परिणाम मिले, और मैंने उन सभी को हाथ से जांचा। दुर्भाग्य से, FreeBSD में डिफ़ॉल्ट रूप से कोई भी वेरिएंट मौजूद नहीं है (फ़ाइलसिस्टम को मैन्युअल रूप से सक्षम / कर्नेल में कंपाइल किया जाना चाहिए)। वेरिएंट वाले फ़ंक्शन इस प्रकार हैं:
mqfs_readdirtmpfs_dir_getdotdenttmpfs_dir_getdotdotdentsmbfs_readvdirautofs_readdir_oneबग इन सभी फ़ंक्शनों में बिल्कुल समान है, इसलिए मैं सिर्फ mqfs_readdir को कवर करूंगा।
struct dirent entry आवंटित किया जाता हैd_name फ़ील्ड को शून्य करने के लिए dirent_terminate को कॉल किया जाता हैvfs_read_dirent को कॉल किया जाता है। यह फ़ंक्शन स्ट्रक्ट को यूज़रस्पेस में कॉपी करने के लिए uiomove को कॉल करेगाअब तक सब कुछ ठीक लग रहा है, है ना? जरूरी नहीं। हमें यह सुनिश्चित करना होगा कि स्ट्रक्ट के सभी फ़ील्ड प्रारंभ किए गए हैं। यदि आप कोड को ध्यान से देखते हैं, तो आप देखेंगे कि d_off फ़ील्ड अप्रारंभित छोड़ दिया गया है। इस फ़ील्ड का प्रकार off_t है, जो मूल रूप से एक int64_t है। जब स्ट्रक्ट को यूज़रस्पेस में कॉपी किया जाता है, तो हमें इस फ़ील्ड में अप्रारंभित डेटा मिलता है।
यही PoC सभी वेरिएंट के लिए काम करेगा, आपको बस इसे एक अलग फ़ाइलसिस्टम पर चलाना होगा। mqueuefs के लिए, निम्नलिखित करें (पहले mqueuefs को सक्षम / कर्नेल में कंपाइल करने की आवश्यकता है):
$ mkdir ./temp
$ sudo mount -t mqueuefs null ./temp
PoC स्वयं variants_poc.c में पाया जा सकता है। बस clang के साथ कंपाइल करें और इसे उपरोक्त कमांड के समान निर्देशिका से चलाएं। आप कर्नेल पॉइंटर्स को प्रिंट होते देखेंगे (संभवतः एक स्टैक पॉइंटर और एक कोड सेक्शन / हीप पॉइंटर, मैंने जांच नहीं की)।