Race condition in snap-confine's must_mkdir_and_open_with_perms() [CVE-2022-3328]

We discussed this vulnerability during Episode 173 on 05 December 2022

A race condition in snap-confine, which is a suid root binary that’s installed by default on Ubuntu. The must_mkdir_and_open_with_perms() function is used for making a directory and opening it for temporary directories for snap-confine. If the given directory already exists, as long as it isn’t root-owned, it will be renamed to a random directory and the function will attempt to make the dir again. Ultimately, this allows you to get a directory you don’t own renamed, and can race to get snap-confine to use a directory you control for its operations to get a root file write.

Their overall strategy involves running two instances of snap-confine in parallel.

  1. Run once instance and block it (using single-step trick)
  2. Run another instance with an instance name that collides with the temporary name of the first instance
  3. Kill the second instance immediately after it renames the directory
  4. Manually recreate the directory and resume execution of the first snap-confine instance
  5. First instance will now read the /tmp dir inside of that root directory you now own and will follow symbolic links.

Taking advantage of this was tricky for a few reasons. For one, it was hard to get the symlink followed by snap-confine as the function that would mount the namespace bind-mounts a read-only squashfs into the root directory. If you create the symlink before hand it gets covered, and you can’t create it afterwards where it’s root only. They circumvented this by mounting a FUSE filesystem onto the root dir immediately when re-creating it, which then allows them to unmount any subsequent bind-mounts.

The other issue was the fact that snap-confine used AppArmor, which prevented bind-mounting /tmp onto an arbitrary directory on the system. They used a pre-existing vulnerability in multipathd as an AppArmor escape and chain with this bug to create a custom AppArmor policy that gives them free reign.

Finally, they exploit the bind-mount against the multipath lib (since they already rely on it for AppArmor bypass), implant their own shared library, and restart multipathd using another DOS bug in multipath to get their library ran as root.