Using LD_PRELOAD to exploit Binaries

Hi all of you,

I have done some privilege escalation using “fake” compiled libraries in the past to exploit binaries compiled with RPATH (run-time search path hardcoded in an executable or library ) where the the SUID bit is not dropped and that motivated me to learn about more ways to “attack” dynamically linked binaries.
Therefore I would like to understand more about using LD_PRELOAD to change the intended execution of a binary.

I have seen ippsec using LD_PRELOAD in the HTB Cyber Mayhem Video (Process Monitoring with Snoopy) and I started reading more about it and tested out some things. I think I understand the concept and benefits behind it. It seems to be a common approach but I’m yet to find a source which states exactly when (ab)using LD_PRELOAD is even possible and I have encountered some strange behaviours which I couldn’t figure out by simply searching for it on the web.

To show a little part of my efforts so far: I have studied the man page of ld.so man ld.so and I found out about the Secure-execution mode and how the linker determines if it should be set:

Effect of secure-execution mode:

“For security reasons, if the dynamic linker determines that a binary should be run in secure-execution mode, the effects of some environment variables are voided or modified, and furthermore those environment variables are stripped from the environment, so that the program does not even see the definitions.”

As LD_PRELOAD simply beeing an environment variable, I see that it could easily be voided or stripped from the environment.

Reasons for setting AT_SECURE entry (enables secure-execution mode):

  • for suid, guid binaries
  • a process with a non-root user ID executed a binary that conferred capabilities to the process.
  • a nonzero value may have been set by a Linux Security Module.

I know that LD_PRELOAD only works for dynamically loaded ELF binaries and obviously not for statically linked ones. I also know that the SUID,GUID will get dropped if executed with LD_PRELOAD.

I nonetheless tried to LD_PRELOAD my own .so on a binary - with the suid bit set - overwriting the system function getppid. I expected it to simply drop the suid bit and still keep executing the program normally with my version of getppid (simply return 1, PID of init process).
I used ldd to see that its actually using my library which was correct. I ltraced it ltrace -S -l /path/to/.so /path/to/binary (which drops suid bit anyways aswell) to see whats happening:

getppid( <unfinished ...> 
SYS_getppid(0xbfc724c,.. and so on) = 3672 (PPID)
< ... getppid resumed> )  = 3672 (PPID) 

Now to my open questions:

  • What is happening exactly in my attempt above to LD_PRELOAD my library? It seems to me that it’s executing it but it’s still falling back to the system implementation of it?
  • Are there any other factors which could restrict the usage of LD_PRELOAD which I have not mentioned above?
  • Is it possible to know/check beforehand if LD_PRELOAD will work (without restrictions) on a binary file (without having root privileges) besides checking if it’s an suid or guid binary? And if so, what is the quickest and best way to check it?

I’m open to all kinds of information or help, please correct me if I have made mistakes or if any information of mine is false.

Thank you in advance, I have actually learned a couple of things in the process of writing this discussion :wink: