Eliminate LD_PRELOAD and other Dangerous Environment Variables

I’ve been experimenting with a way to eliminate LD_PRELOAD (and other similar variables). We can create a library that checks if any dangerous variables have been set and then force it to exit if they are. This would be globally preloaded via /etc/ld.so.preload so the check is executed at the start of all applications.

https://paste.debian.net/hidden/f3a9db80/

Compile this with:

gcc rootkit-detect.c -shared -fpic -Wl,-init,init -o rootkit-detect.so

And append /etc/ld.so.preload with:

/usr/lib/rootkit-detect.so

I’ve tested this and it does work. I think it should be included in Whonix as it does eliminate a large vector for user space rootkits. It does hurt debugging a bit but the protection can easily be disabled by root.

Edit: Actually, I forgot about ld-system-preload-disable which would bypass this. Maybe there’s another way to preload this library?

2 Likes

It’s an interesting idea!

Could you please describe the threat model? I guess it assumes a compromise of a user account (probably most likely user user?) and then malware under that account using LD_PRELOAD tricks to sniff what other applications are doing (such as sniffing keystrokes, passwords, memory?)

I guess complexity is low enough (which is a very good thing - I can ask some people for code review since small enough). Doesn’t need its own Makefile. Could be added to package security-misc and compiled during postinst similar to bindp.postinst?

There are a few legitimate uses of LD_PRELOAD in Whonix and Kicksecure:

  • uwt uses torsocks (for stream isolation) (torsocks as per its man page is a wrapper that adds the torsocks LD_PRELOAD) and bindp (to change local listen interface).
  • Hardened Malloc

These are all based on LD_PRELOAD. Therefore do you think you could add some kind of root configurable whitelist .d mechanism?

Other things needed:

  • license header
  • some test
    • whitelisted.so ld preload lib saying “i am here and this is good”
    • blacklisted-test.so ld preload lib saying “i am here and this is an error”
    • then the test script could look if the whitelisted lib is still there and the blacklisted one got filtered out. (We don’t want to break torsocks, stream isolation, bindp, hardeneed malloc.)

Nitpick: maybe shouldn’t be called rootkit-detect. While it would be great to have a fully featured rootkit detection installed by default, and while this is really cool, it doesn’t cover all of that. There are many ways to implement rootkits (such as kernel rootkits) which wouldn’t be covered here.

ld-system-preload-disable requires root if I am not mistaken. Therefore out of threat model because…

Right?

I don’t think so. Not when going the LD_PRELOAD route. The LD_PRELOAD mechanism is “somewhat limited”. System wide preloading is what /etc/ld.so.preload is for. We discussed options for LD_PRELOAD in Hardened Malloc - Hardened Memory Allocator and didn’t find too many options for that.

Related:

Unless there is any different approach of environment variable filtering / enforcement. I might have more ideas (or at least contribute to brainstorming) once you described the threat model.

Environment variables have a parent / child relationship. Environment variables can be set by the parent process and are inherited by the child process. I guess the threat model here is that the parent shall be untrusted. The parent might be compromised and set malicious environment variables. Therefore we need to look closer at the parent. Who is the actual parent process here? Let’s look at pstree.

Maybe one of multiple of the following programs such as systemd, systemd user sessions, X login sessions, wayland sessions, getty, xfce4-terminal emulator, bash, etc. should have an option (set by --parameter or environment variable) no-unsafe-environment-variables or so which would then prohibit setting dangerous environment variables such as LD_PRELOAD? I wonder if this was ever considered, discussed somewhere?

It’s all about the threat model. I guess at some point there was a browser exploit. User user compromised. Some malware process running under user user which can now silently do everything that user user can do too. If in this case the login session was started with no-unsafe-environment-variables could that be effective?

Apparmor can filter environment variables. Would it work for this use case?

1 Like

A compromised user account has the ability to set the LD_PRELOAD (or similar) environment variable. This basically allows an attacker to redefine syscalls and spy on whatever they want. It is the most common method of user space rootkits. A few examples I found within a few seconds of searching:

It is trivial to create a rootkit via LD_PRELOAD. This is somewhat similar to the ptrace syscall which we already restrict in security-misc via the YAMA LSM.

LD_PRELOAD and co. are ignored when executing setuid binaries to prevent obvious privilege escalations. MAC systems can also enforce some restrictions (AppArmor’s isn’t great though, see below).

https://manpages.debian.org/buster/manpages/ld.so.8.en.html#Secure-execution_mode

However, when considering sandbox-app-launcher, an attacker would only be able to set LD_PRELOAD within the sandbox (not outside) unless they also have a sandbox escape. This would still be useful for defense-in-depth and for programs that are not sandboxed though.

I likely can.

No, unless you revoke the setuid bit from bwrap which we could do but might be too restrictive and annoying.

No, AppArmor’s environment scrubbing only applies to profile transitions. E.g. a program executing under the init-systemd profile would have some dangerous environment variables erased when transitioning to the torbrowser profile. This would not cover programs executing within the same profile though. E.g. if program X and Y are both under the init-systemd profile and X sets LD_PRELOAD, it will also leak over to program Y. AppArmor’s environment scrubbing is only feasible when everything has it’s own profile.

1 Like

madaidan via Whonix Forum:

ld.so(8) — manpages — Debian buster — Debian Manpages

Can we globally enable secure-execution mode for user user? Asked:

Should ask somewhere else too if no suitable answer comes up there such as oss-security mailing list

However, when considering sandbox-app-launcher, an attacker would only be able to set LD_PRELOAD within the sandbox (not outside) unless they also have a sandbox escape. This would still be useful for defense-in-depth and for programs that are not sandboxed though.

Very much so.

No, unless you revoke the setuid bit from bwrap which we could do but might be too restrictive and annoying.

Should consider removing setuid from bwrap.

Imagine there was any widespread use of /etc/ld.so.preload to enforce preloading of security enhancing ld shared objects. In that case bwrap setuid allowing ignoring that file would be a security issue. This would be an abuse of bwrap to lower the security of the system.

Are there any other similar cases where that would be an issue?

No, AppArmor’s environment scrubbing only applies to profile transitions. E.g. a program executing under the init-systemd profile would have some dangerous environment variables erased when transitioning to the torbrowser profile. This would not cover programs executing within the same profile though. E.g. if program X and Y are both under the init-systemd profile and X sets LD_PRELOAD, it will also leak over to program Y. AppArmor’s environment scrubbing is only feasible when everything has it’s own profile.

Currently when using apparmor-profile-everything init-systemd apparmor profile is everyone’s apparmor profile. Could there be some sort different apparmor profile? I.e. when a user under apparmor profile init-systemd runs a program it would scrub environment and transition to apparmor-profile-everything?

(That apparmor profile might then be named apparmor-profile-everything.)

If that was possible, that would solve the issue for applications not having their own profile. But what about applications having their own profile? Could there be an intermediary apparmor profile which is used for environment scrubbing before transition to actual apparmor profile?

Worth contacting apparmor upstream about this? Seems like an interesting
feature.

2 Likes

Even if we do this, I’m not sure if there would be other ways to achieve the same thing. At the very least, the prerequisites for this feature working properly would be:

  • Unprivileged user namespaces disabled.
  • No setuid binaries allowing mount namespace creation.

They already have plans to overhaul the existing environment scrubbing functionality.

1 Like

Can be considered.

Would be good if you could open a new forum thread for this.

SUID disabling feature will most likely be enabled by default one day.

2 Likes

They’re already disabled on Debian.

Is there any blocker stopping it from being enabled now?

1 Like

Answered here:
SUID Disabler and Permission Hardener - #64 by Patrick

1 Like