So recently I wrote a Linux Security Module (LSM) which would deny execution of commands, unless an extended attribute existed upon the filesystem belonging to the executables.
whitelist-LSM worked well, but it soon became apparent that it was a little pointless. Most security changes are pointless unless you define what you're defending against - your "threat model".
In my case it was written largely as a learning experience, but also because I figured it seemed like it could be useful. However it wasn't actually as useful because you soon realize that you have to whitelist too much:
redis-serverbinary must be executable, to the
redis-user, otherwise it won't run.
/usr/bin/gitmust be executable to the
In short there comes a point where user
alice must run executable
alice can run it, then so can
mallory. At which point you realize the exercise is not so useful.
Taking a step back I realized that what I wanted to to prevent was the execution of unknown/unexpected, and malicious binaries How do you identify known-good binaries? Well hashes & checksums are good. So for my second attempt I figured I'd not look for a mere "flag" on a binary, instead look for a valid hash.
Now my second LSM is invoked for every binary that is executed by a user:
- When a binary is executed the
sha1hash is calculated of the files contents.
- If that matches the value stored in an extended attribute the execution is permitted.
- If the extended-attribute is missing, or the checksum doesn't match, then the execution is denied.
In practice this is the same behaviour as the previous LSM - a binary is either executable, because there is a good hash, or it is not, because it is missing or bogus. If somebody deploys a binary rootkit this will definitely stop it from executing, but of course there is a huge hole - scripting-languages:
/usr/bin/perlis whitelisted then
/usr/bin/perl /tmp/exploit.plwill succeed.
/usr/bin/pythonis whitelisted then the same applies.
Despite that the project was worthwhile, I can clearly describe what it is designed to achieve ("Deny the execution of unknown binaries", and "Deny binaries that have been modified"), and I learned how to hash a file from kernel-space - which was surprisingly simple.
(Yes I know about
EVM- this was a simple project for learning purposes. Public-key signatures will be something I'll look at next/soon/later. :)
Perhaps the only other thing to explore is the complexity in allowing/denying actions based on the user - in a human-readable fashion, not via UIDs. So
www-data can execute some programs,
alice can run a different set of binaries, and
git can only run
Of course down that path lies apparmour, selinux, and madness..