Wrapping Package Management With Immutability
Bear Giles | April 7, 2011Continuing the security theme there is a technique that I’ve been playing with but don’t have something solid enough to release as an open source project yet.
Modern filesystems usually have three common features: journaling, access control lists and extended attributes. We’re specifically interested in the “immutable” extended attribute. If this flag is set then nobody, not even root, can modify or delete this file.
At this point I we need to take a step back and do something I should have done a few posts ago…
Threat Model
The threat model for us is pretty straightforward.
Fumble-fingered admins. We’ve all done it. The key is a solid practice of always doing the least we can do (e.g., changing permissions for a single file instead of an entire directory if that’s all we need to do) and using ‘defense in defect’ so a single mistake can’t do devastating damage.
Disgrunted insider. This person has legitimate access and a grudge. He has limited technical skills but the ability to find stuff on Google. He might get lucky and find something sophisticated but in general we can treat he as a casual intruder. Important: not always! If he’s really angry he may become a motivated attacker.
Casual intruder. This is the classic ‘script kiddie.’ He’ll use tools he got from others but if he’s stymied he’ll probably just move on to the next target. There are millions of unsecured systems out there after all.
Motivated and knowledgeable attacker. Or a motivated person plus a knowledgeable person. This person will not give up after a few failed attacks and has better technical skills than your staff. Or if you prefer, this person isn’t surprised by anything in these posts and already knows ways to circumvent them. You can make life more difficult for this person but ultimately no technical-only solution will be enough. Always keep in mind that technical approaches alone will never be enough against a sufficiently motivated and knowledgeable attacker.
It’s also important to remember that there are tools in the wild that have been written by knowledgeable attackers. You can’t assume that a script kiddie is always using an unsophisticated attack.
Technical Approaches
That said we can discuss three technical approaches that will work against casual attackers.
The /usr mountpoint approach
[Update 5/7/2011. I tried to use a separate partition for /usr on Ubuntu 11.04 but was then unable to execute anything under /usr/bin. I don’t know what the problem is – permissions should not have been an issue. Anyway this approach is nonviable unless I put in the effort to figure out why this approach isn’t working.]
This approach is mentioned in passing in the Debian documentation. The setup is straightforward:
- perform minimal installation.
- create partition and copy everything under /usr into this partition.
- mount that partition over /usr.
- install everything else.
You can now mount /usr as a read-only partition. Package management can proceed as normal with the following apt.conf stanza:
- DPkg
- {
- Pre-Invoke { "mount -oremount,rw /usr"; };
- Post-Invoke { "mount -oremount,ro,nodev /usr"; };
- };
DPkg { Pre-Invoke { "mount -oremount,rw /usr"; }; Post-Invoke { "mount -oremount,ro,nodev /usr"; }; };
I’ve used this in the past with mixed success. As I recall the two problems were running out of the space I allocated for the partition and package manager confusion after the partition wasn’t remounted after a crash. The first is now handled by larger disks and ‘resize’ commands and the second is now handled by journaling filesystems that make it less likely the partition won’t come back up after a crash.
Attacks
- Remount the partition rw. A sophisticated attacker can do this via system calls and finish his work in less than a second.
- Mount a unionfs on top of the /usr partition.
The brute force chattr approach
This approach is similar to the last one. We add the immutable extended attribute to all files within the partition. Package management can proceed as normal with the following apt.conf stanza:
- DPkg
- {
- Pre-Invoke { "chattr -R -i /bin /lib /sbin /usr"; };
- Post-Invoke { "chattr -R +i /bin /lib /sbin /usr"; };
- };
DPkg { Pre-Invoke { "chattr -R -i /bin /lib /sbin /usr"; }; Post-Invoke { "chattr -R +i /bin /lib /sbin /usr"; }; };
This works but is an extremely crude approach that can touch hundreds of thousands of files unnecessarily. This violates the “do the least you need to do” principle since we rarely need to touch more than a few hundred files when installing a package.
Attacks
- Change the extended attributes back. A sophisticated attacker can do this via system calls and only for the files that he’s modifying.
- Mount a unionfs on top of these partitions.
The dpkg wrapper approach
A third approach uses the fact that Debian/Ubuntu uses a nested set of applications for package management. The user-facing applications like apt are responsible for dependency management and defer the actual installation and removal of packages to dpkg (for Debian PacKaGe). We can do a lot by wrapping this application.
If we only access dpkg via apt or the package manager we only need to worry about four operations:
dpkg –unpack package.deb – run the ‘preinst’ scripts and then unpack the data archive into the file system. We can do our work after dpkg has finished.
dpkg –configure package – configure the package and run the ‘postinst’ scripts. The immutable flag can be set on everything but the conffiles after the ‘postinst’ scripts have run. We can do our work after dpkg has finished.
dpkg –remove package – run the ‘prerm’ scripts, delete everything but the conffiles, then run the ‘postrm’ scripts. The immutable flag would need to be removed before the ‘prerm’ scripts are run. We can do our work before dpkg has run.
dpkg –purge package – identical to ‘–remove’ except that even the conffiles are deleted. We can do our work before dpkg has run.
We need to support a few additional operations if the system administrator runs dpkg directly.
It isn’t too hard to write a C program to do all of this. There are libraries for MD5 and SHA1 checksums, Linux has abstracted extended permissions with <linux/fs.h> and the fgetflags() and fsetflags() system calls to avoid dependencies on using a specific filesystem. (Many of us prefer JFS or XFS to Ext4.)
You can use static linking to knock out certain attacks.
The biggest roadblock to releasing the work is the need to set up autoconf and automake. That’s not a problem if you’re doing it for your own system or are familiar with the tools but I haven’t set up a C project from scratch in several years.
Attacks:
same as above.