flawopen.com/Teardowns/cve-2024-26642-linux-nftables-anonymous-set-timeout
CVE-2024-26642: Linux nf_tables में टाइमआउट फ़्लैग वाला एनॉनिमस सेट
CVE-2024-26642 (NIST के अनुसार CVSS 7.8): nf_tables_newset() ऐसे एनॉनिमस सेट स्वीकार कर लेता था जिन पर टाइमआउट फ़्लैग भी लगा हो, जबकि कोई यूज़रस्पेस टूल यह जोड़ी नहीं बनाता; इससे किसी नियम से बँधे सेट एलिमेंट की समय-सीमा और गार्बेज कलेक्शन के दायरे में आ जाते थे। Linux 6.8 इसे -EOPNOTSUPP से ठुकरा देता है।
एक क्लोकरूम में दो व्यवस्थाएँ हैं। नंबर वाले हुक एक शाम के लिए सिर्फ़ एक मेहमान के होते हैं और मेहमान के जाते ही खाली कर दिए जाते हैं। समय वाली पर्चियों को एक कर्मचारी हर घंटे देखता है और जिनका समय पूरा हो गया उन्हें हटा देता है। फ़ॉर्म एक ही कोट के लिए दोनों खाने टिक करने देता है, तो वह हुक मेहमान के जाने पर भी खाली होता है और कर्मचारी के चक्कर पर भी। दो लोग मानते हैं कि वही हुक उनकी ज़िम्मेदारी है, और किसी को दूसरे का समय-सारणी नहीं पता। सुधार सीधा है: फ़ॉर्म अब यह जोड़ी स्वीकार नहीं करता।
इस पेज के मुख्य शब्द
nf_tables set- कर्नेल की डेटा संरचना जिसमें पते, पोर्ट या दूसरी कुंजियाँ होती हैं जिनसे nftables नियम मिलान करते हैं; यह
NFT_MSG_NEWSETnetlink संदेश से बनती है औरnf_tables_newset()इसे संभालता है। एनॉनिमस सेट (NFT_SET_ANONYMOUS)- बिना नाम का सेट, जैसे नियम के भीतर लिखा
{ 22, 80 }। यह उसी एक नियम से बँधा होता है और उसी ट्रांज़ैक्शन में नियम के साथ नष्ट होता है। NFT_SET_TIMEOUT- वह फ़्लैग जो सेट के एलिमेंट को समाप्ति समय देता है। समय पूरा होने पर एलिमेंट सेट का गार्बेज कलेक्शन तंत्र हटाता है।
NFT_SET_EVAL- पैकेट पाथ से अपडेट होने वाले सेट का फ़्लैग, जिसे पुराने
meterस्टेटमेंट इस्तेमाल करते हैं। सुधार उनके लिए एनॉनिमस + टाइमआउट + eval को अब भी वैध रखता है। यूज़र नेमस्पेस में CAP_NET_ADMIN- nf_tables अपने नेटवर्क नेमस्पेस में CAP_NET_ADMIN रखने वाली किसी भी प्रोसेस का कॉन्फ़िगरेशन मान लेता है; जहाँ डिस्ट्रीब्यूशन अनुमति दे, वहाँ बिना विशेषाधिकार वाला उपयोगकर्ता यूज़र नेमस्पेस बनाकर यह पा सकता है।
मूल कारण विश्लेषण (Root Cause)
net/netfilter/nf_tables_api.c में nf_tables_newset() सेट फ़्लैग में सिर्फ़ अज्ञात बिट और MAP+OBJECT व EVAL+OBJECT टकराव जाँचता था। वह NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT स्वीकार कर लेता था, इसलिए जिस सेट का जीवनकाल एक ही नियम और ट्रांज़ैक्शन से बँधा है, उसमें ऐसे एलिमेंट भी हो सकते थे जिनकी समय-सीमा खत्म होती है और जिन्हें गार्बेज कलेक्शन उठाता है। यूज़रस्पेस यह जोड़ी कभी नहीं बनाता, और जीवनकाल संभालने वाला कोड इसके लिए नहीं लिखा गया था। अपस्ट्रीम सुधार इसे -EOPNOTSUPP से ठुकराता है, जब तक NFT_SET_EVAL भी सेट न हो। सार्वजनिक रिकॉर्ड इससे होने वाली मेमोरी त्रुटि का विस्तार नहीं देता; NIST इसे 7.8 (गोपनीयता, अखंडता और उपलब्धता तीनों उच्च) और कर्नेल CNA 5.5 (केवल उपलब्धता) अंक देता है।
हमले का चरण-दर-चरण प्रवाह
CAP_NET_ADMIN पाना
एक लोकल उपयोगकर्ता यूज़र नेमस्पेस और नेटवर्क नेमस्पेस बनाता है (जहाँ बिना विशेषाधिकार वाले यूज़र नेमस्पेस चालू हैं) और उस नेमस्पेस की nf_tables स्थिति पर CAP_NET_ADMIN पा लेता है।
अवैध सेट माँगना
एक netlink बैच में प्रोसेस NFTA_SET_FLAGS = NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT के साथ NFT_MSG_NEWSET भेजता है और lookup एक्सप्रेशन से सेट को एक नए नियम से बाँधता है।
कर्नेल स्वीकार कर लेता है
nf_tables_newset() को कोई वर्जित फ़्लैग जोड़ी नहीं मिलती और वह नियम से बँधा एक एनॉनिमस सेट बना देता है जिसके एलिमेंट की समय-सीमा होती है।
बिना परखे जीवनकाल पाथ
अब नियम को कमिट, एबॉर्ट या डिलीट करने पर सेट का विनाश और एलिमेंट का गार्बेज कलेक्शन एक साथ चलते हैं, ऐसे पाथ में जिन्हें nf_tables का ट्रांज़ैक्शन कोड सुरक्षित रूप से नहीं संभालता।
सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन
/* net/netfilter/nf_tables_api.c: nf_tables_newset(), before the fix */
if (nla[NFTA_SET_FLAGS] != NULL) {
flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
NFT_SET_MAP | NFT_SET_EVAL |
NFT_SET_OBJECT | NFT_SET_CONCAT | NFT_SET_EXPR))
return -EOPNOTSUPP;
/* Only one of these operations is supported */
if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
(NFT_SET_MAP | NFT_SET_OBJECT))
return -EOPNOTSUPP;
if ((flags & (NFT_SET_EVAL | NFT_SET_OBJECT)) ==
(NFT_SET_EVAL | NFT_SET_OBJECT))
return -EOPNOTSUPP;
/* BUG: NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT is accepted. A rule-bound
* anonymous set now also gets per-element expiry and garbage
* collection, a combination nft never creates and the set
* lifetime code was not written for. */
}
/* net/netfilter/nf_tables_api.c: nf_tables_newset(), commit 16603605b667 */
if (nla[NFTA_SET_FLAGS] != NULL) {
flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
NFT_SET_MAP | NFT_SET_EVAL |
NFT_SET_OBJECT | NFT_SET_CONCAT | NFT_SET_EXPR))
return -EOPNOTSUPP;
/* Only one of these operations is supported */
if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
(NFT_SET_MAP | NFT_SET_OBJECT))
return -EOPNOTSUPP;
if ((flags & (NFT_SET_EVAL | NFT_SET_OBJECT)) ==
(NFT_SET_EVAL | NFT_SET_OBJECT))
return -EOPNOTSUPP;
/* FIX: anonymous sets are never used with timeouts from userspace,
* so reject the combination before the set exists. NFT_SET_EVAL
* stays allowed so legacy meters keep working. */
if ((flags & (NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT | NFT_SET_EVAL)) ==
(NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT))
return -EOPNOTSUPP;
}
इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट
- ✓ऐसा कर्नेल चलाएँ जिसमें कमिट 16603605b667 हो (Linux 6.8, या ऐसा स्टेबल या डिस्ट्रीब्यूशन कर्नेल जिसके चेंजलॉग में
CVE-2024-26642हो)। - ✓जहाँ वर्कलोड को ज़रूरत न हो, बिना विशेषाधिकार वाले यूज़र नेमस्पेस बंद करें (Debian और Ubuntu पर
kernel.,unprivileged_userns_clone=0user., या Ubuntu की AppArmor userns पाबंदी) ताकि लोकल उपयोगकर्ता nf_tables तक न पहुँचें।max_user_namespaces=0 - ✓जिन होस्ट पर nftables इस्तेमाल नहीं होता, वहाँ
nf_tablesमॉड्यूल लोड होने से रोकें। - ✓कर्नेल और API कोड में स्वीकार्य फ़्लैग संयोजनों को स्पष्ट अनुमति-सूची के रूप में लिखें और वह सब ठुकराएँ जो कोई समर्थित क्लाइंट नहीं बनाता, न कि सिर्फ़ ज्ञात खराब जोड़ियाँ।
- ✓हर फ़्लैग बिट को कवर करने वाले syzkaller विवरणों से netlink इंटरफ़ेस की फ़ज़िंग करें, ताकि असामान्य संयोजन पहले टेस्ट में ही ऑब्जेक्ट के जीवनकाल वाले कोड तक पहुँचें।