Hacking & Computer Science stuff

The nodev mount option

Brief summary about mount security principles, Linux file types and risk about this specific option.

Disclaimer: this article is not intended to be exhaustive and is the result of my own research and understanding of this quiet complex and vast subject.

About mounting

Operating systems implement many interoperable features in order to read, write or execute files from file systems. Some of them are based on standards: CIFS SMB, NFS, ...

On Linux, mount is a privileged system call to attach a filesystem specified by a source related to a type with options.

Note: mount program is different than mount system call: the first one does not need specific privileges by default, unless you use it to effectively mount a filesystem. You can spot mount program calling mount syscall by using strace mount for a real mount attempt. By default, without arguments, mount program will read informative system files like /proc/self/mountinfo readable by standard users.

Mount privileges

You could tell that the usage of root user (EUID 0) is mandatory, but I will answer you that only CAP_SYS_ADMIN effective capability is needed (Read page 2 of mount manual).

By default, that should be enabled in the initial user namespace, it does not apply to a child user namespace except if the requested filesystem especially accept it through a fs_flag containing FS_USERNS_MOUNT. Fast preview from a grep in Linux source code shows that OverlayFS could be mount by a non-privileged user in their initial user namespace:

fs/overlayfs/super.c:1480: .fs_flags = FS_USERNS_MOUNT,

So, given a mount (MNT) namespace and user (USR) namespace combined with CAP_SYS_ADMIN capability could offer you a privileged access to local devices sharing this namespace.
Required and obtained privileges are also dependent of each filesystem module implementation. The GameOver(lay) vulnerability (CVE-2023-2640 and CVE-2023-32629) exploits a defect in the OverlayFS module in the security management of mounting this filesystem for non-privileged user.

Additionally, mount could also be gated through seccomp or AppArmor.

Mount types

As explained in the page 8 of mount manual, the mount type matches the compatible filesystem to be attached to your current context.

Mount types can be found in folder /lib/modules/$(uname -r)/kernel/fs. In my case:

find /lib/modules/$(uname -r)/kernel/fs/ -type d | cut -d '/' -f7 | sort | uniq
  • 9p
  • adfs
  • affs
  • afs
  • autofs
  • befs
  • bfs
  • btrfs
  • cachefiles
  • ceph
  • cifs
  • coda
  • configfs
  • dlm
  • ecryptfs
  • efivarfs
  • efs
  • erofs
  • exfat
  • ext4
  • f2fs
  • fat
  • freevxfs
  • fscache
  • fuse
  • gfs2
  • hfs
  • hfsplus
  • hpfs
  • isofs
  • jbd2
  • jffs2
  • jfs
  • ksmbd
  • lockd
  • minix
  • netfs
  • nfs
  • nfs_common
  • nfsd
  • nilfs2
  • nls
  • ocfs2
  • omfs
  • orangefs
  • overlayfs
  • pstore
  • qnx4
  • qnx6
  • quota
  • reiserfs
  • romfs
  • smbfs_common
  • squashfs
  • sysv
  • ubifs
  • udf
  • ufs
  • vboxsf
  • xfs
  • zonefs

Mount options

The manual also specifies that filesystem mounting can be done with following options:

  • async
  • atime
  • auto
  • context=context
  • defaults
  • defcontext=context
  • dev
  • diratime
  • dirsync
  • exec
  • fscontext=context
  • group
  • iversion
  • lazytime
  • loud
  • mand
  • _netdev
  • noatime
  • noauto
  • nodev
  • nodiratime
  • noexec
  • nofail
  • noiversion
  • nolazytime
  • nomand
  • norelatime
  • nostrictatime
  • nosuid
  • nosymfollow
  • nouser
  • owner
  • relatime
  • remount
  • ro
  • rootcontext=context
  • rw
  • silent
  • strictatime
  • suid
  • sync
  • user
  • users
  • x-*
  • X-*
  • X-mount.auto-fstypes=list
  • X-mount.group=group|GID
  • X-mount.idmap=id-type:id-mount:id-host:id-range
  • X-mount.mkdir[=mode]
  • X-mount.mode=mode
  • X-mount.owner=username|UID
  • X-mount.subdir=directory

All these options are not compatible / implemented for every filesystem / OS / context.

The options have an effect on the attached filesystem behavior, and sometimes on general security.

I did not find a clear compatibility matrix between mount types and options. You are welcome to share one!

Mounting operations and udisks

Although mount system call needs privileges, hopefully a standard user does not always directly needs them by default to get a filesystem mount.

