β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0141

Missing privilege check in sys_vquotactl: any user can set/read all quotas

Summary

sys_vquotactl(syscall 530) has NO caps_priv_check anywhere. UFS quota ioctls check SYSCAP_NOQUOTA_WR. Any unprivileged user can: set ac_limit=0 -> filesystem-wide write DoS; set/remove per-uid/gid limits for arbitrary users including uid 0; get usage all -> cross-user info disclosure. Gated by vfs_quota_enabled=0 default.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0141 Β· 12 files
FileTypeDescriptionSize
df0141_poc.c trigger-source unprivileged vquotactl privilege-bypass PoC (read/set all quotas via syscall 530) 6.7 KB view raw
build.sh build-script cc -o df0141_poc df0141_poc.c -lprop 143 B view raw
run.sh run-script runs PoC + DoS end-to-end demo as unprivileged user 741 B view raw
build.log build-log full nativekernel build of the single-fix kernel (rc=0) 5.6 MB ↓ download
run.log run-log baseline run on unpatched #0 kernel: all ops SUCCESS + DoS confirmed 1.4 KB view raw
fix_run.log run-log patched #1 kernel run: all ops EPERM, DoS neutralized 1.1 KB view raw
fix.diff suggested-fix add caps_priv_check_td(curthread, SYSCAP_NOQUOTA_WR) to sys_vquotactl 912 B view raw
env.txt environment uname, kern.version, cc version, quota state, kernel sha256 619 B view raw
VERDICT.md verdict full narrative: mechanism, evidence, fix validation 4.6 KB ↓ raw
README.md readme human reproduce doc 1.4 KB ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme human reproduce doc
↓ download raw

DF-0141 β€” Missing privilege check in sys_vquotactl

Bug: sys/kern/vfs_quota.c:sys_vquotactl (syscall 530) has no caps_priv_check / privilege test. Any unprivileged local user can read every user's disk usage, set/remove per-uid/gid quota limits (including root's), and set the filesystem-wide ac_limit (filesystem-wide write DoS).

Class: privilege boundary violation (logic/auth). Not memory corruption.

Reproduce

Admin precondition (realistic β€” admin deploying VFS quotas):

echo 'vfs.quota_enabled=1' >> /boot/loader.conf
reboot

Build & run as unprivileged user:

./build.sh
./run.sh        # defaults to /tmp
# or: ./df0141_poc /tmp

Expected (bug present, unpatched kernel): all vquotactl operations return rc=0 (SUCCESS). maxx reads root's usage, sets root's limit to 99999999 (confirmed by re-read), sets /tmp fs-wide limit. The DoS takes effect: dd to /tmp returns "Disc quota exceeded".

Expected (fixed kernel): all vquotactl operations return EPERM (errno=1, Operation not permitted). Writes to /tmp succeed normally.

Files

  • df0141_poc.c β€” the PoC source
  • fix.diff β€” the one-line fix (add caps_priv_check_td + include)
  • run.log β€” baseline (unpatched) decisive run
  • fix_run.log β€” patched kernel decisive run
  • build.log β€” full single-fix kernel build log
  • VERDICT.md β€” detailed analysis
  • manifest.json β€” artifact catalog
VERDICT.md verdict full narrative: mechanism, evidence, fix validation
↓ download raw

DF-0141 β€” Missing privilege check in sys_vquotactl

Verdict: REPRODUCED + FIX VALIDATED

sys/kern/vfs_quota.c:sys_vquotactl (syscall 530) contains no privilege check anywhere in its body. Any unprivileged local user can read every user's disk usage, set/remove per-uid and per-gid quota limits (including root's), and set the filesystem-wide ac_limit β€” which, when set below current usage, denies all writes on the entire filesystem for all users (filesystem-wide write DoS). The fix adds caps_priv_check_td(curthread, SYSCAP_NOQUOTA_WR), matching the UFS quota ioctl model, and was built, booted, and verified.

Mechanism (trigger β†’ primitive β†’ effect)

  1. Reachability gate. sys_vquotactl (sys/kern/vfs_quota.c:328) begins with if (!vfs_quota_enabled) return EOPNOTSUPP; (line 342). vfs_quota_enabled is a boot tunable (vfs.quota_enabled=1 in /boot/loader.conf), CTLFLAG_RD so not settable at runtime. An admin who deploys VFS quotas sets this; it is a realistic deployment precondition, not part of the exploit chain.

  2. No privilege check. After the gate, the function does nlookup on the user-supplied path (line 351-359), extracts the proplib command dictionary, and dispatches to cmd_get_usage_all, cmd_set_limit, cmd_set_limit_uid, cmd_set_limit_gid (lines 380-399) β€” none of which call caps_priv_check, priv_check, suser, or any other privilege test. Compare sys/kern/vfs_syscalls.c where every privileged VFS operation calls caps_priv_check_td(td, ...), and the UFS quota ioctls in sys/vfs/ufs/ufs_quota.c gate on SYSCAP_NOQUOTA_WR (sys/sys/caps.h:173).

  3. Primitive. The attacker fully controls the proplib command and its arguments (uid, gid, limit). The kernel writes attacker-chosen 64-bit values directly into mp->mnt_acct.ac_limit and unp->uid_chunk[uid].limit / gnp->gid_chunk[gid].limit under the mount's spinlock.

  4. Effect (demonstrated): - Info disclosure: "get usage all" returns every uid/gid's exact disk usage and limits. maxx (uid 1001) read root's usage (uid 0: 102400). - Privilege bypass: "set limit uid" with uid=0 succeeded (rc=0) and the re-read confirmed uid=0 limit=99999999. - Filesystem-wide write DoS: "set limit" with a small value caused all subsequent writes to return Disc quota exceeded β€” maxx wrote 0 bytes after setting the limit, proving the DoS is live.

