cve-toolkit

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

commit 924ff61950ab936176c87ab66839f77ec6c009a3
parent a8fe0d6409813695f23637bbbc2825d8609e3252
Author: finwo <finwo@pm.me>
Date:   Fri,  8 May 2026 15:56:53 +0200

Forced recovery of original passwd file

Diffstat:
Msrc/detector/dirtyfrag.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 103 insertions(+), 5 deletions(-)

diff --git a/src/detector/dirtyfrag.c b/src/detector/dirtyfrag.c @@ -36,7 +36,8 @@ #define SEQ_VAL 200 #define REPLAY_SEQ 100 #define TARGET_PATH "/usr/bin/su" -#define SU_BACKUP_PATH "/tmp/.dirtyfrag_su_backup" +#define SU_BACKUP_PATH "/tmp/.dirtyfrag_su_backup" +#define PASSWD_BACKUP_PATH "/tmp/.dirtyfrag_passwd_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 */ @@ -374,6 +375,92 @@ static int revert_su(void) return 0; } +/* ------------------------------------------------------------------ */ +/* Backup / Revert for /etc/passwd */ +/* */ +/* A copy of the original /etc/passwd is saved before any corruption. */ +/* revert_passwd() restores it (including drop_caches so the page */ +/* cache is re-read from disk), then removes the backup temp file. */ +/* ------------------------------------------------------------------ */ + +#define PASSWD_TARGET_PATH "/etc/passwd" + +static int backup_passwd(void) +{ + struct stat st; + if (stat(PASSWD_BACKUP_PATH, &st) == 0) { + SLOG("passwd backup already exists at %s, skipping", PASSWD_BACKUP_PATH); + return 0; + } + int src = open(PASSWD_TARGET_PATH, O_RDONLY); + if (src < 0) { + SLOG("backup_passwd: open(%s): %s", PASSWD_TARGET_PATH, strerror(errno)); + return -1; + } + int dst = open(PASSWD_BACKUP_PATH, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (dst < 0) { + SLOG("backup_passwd: open(%s): %s", PASSWD_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_passwd: write failed: %s", strerror(errno)); + close(src); close(dst); + unlink(PASSWD_BACKUP_PATH); + return -1; + } + } + close(src); + close(dst); + if (n < 0) { + SLOG("backup_passwd: read failed: %s", strerror(errno)); + unlink(PASSWD_BACKUP_PATH); + return -1; + } + SLOG("backed up %s to %s", PASSWD_TARGET_PATH, PASSWD_BACKUP_PATH); + return 0; +} + +static int revert_passwd(void) +{ + int src = open(PASSWD_BACKUP_PATH, O_RDONLY); + if (src < 0) { + SLOG("revert_passwd: no backup at %s", PASSWD_BACKUP_PATH); + return -1; + } + int dst = open(PASSWD_TARGET_PATH, O_WRONLY | O_TRUNC); + if (dst < 0) { + SLOG("revert_passwd: open(%s): %s", PASSWD_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_passwd: write failed: %s", strerror(errno)); + close(src); close(dst); + return -1; + } + } + close(src); + close(dst); + if (n < 0) { + SLOG("revert_passwd: read failed: %s", strerror(errno)); + return -1; + } + /* Sync to disk and drop caches so the restored page cache is read */ + int sfd = open(PASSWD_TARGET_PATH, O_RDONLY); + if (sfd >= 0) { syncfs(sfd); close(sfd); } + write_proc("/proc/sys/vm/drop_caches", "1"); + unlink(PASSWD_BACKUP_PATH); + SLOG("%s reverted from backup, page cache flushed", PASSWD_TARGET_PATH); + return 0; +} + static int corrupt_su(void) { setup_userns_netns(); @@ -2078,15 +2165,18 @@ int detector_dirtyfrag(int num) { if (detector_su_already_patched() || detector_passwd_already_patched()) return 3; + /* Back up both artifacts before any corruption attempt. */ + backup_su(); + backup_passwd(); + { /* ESP / xfrm path */ char *argv[] = {"dirtyfrag", "--corrupt-only", NULL}; if (su_lpe_main(2, argv) == 0 && detector_su_already_patched()) { - /* System is vulnerable — restore su before returning. */ + /* System is vulnerable — restore artifacts before returning. */ revert_su(); + revert_passwd(); return 3; } - /* Backup was created by su_lpe_main; restore su to its - * original state (harmless if the binary was never corrupted). */ revert_su(); } @@ -2095,10 +2185,18 @@ int detector_dirtyfrag(int num) { char *argv[] = {"dirtyfrag", "--corrupt-only", NULL}; int rc = rxrpc_lpe_main(2, argv); unsetenv("DIRTYFRAG_CORRUPT_ONLY"); - if (rc == 0 && detector_passwd_already_patched()) + if (rc == 0 && detector_passwd_already_patched()) { + /* System is vulnerable — restore artifacts before returning. */ + revert_su(); + revert_passwd(); return 3; + } } + /* Restore any artifacts that may have been touched. */ + revert_su(); + revert_passwd(); + return 0; }