For an easy example, in non-hardened systems, an unprivileged user should have the capability to use an USB flash drive without having administrator rights.

How do we manage unprivileged mounts (without messing up with kernel)?

Auto-mount handling is managed by external programs.

First, in my case, GNOME is responsible of this feature:

❯ gsettings get org.gnome.desktop.media-handling automount
true

Second, though I did not dig into GNOME code, it is probably interacting with udisks (Or udisks2) interface, dedicated to mount USB flash drives without high privileges.

You can try with udisksctl mount -b /dev/sda1 (If /dev/sda1 is the target partition of your USB flash drive), then you will get your UDisk mounted at /media/user/xyz.

That's sound crazy at first sight: how this device get mounted in so far mount syscall needs CAP_SYS_ADMIN privileges ?

arthur

Because udisks is asynchronous: it works via polling and jobs thanks to its autonomous daemon, and not direct user action. It can be seen with udiskctl monitor. Example after a direct USB flash drive plug-in:

19:53:05.489: Added /org/freedesktop/UDisks2/block_devices/sda
  org.freedesktop.UDisks2.Block:
    Configuration:              []
    CryptoBackingDevice:        '/'
    Device:                     /dev/sda
    DeviceNumber:               2048
    Drive:                      '/org/freedesktop/UDisks2/drives/General_UDisk_General_UDisk_0_3a0'
    HintAuto:                   true
    HintIconName:               
    HintIgnore:                 false
    HintName:                   
    HintPartitionable:          true
    HintSymbolicIconName:       
    HintSystem:                 false
    Id:                         
    IdLabel:                    
    IdType:                     
    IdUUID:                     
    IdUsage:                    
    IdVersion:                  
    MDRaid:                     '/'
    MDRaidMember:               '/'
    PreferredDevice:            /dev/sda
    ReadOnly:                   false
    Size:                       8054112256
    Symlinks:                   /dev/disk/by-diskseq/27
                                /dev/disk/by-id/usb-General_UDisk-0:0
                                /dev/disk/by-path/pci-0000:06:00.4-usb-0:2:1.0-scsi-0:0:0:0
    UserspaceMountOptions:      
  org.freedesktop.UDisks2.PartitionTable:
    Partitions:         
    Type:               dos
19:53:05.553: Added /org/freedesktop/UDisks2/block_devices/sda1
  org.freedesktop.UDisks2.Block:
    Configuration:              []
    CryptoBackingDevice:        '/'
    Device:                     /dev/sda1
    DeviceNumber:               2049
    Drive:                      '/org/freedesktop/UDisks2/drives/General_UDisk_General_UDisk_0_3a0'
    HintAuto:                   true
    HintIconName:               
    HintIgnore:                 false
    HintName:                   
    HintPartitionable:          true
    HintSymbolicIconName:       
    HintSystem:                 false
    Id:                         by-uuid-5B67C7955E71E727
    IdLabel:                    usb1
    IdType:                     ntfs
    IdUUID:                     5B67C7955E71E727
    IdUsage:                    filesystem
    IdVersion:                  
    MDRaid:                     '/'
    MDRaidMember:               '/'
    PreferredDevice:            /dev/sda1
    ReadOnly:                   false
    Size:                       8053063680
    Symlinks:                   /dev/disk/by-id/usb-General_UDisk-0:0-part1
                                /dev/disk/by-label/usb1
                                /dev/disk/by-partuuid/a100c314-01
                                /dev/disk/by-path/pci-0000:06:00.4-usb-0:2:1.0-scsi-0:0:0:0-part1
                                /dev/disk/by-uuid/5B67C7955E71E727
    UserspaceMountOptions:      
  org.freedesktop.UDisks2.Filesystem:
    MountPoints:        
    Size:               0
  org.freedesktop.UDisks2.Partition:
    Flags:              0
    IsContained:        false
    IsContainer:        false
    Name:               
    Number:             1
    Offset:             1048576
    Size:               8053063680
    Table:              '/org/freedesktop/UDisks2/block_devices/sda'
    Type:               0x07
    UUID:               a100c314-01
19:53:05.553: /org/freedesktop/UDisks2/block_devices/sda: org.freedesktop.UDisks2.PartitionTable: Properties Changed
  Partitions:           /org/freedesktop/UDisks2/block_devices/sda1
19:53:05.617: Added /org/freedesktop/UDisks2/jobs/14
  org.freedesktop.UDisks2.Job:
    Bytes:              0
    Cancelable:         true
    ExpectedEndTime:    0
    Objects:            /org/freedesktop/UDisks2/block_devices/sda1
    Operation:          filesystem-mount
    Progress:           0.0
    ProgressValid:      false
    Rate:               0
    StartTime:          1692813185616440
    StartedByUID:       0
