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:
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;
}