This is a logic/auth bug, not memory corruption

No slab grooming, heap feng shui, or escalation chain applies. The bug is the privilege boundary violation itself: the syscall grants quota-administrator power (set/read all quotas, deny filesystem writes) to any unprivileged user. The impact ceiling is filesystem-wide availability attack + cross-user information disclosure from any local unprivileged account. (It does not grant uid=0.)

PoC

  • df0141_poc.c β€” calls vquotactl(2) (syscall 530) via the libc weak alias, using the same proplib wire format as sbin/vquota/vquota.c. Exercises read-all, set-uid-limit (targeting uid 0), and set-fs-limit.
  • Build: cc -o df0141_poc df0141_poc.c -lprop
  • Run (unprivileged): ./df0141_poc /tmp (requires vfs.quota_enabled=1)
  • Admin setup (one-time): echo 'vfs.quota_enabled=1' >> /boot/loader.conf then reboot. This enables per-mount accounting on tmpfs/ufs mounts.

Evidence (before / after)

Unpatched kernel (#0, vfs.quota_enabled=1), as maxx uid=1001:

[1] get usage all    rc=0 (SUCCESS) β€” read root's usage (info disclosure)
[2] set limit uid=0  rc=0 (SUCCESS β€” PRIVILEGE BYPASS)
[3] re-read          uid=0  limit=99999999  (write confirmed)
[4] set limit /tmp   rc=0 (SUCCESS β€” FILESYSTEM-WIDE WRITE DoS)
DoS demo: dd β†’ "Disc quota exceeded", 0 bytes written

Patched kernel (#1, fix applied), as maxx uid=1001:

vquotactl returned -1, errno=1 (Operation not permitted)
[1]-[4] all rc=-1 (EPERM)
DoS demo: dd β†’ 1024 bytes written (DD_RC=0, NOT blocked)

Root regression (patched kernel):

vquota show /tmp   β†’ total: 0          (works)
vquota limit /tmp 500000 β†’ rc=0        (works, limit confirmed)

Fix

fix.diff adds #include <sys/caps.h> and inserts error = caps_priv_check_td(curthread, SYSCAP_NOQUOTA_WR); if (error) return (error); immediately after the vfs_quota_enabled gate in sys_vquotactl. This mirrors the UFS quota ioctl privilege model (SYSCAP_NOQUOTA_WR). The single-fix kernel was built (make -j6 nativekernel KERNCONF=X86_64_GENERIC, rc=0), installed, booted (kern.version #1), and confirmed: maxx gets EPERM on all paths; root still manages quotas normally.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: baseline maxx reads/sets root quota + fs DoS; patched EPERM all ops, dd succeeds, root works.

BEFORE: maxx rc=0 all ops, dd blocked. AFTER: maxx EPERM, dd=1024B, root rc=0.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Thu Jul 16 05:35:30 UTC 2026

Confirmed kernel references

Detail

Exploit chain

none -- privilege bypass IS the bug. maxx gets quota-admin power: read all users, set any limit, fs-wide write DoS.

Evidence (decisive lines)

BEFORE: maxx get usage rc=0, set limit uid=0 rc=0 (root's quota changed), set fs limit -> dd 'Disc quota exceeded'. AFTER: maxx EPERM all ops, dd succeeds, root unaffected.

PoC changes

Authored: df0141_poc.c (proplib vquotactl syscall), fix.diff (caps_priv_check_td SYSCAP_NOQUOTA_WR after quota_enabled gate), VERDICT.md, manifest.json.

Verified recommended fix

Add caps_priv_check_td(curthread, SYSCAP_NOQUOTA_WR) in sys_vquotactl after vfs_quota_enabled gate at vfs_quota.c:342. Mirrors UFS quota ioctl model. Full diff in findings/poc/DF-0141/fix.diff.

Verdict

REPRODUCED. sys_vquotactl (syscall 530) vfs_quota.c:328-413 has NO caps_priv_check. maxx reads root's disk usage, sets root's uid-0 quota limit, sets fs-wide ac_limit -> 'Disc quota exceeded' DoS. Precondition: vfs.quota_enabled=1 boot tunable.