19:53:05.668: /org/freedesktop/UDisks2/jobs/14: org.freedesktop.UDisks2.Job::Completed (true, '')
19:53:05.669: Removed /org/freedesktop/UDisks2/jobs/14
19:53:05.669: /org/freedesktop/UDisks2/block_devices/sda1: org.freedesktop.UDisks2.Filesystem: Properties Changed
  MountPoints:          /media/seb/usb1
19:53:05.673: /org/freedesktop/UDisks2/block_devices/sda1: org.freedesktop.UDisks2.Block: Properties Changed
  UserspaceMountOptions:        uhelper=udisks2

udisks security can be configured with (Not exhaustive):

Apart from the final computed options UDisks always adds the following options due to security concerns: nodev, nosuid, uhelper=udisks2 no matter if included in _allow or not. These are hardcoded and can't be changed.

That said, we can go deeper in the mount option nodev and why it is important for system security.

The nodev option

From the Linux manual

Sure it could be very interesting to enumerate all security impacts for all other options for each filesystem, but I will just explain the security risk about nodev option.

What does the manual tell us about nodev ?

Do not interpret character or block special devices on the filesystem.

It could be unclear about real effects of this option on the attached filesystem and security, especially for a Linux neophyte. Some knowledges are prerequisites about Linux file types before understanding the impacts.

Let me tell you how I discovered the security risk about NOT using the nodev option ...

Security risk

The case

I was analysing the security of two devices, where first one (A) had an automatic CIFS SMB mount of a share from the other one (B).

I have limited privileges on the device (A).
I have high privileges on the share of the device (B).

About file types

I was aware that every file you see on a filesystem could not be a regular file. In fact, we can distinguish following file types (Brought by POSIX standard, read https://en.wikipedia.org/wiki/Unix_file_types):

  • Regular file
  • Directory file
  • Symbolic link (Or symlink)
  • FIFO (Or named pipe)
  • Socket
  • Device file (Divided in character or block types)

Synthesis with needed capabilities for creating these files:

File typeMacro name (S_IFMT)File type attribute flagNeeded capabilitiesExample of file creation
RegularS_IFREG--touch myfile
DirectoryS_IFDIRd-mkdir mydirectory
Symbolic linkS_IFLNKl-ln -s mysymlink
FIFOS_IFIFOp-mknod myfifo p
SocketS_IFSOCKs-socat UNIX-LISTEN:my.socket -
Device (block)S_IFBLKbCapability CAP_MKNODmknod myblockdevice b 1 2
Device (character)S_IFCHRcCapability CAP_MKNODmknod mychardevice c 3 4

A block device is using two identifiers to point a real device like a disk. You can list them with lsblk. For example, on the device (A), we have:

nvme1n1     259:5    0 931,5G  0 disk 
└─nvme1n1p1 259:6    0 931,5G  0 part /

So the partition nvme1n1p1 have identifiers 259 and 6.

The matching block device file can also be found at /dev/nvme1n1p1:

ls -l /dev/nvme1n1p1
brw-rw---- 1 root disk 259, 6 23 août  20:25 /dev/nvme1n1p1

If I create a block device somewhere else with those identifiers, it will refer to the same partition.

Experiment

I was curious how device files (which needs high privileges) were interpreted through SMB mount ...

On the device (B), I can create a block device matching the partition from the device (A):

# In the SMB share from device (B) - Creation of block device pointing to the partition present in device (A)
sudo mknod mypartition b 259 6

Go back in the device (A), with standard user:

# Go into the SMB share mounted pathcd /mnt/shared
# Listls -l
brwxrwxrwx  1 root  root  259, 6 22 août  20:30  mypartition
# Try to display the block devicehead mypartition
# (...) Raw bytes here (...)

Consequences

I can have now a full raw access to the main partition as a standard user, through the mounted block device.

Why it is permitted ? For this case, it is mainly because nodev was not set in the options of the CIFS SMB share mount. If nodev were set (mount -t cifs -o nodev (...)), I would had an error of type EACCES, even with root user, which is very efficient.

You can reproduce it with a single USB flash drive containing a block device, without using udisk. Simply mount it with mount -t auto /dev/sda1 /mnt/usb (Of course you need CAP_SYS_ADMIN)

So, next times, be aware of mount without options impacting security like nodev ;)

© Sébastien Copin (cosades) 2024