No Love for Negative Permissions
TL;DR: Avoid using negative permissions; on modern Linux systems, they can be circumvented.
Linux offers a diverse range of methods for implementing file permissions. In addition to the traditional discretionary access control (DAC), it also supports access control lists (ACL) and mandatory access control (MAC). Typically, all of these methods operate in a positive manner, meaning that each established permission configuration grants power. By default, nothing is permitted. This approach is also occasionally termed white or allow listing. However, both DAC and ACL do allow the implementation of negative permissions. Negative permissions deny access to certain users or groups while everybody else retains access. This practice is much less common and is widely seen as a poor security practice within the community.
With the advent of Linux containers, the landscape has shifted, rendering negative permissions not only a poor practice but also susceptible to bypass attacks. In contemporary Linux systems, there’s a significant likelihood that users can sidestep negative permissions and potentially gain entry to protected files.
Example of Negative Permissions
Let’s consider a scenario where everyone has access to the /usr/share/games
folder. However, there’s a desire to prevent specific users from playing games. To achieve this, a new group called nogames
is established, and a negative permission is implemented to deny access for members belonging to this group.
|
|
Now, users bob
and john
are effectively restricted from accessing the /usr/share/games
directory. This outcome is achieved due to the priority sequence: user
DAC entries take precedence over group
, and group
takes precedence over other
.
Given that both bob
and john
belong to the nogames
group, the permissions established for the nogames
group take precedence, resulting in no granted permissions for them.
Conversely, for all other users, either the user
or the other
entities come into play.
The same objective can also be accomplished using Access Control Lists (ACLs) in place of discretionary access control (DAC). For instance, you can implement an ACL using a command like setfacl -m g:nogames:--- /usr/share/games
.
Exploiting the Problem
Certainly, if a user manages to remove themselves from the nogames
group, access would be restored.
The setgroups()
1 system call enables the modification of supplementary groups associated with the current process.
Naturally, such an operation requires privileged capabilities, specifically the CAP_SETGID
capability.
This mechanism highlights the significance of managing privileges and group memberships to effectively uphold the desired access controls.
With the advent of Linux containers, particularly user namespaces, the dynamics have shifted.
In a user namespace, an unprivileged user possesses all capabilities.
However, every action taking place within the namespace is associated with the user and group IDs of the namespace’s creator.
For enabling rootless container engines like podman
, the shadow-utils
package includes a lesser-known toolkit: newuidmap
2 and newgidmap
3.
These tools are either set-uid-root or possess the file capability CAP_SETGID
.
By utilizing these tools, users can establish a fresh user namespace and set up UID and GID mappings that target distinct IDs from those of the invoking task.
These mappings are maintained in /etc/subuid
and /etc/subgid
.
When a user is created, the useradd
tool configures these designated subordinate IDs.
Having multiple group ID mappings in place empowers a process within a user namespace once again to employ the setgroups()
syscall, allowing it to shed supplementary groups at its discretion!
|
|
The unshare
tool internally employs newuidmap
and newgidmap
.
By consulting /etc/subgid
, we know that the user named bob
can utilize the subordinate group ID 100000
.
|
|
podman
in rootless mode can also be utilized to achieve the same result.
User based Negative Permissions
An attentive reader might wonder about negative permissions based on users, as we’ve mostly talked about groups so far.
Consider a scenario involving negative ACLs like this: let’s say we want to prevent user bob
from accessing games using an ACL command like setfacl -m u:bob:--- /usr/share/games
.
Once again, using unshare
along with newuidmap
proves to be effective.
However, this time, it doesn’t involve dropping additional groups.
Instead, it works by changing the effective user ID to one within a specific range.
|
|
Background and History
At first, the idea that dropping or changing group memberships might cause problems wasn’t considered when user namespaces were added. This was mainly because people didn’t think about situations where permissions work in a negative way. It was assumed that if you leave a group, you’d have less power, and on Linux diminishing one’s own authority is usually okay. Yet, it didn’t take long to realize4 that dropping groups could actually give more power when dealing with negative permissions. This discovery led to an issue known as CVE-2014-8989, which was quickly fixed in Linux version 3.19.
In 2018, it was discovered that version 4.5 of the shadow-utils
package introduced a new tool named newgidmap
.
This tool can be used to create more complex mappings for group IDs within user namespaces.
Consequently, it reinstates the ability to drop group memberships using the setgroups()
syscall5.
This issue was given the tracking identifier CVE-2018-7169.
The resolution adopted by some Linux distributions was to exclude this tool from their packages.
Furthermore, newuidmap
assists users in changing their user ID to one within an authorized range, as configured in /etc/subid
6.
This maneuver can also serve to circumvent negative permissions based on user settings.
Though this problem wasn’t a kernel bug, it became evident that rectifying this vulnerability without disrupting established programs like podman
would require a non-trivial kernel change78.
Other potential solutions also focused on the shadow-utils
package itself9, but as of now, they haven’t gained significant traction.
Ultimately, by 2021, a general agreement was reached to move away from supporting negative permissions and to acknowledge the reality that they can be circumvented10.
In 2023, Richard rediscovered the problem and opted to comprehensively document it across multiple Linux manual pages11.
Summary
Negative permissions have consistently been regarded as bad practice and often treated as theoretical concept. With the incorporation of Linux containers, negative permissions can now be evaded.
If you find yourself reliant on them, consider one of the following actions:
- Restructure your permissions into proper allow rules; this is the most recommended approach.
- Ensure that neither
newuidmap
nornewgidmap
is present. As of 2023, these two tools are the primary gateways for bypassing such permissions. However, this situation might change in the future. - Completely disable user namespaces by setting the
user.max_user_namespaces
sysctl to0
. - On Ubuntu-based systems, you can also use the
kernel.unprivileged_userns_clone
to disable user namespaces only for unprivileged users.
Finally, it is worth noting that there is still effort to tame newgidmap
and newuidmap
12.
-
https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/1729357 ↩︎
-
https://lore.kernel.org/lkml/20210510130011.1441834-1-gscrivan@redhat.com/ ↩︎
-
https://lore.kernel.org/lkml/878sc27zfd.fsf_-_@x220.int.ebiederm.org/ ↩︎
-
https://lore.kernel.org/lkml/m1o8d4xgkk.fsf@fess.ebiederm.org/] ↩︎
-
https://lore.kernel.org/linux-api/20230829205833.14873-1-richard@nod.at/ ↩︎
-
https://github.com/shadow-maint/shadow/pull/99#issuecomment-1698356714 ↩︎