Quote Solar Designer (someone that Joanna Rutkowska respects):
Ideally, there should be no SUID binaries reachable from the user account, as otherwise significant extra attack surface inside the VM is exposed (dynamic linker, libc startup, portions of Linux kernel including ELF loader, etc.)
This will break some binaries though. For example, ping needs to be setuid to open a socket.
These can be fixed by only giving them the needed capabilities with the setcap command. For example, we can give ping just the CAP_NET_RAW capability without having to give it full root access.
Looks like a sledge hammer approach. I mean, implementations that are ok as a sysadmin for its own computer are not necessarily advisable to be applied by default in a Linux distribution.
Does this change survive package re-installation?
It certainly takes no effect for newly installed packages.
How to reverse this?
What kind of feature breakage is to be expected?
Are there any alternative ways to implement this?
Are there any other linux distributions implementing this so we can look at their implementation for comparison?
Likely, no as the package manager would give it back the setuid bit.
Create a list of all current setuid and setgid binaries before running this and then you can just give the setuid/setgid bit back to them.
A list of all setuid and setgid binaries can be gotten by running
find / -type f \( -perm -4000 -o -perm -2000 \)
Assuming you haven’t given the binary the required capabilities, it will not work. Some binaries like su will pretend to work but then always give a permission denied error.
We can create a list of common setuid and setgid binaries that are usually found on a system and remove their setuid/setgid bits individually. This way, we have a lot more control over what happens but there may be certain binaries that slip past.
Not that I know of.
The Arch Wiki has a page on this that may be useful.
That sounds like something that’d be good for /etc/fstab.d, similar to hidepid.
Mounting certain directories (/bin, /usr/bin etc.) would break many things with nosuid. Mounting directories like /home with nosuid may be useful but then it wouldn’t have any effect on the other setuid binaries.
We could use one of the methods above to remove setuid bits from most binaries and then use nosuid on directories like /home.
Using multiple different mount options for increased security would be a whole other topic.
Alternatively, could we identify suid binaries and then run chmod o-x [0] on them. I.e. o (“others”) (i.e. user user) may not execute them. In result only root could execute suid binaries but non-root could not. Would that help to reduce the risk of suid binaries?
Tested. It’s possible to remove executable permission for binaries for others (user user) while root can still execute the binary.
Either,
chmod all suid binaries except a whitelist [1], OR
we manually maintain a blacklist of suid binaries (the ones shipped by default but we want to disable). [2]
That would prevent non-root users from executing suid binaries.
A dpkg hook or something could re-apply these changes on upgrades. But not great since malware could wait for the upgrade to happen and there would a vulnerable time window between package upgrade and suid re-disable.
Maybe ACL or something similar could enforce keeping the file permissions (chmod o-x) during upgrades?
[0] Or chmod og-x or chmod o-rx or chmod og-rwx or something?
[1] In a noroot boot mode, even sudo could be set to chmod o-x since during such sessions the user decided to trade sudo not being accessible for better security.
[2] On top, there could be a tool to search and warn against newly installed suid binaries.
The entire point of SUID binaries are so unprivileged users can do some privileged operations so why not just remove the SUID bit instead?
This seems like the best approach as it would have minimal breakage.
The whitelist idea would be more effective in ensuring only needed SUID binaries can be executed but then it would break binaries a user creates themselves.
Unless there’s something we can make that checks if a new SUID binary has been created and says “SUID binaries are not allowed by default, see https://www.whonix.org/wiki/wiki_page and whitelist your binary” or something like that.
Didn’t have this in mind indeed. Therefore also chmod u-s would do. chmod u-s would not limit root from using these binaries.
However, making these binaries non-executable (and/or non-readable) might have another advantage: we fail closed and loudly (with error message) rather than leading to stranger to interpret issues, such as:
Added some refactoring, debugging, code simplification, and stylistic changes. Btw I make no claim that my code style is superior in any form but it is easier to grasp by my participial biased brain. Hope that is ok.
Should we just skip symlinks in nosuid mode? That is what I have implemented for now. Config entries such as /bin/ nosuid should catch such entries anyhow since these would be in other folders which config will “run nosuid” on anyhow. Except, it would miss custom made symlinks linking to folders which we don’t parse.
Otherwise we could resolve the symlink (maybe using realpath) and then “run nosuid” on wherever it links to even if a weird path such as hypothetically /usr/share/somesuid/somesuidbinary. Such entries however would likely never be updated/removed when the symlink changed.
Let’s not use add_statoverride_entry for both, case of nosuid and regular modes. That is because when we’re using nosuid that is totally different. In that case we iterate over folders. For other modes, we don’t iterate but set specific modes. There is not any code that would be repeated. Therefore made that add_nosuid_statoverride_entry.
This is fixed.
For now, no longer doing this. That was only only a bug when using nosuid entries. Not a deliberate choice in config. If this was wanted, another entry should be added to config (similar to /boot/ 0700 root root but would have to think about mode/owner/group but there may be no fitting config file and does not seem important).
It’s fixed. But by fixing the parsing of /lib it’s also very slow. Needs 3 minutes to finish.
(And only using dry mode, i.e. not running the dpkg-statoverwrite --add / --remove commands yet actuary. I don’t think that would change much to the worse, though.)
time sudo usr/lib/security-misc/permission-hardening
The xtrace (sudo bash -x usr/lib/security-misc/permission-hardening or setting set -x) at the top of the script looks efficient. We don’t call stat a million times and we are using bash built-ins.
Parsing /lib/ and /usr/lib/ takes far most of the time.
Fortunately /lib does not have any suid binaries by default on my system.
Maybe we can mount /lib as nodev,nosuid. As per Kurt Seifried - LASG / Installation we can. Then we could remove /lib from permission hardening config and safe 1 minute.