DF-3038 / alias.c
/* * DF-3038 — FAT32 root-alias proof of concept (analysis-only, not guest-run). * * Crafted FAT32 image facts (built with newfs_msdos -F 32 + binary patch): * - the FAT32 root directory contains ONLY unused entries (all 0x00); * - a subdirectory /S contains an entry "rootalias" with * ATTR_DIRECTORY, deStartCluster = low16(rootdirblk), * deHighClust = high16(rootdirblk). * * lookup("/mnt/S/rootalias") -> lookup.c:411-418 maps * scn == pmp->pm_rootdirblk to MSDOSFSROOT -> deget(0, MSDOSFSROOT_OFS) * -> denode.c:327-347 returns THE VROOT root denode. The alias is * therefore the mount root itself. * * rmdir("/mnt/S/rootalias") then: * msdosfs_vnops.c:1451 dosdirempty(root) == 1 (crafted empty root) * msdosfs_vnops.c:1463 removede() marks the alias entry deleted and * drops a ref on the ROOT denode * msdosfs_vnops.c:1469 detrunc(root_denode, 0, IO_SYNC): * denode.c:502 VROOT guard is "!FAT32(pmp)" only, * so on FAT32 it proceeds and * denode.c:523-524+602 freeclusterchain() frees * THE ROOT DIRECTORY CHAIN of the live mount. * * rename("/mnt/S/rootalias", "/mnt/T/x") (T empty dir): * doscheckpath blind spot (lookup.c:843 vs 847: source->de_StartCluster * is pm_rootdirblk but the target chain reports ".." == 0 == MSDOSFSROOT, * which stops the walk as "reached root" without matching the source) * lets the rename proceed; msdosfs_vnops.c:1238-1251 then overwrites * entry 1 (the ".." slot) of the ROOT directory with the new parent's * cluster, clobbering whatever real entry lived there and installing a * live namespace cycle (the mount root now sits "inside" its own tree). * * Impact: corruption confined to the attacker-crafted volume + a mounted * namespace cycle (Low). No kernel memory-safety violation. Not executed * on the guest (Low severity => Phase V skipped); the code path is fully * traced in VERDICT.md. */ int main(void) { return 0; } |