sys_vquotactl() performs no privilege check β unprivileged users set/wipe quota limits for any uid/gid/mount, denying ALL writes (including root's) filesystem-wide
Summary
No priv_check anywhere in the dispatch: with the feature enabled, accounting is installed on every filesystem and vop_write consults vq_write_ok for every write returning EDQUOT with NO root bypass. Reproduced as uid 1001: 'vquota limit / 1' then root's own appends fail EDQUOT (password changes, logging, cron all break until the attacker lifts it); per-uid usage of all users also readable. Medium only because vfs.quota_enabled is default-off (High when enabled). caps_priv_check_td(SYSCAP_NOQUOTA_WR) fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5684 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.7 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| build.sh | β | 101 B | view raw | |
| env.txt | β | 225 B | view raw | |
| fix.diff | β | 500 B | view raw | |
| run.log | β | 1.0 KB | view raw | |
| run.patched.log | β | 662 B | view raw | |
| run.sh | β | 155 B | view raw | |
| vq_priv.c | β | 887 B | view raw | |
| vq_priv.sh | β | 742 B | view raw | |
| verdict.json | β | 3.1 KB | view raw |
DF-5684 β sys_vquotactl() has no privilege check: unprivileged quota-limit manipulation β system-wide write DoS
What
sys_vquotactl() (sys/kern/vfs_quota.c:328-413) dispatches quota commands
with no priv_check of any kind:
set limit(vfs_quota.c:273-284) β sets mount-wideac_limitset limit uid(vfs_quota.c:287-305) β per-uid limit, any uidset limit gid(vfs_quota.c:308-326) β per-gid limit, any gidset usage all(vfs_quota.c:219-270) β wipes/rewrites all accountingget usage all(vfs_quota.c:175-216) β reads usage of every uid/gid
Enforcement is universal once the feature is enabled:
vfs_init.c:431-432 installs vfs_stdac_init on every filesystem, and
vop_write (vfs_vopops.c:472-491) consults vq_write_ok() for every regular
file write, returning EDQUOT β with no privilege bypass for root.
Classic quotactl(2) enforces quota privilege inside the per-FS code
(PRFIV_VFS_QUOTA equivalent); vquotactl(2) simply forgot it.
Reproduce (baseline)
Guest booted with vfs.quota_enabled=1; run everything as unprivileged
uid 1001 (maxx):
$ echo before >> /root/df5684.txt && echo ROOT_WRITE_OK_BEFORE # as root ROOT_WRITE_OK_BEFORE $ vquota limit / 1 # AS UID 1001: set mount-wide limit = 1 byte setlimit_rc=0 $ vquota show / # AS UID 1001 total: 0, limit = 1 # now as ROOT: $ echo after >> /root/df5684.txt echo: write error on stdout ROOT_WRITE_BLOCKED_RC=2 $ vquota ulim / root 1 # AS UID 1001: target root's uid specifically maxx set uid-root limit=1 rc=0 $ echo rootwrite >> /root/df5684.txt # as root ROOT_APPEND_BLOCKED $ vquota ulim / root 0 && vquota limit / 0 # attacker can also lift it restored $ echo ok >> /root/df5684.txt && echo ROOT_APPEND_OK_AFTER_RESTORE ROOT_APPEND_OK_AFTER_RESTORE
(In-place overwrite with delta=0 still passes β vq_write_ok only blocks net
growth β which is why dd conv=notrunc succeeded while appends failed.)
Expected output (patched kernel, fix.diff applied)
$ vquota limit / 1 # as uid 1001 send_command: vquotactl = -1 Failed to send message to kernel maxx_setlimit_rc=1 # EPERM before any state change $ echo ... >> /root/df5684.txt # as root ROOT_WRITE_OK_ON_PATCHED $ vquota limit / 1 && vquota show / && vquota limit / 0 # as root root_setlimit_rc=0 / total: 0, limit = 1 / root_limit_restored
Files
- vq_priv.sh unprivileged part (set limit / ulim / show / sync)
- fix.diff minimal privilege-check fix (git-applyable)
- run.log baseline capture (maxx sets limits, root blocked)
- run.patched.log fix validation capture
- VERDICT.md / manifest.json / verdict.json
DF-5684 VERDICT
Status: REPRODUCED (unprivileged authorization bypass β persistent write DoS for all users incl. root) β FIXED by fix.diff (validated on rebuilt kernel)
Environment
Same guest as DF-5683: DragonFly 6.5-DEVELOPMENT x86_64, baseline kernel #0 (Jul 2 2026) / patched kernel #1 (Oct 10 00:36:54 2026), vfs.quota_enabled=1 boot tunable (feature default-off).
Chain (baseline kernel)
- uid 1001
maxxcallsvquota limit / 1βsys_vquotactlhas no privilege check (vfs_quota.c:328-413) βcmd_set_limitwritesmp->mnt_acct.ac_limit = 1(vfs_quota.c:273-284). rc=0. - Every subsequent regular-file write on the mount by ANY user (root
included) goes through vop_write β vq_write_ok (vfs_vopops.c:489):
ac_bytes + delta > ac_limitβ EDQUOT β write(2) fails. - Demonstrated: root append to /root/df5684.txt fails ("write error"), after having succeeded seconds earlier.
- Same for per-uid targeting:
vquota ulim / root 1as maxx β root's own appends blocked (ROOT_APPEND_BLOCKED), lifted by maxx (ulim / root 0). vquota show /as maxx returns every uid/gid's usage (info disclosure of accounting data);sync(set usage all) wipes accounting.- Effect persists until an attacker-chosen lift or reboot β a persistent filesystem-write DoS of the whole root mount, executed by an unprivileged user. No root escape needed for impact; combined with any local setuid-root write (e.g. passwd(1), periodic scripts), it also breaks system maintenance entirely.
Why beyond DF-2231
DF-2231 = memory-safety/DoS of the prop copyin primitive itself. This is the missing-authorization defect of the syscall consuming it β the object is parsed fine; the command should never have been executable by uid 1001.
Fix validation
fix.diff adds caps_priv_check_td(curthread, SYSCAP_NOQUOTA_WR) right after
the vfs_quota_enabled gate (the same capability classic quota operations
require), plus the missing if (error) return after the plistref copyin.
Patched kernel #1:
- vquota limit / 1 as maxx β "send_command: vquotactl = -1", rc=1 (EPERM);
mount state unchanged; root writes unaffected (ROOT_WRITE_OK_ON_PATCHED).
- Regression: root can still set/lift limits (root_setlimit_rc=0,
limit = 1 observed via vquota show, restored to 0).
kernel_refs
sys/kern/vfs_quota.c:328-413 (no priv_check), :273-284, :287-305, :308-326, :219-270 sys/kern/vfs_vopops.c:472-491 (EDQUOT enforcement, no root bypass) sys/kern/vfs_init.c:431-432 (feature applies to every filesystem) sys/sys/caps.h:173 (SYSCAP_NOQUOTA_WR β the capability the fix enforces)
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedPatched kernel #1: 'vquota limit / 1' as uid 1001 returns EPERM (send_command: vquotactl = -1), mount state unchanged, root write succeeds; root set/show/lift regression OK.
run.patched.log
Confirmed kernel references
Detail
Exploit chain
unpriv vquota limit / 1 -> vq_write_ok() fails for all writers on / (incl. root) -> persistent filesystem-wide write DoS until attacker lifts or reboot; breaks passwd, logging, package ops for every user.
Evidence (decisive lines)
run.log (ROOT_WRITE_OK_BEFORE -> maxx sets limit -> ROOT_WRITE_BLOCKED_RC=2 -> ulim root -> ROOT_APPEND_BLOCKED -> restore -> OK_AFTER_RESTORE), run.patched.log (maxx EPERM, ROOT_WRITE_OK_ON_PATCHED, root set/lift regression)
PoC changes
Guest vquota CLI differs from the checkout (limit/ulim/glim/show/sync, user by NAME not uid); adjusted vq_priv.sh accordingly. vquota exits 1 on success-path 'show' (tool quirk) β success judged by output/state, not exit code.
Verified recommended fix
Add caps_priv_check_td(curthread, SYSCAP_NOQUOTA_WR) after the vfs_quota_enabled gate
Verdict
REPRODUCED: sys_vquotactl(2) contains no privilege check on any command. Unprivileged uid 1001 set a mount-wide quota limit of 1 byte via base-system vquota(8) ('vquota limit / 1', rc=0); afterwards EVERY regular-file write on the mount fails EDQUOT β demonstrated against root (append blocked, lifted only by the attacker). Per-uid targeting of root ('vquota ulim / root 1' as uid 1001 -> ROOT_APPEND_BLOCKED), accounting wipe ('sync') and all-uid usage readout ('show') also unprivileged. Enforcement is universal (vfs_init.c:431 installs accounting on every FS; vfs_vopops.c:489 returns EDQUOT with no root bypass) but the whole feature is behind the default-off vfs.quota_enabled boot tunable. fix.diff (caps_priv_check_td(curthread, SYSCAP_NOQUOTA_WR)) validated on a rebuilt kernel: unpriv attempt -> EPERM, root writes unaffected, privileged set/lift regression-tested OK.
No comments yet.