Skip to content

AD3013: ValidateRunpath

Summary

Property Value
ID AD3013
Name ValidateRunpath
Category Security
Severity Warning
Applies to ELF (Linux/Unix)

Description

This rule validates that DT_RUNPATH entries in ELF binaries do not contain dangerous path specifications that could be exploited for library injection attacks.

Why This Matters

Misconfigured RUNPATH entries can allow attackers to inject malicious libraries into trusted applications, gaining code execution with the application's privileges. Even well-intentioned paths can create security vulnerabilities.

Library Injection Attack Flow

Vulnerable RUNPATH: /tmp/mylibs:/opt/app/lib

Attack:
  1. Attacker places malicious libc.so.6 in /tmp/mylibs
  2. Victim runs the application
  3. Linker finds libc.so.6 in /tmp/mylibs first
  4. Attacker's code executes with victim's privileges

Dangerous Path Patterns

Pattern Risk Example
World-writable dirs Anyone can inject /tmp, /var/tmp
Relative paths CWD-dependent ./lib, ../libs
Empty components Treated as "." ::/lib, /lib::
$ORIGIN in SUID Complex attacks $ORIGIN/../lib

Why Relative Paths Are Dangerous

RUNPATH: ./lib

Attacker:
  1. Creates /tmp/attack/lib/libfoo.so (malicious)
  2. Tricks user to run: cd /tmp/attack && /path/to/binary
  3. Binary loads ./lib/libfoo.so from /tmp/attack/lib
  4. Attacker code runs

$ORIGIN Risks in SUID Binaries

$ORIGIN expands to the executable's directory:

Normal binary with $ORIGIN/../lib: Generally safe

SUID binary with $ORIGIN:
  Problem: Attacker might control the symlink structure

  /home/attacker/mydir/suid_binary -> /real/suid_binary
  /home/attacker/mydir/lib/malicious.so

  Some linker versions may be vulnerable to path manipulation

Empty Path Component Danger

RUNPATH: /opt/lib::/usr/lib
                ^^-- Empty = current directory

Same as: /opt/lib:.:usr/lib

Attacker controls current directory = attacker controls library

Safe RUNPATH Practices

Practice Why
Absolute paths only No CWD dependence
Owned by root Can't be modified
Not world-writable No unauthorized writes
Minimal entries Smaller attack surface
Avoid $ORIGIN in SUID Complex edge cases

Verification Commands

# Check RUNPATH
readelf -d binary | grep RUNPATH

# Check directory permissions
ls -la /path/from/runpath

# Verify no world-writable
find /path/from/runpath -perm -002 -type d

Dangerous RUNPATH entries can allow attackers to inject malicious libraries:

  • Relative paths (./lib, ../lib): Depend on current working directory
  • World-writable directories: Any user can place malicious libraries there
  • $ORIGIN in SUID binaries: Can be exploited in certain scenarios
  • Empty entries: Treated as current directory

What This Rule Checks

  1. No empty path components (::/lib or /lib::)
  2. No relative paths without $ORIGIN
  3. No paths to world-writable directories like /tmp
  4. Validates $ORIGIN usage in setuid/setgid binaries

Performance Considerations

Using safe RUNPATH entries has no performance impact:

Aspect Impact
Library loading Identical
Runtime overhead None
Path resolution Same speed

Why there's no overhead: - RUNPATH is resolved once at program start - Absolute paths vs relative paths have same lookup cost - Safe paths don't add any runtime checks

This is purely about security—there is no performance trade-off.

Resolution

Use absolute paths

# Good: Absolute path
gcc -o myapp myapp.c -Wl,-rpath,/opt/mylibs -Wl,--enable-new-dtags

# Good: $ORIGIN for relocatable installations
gcc -o myapp myapp.c -Wl,-rpath,'$ORIGIN/../lib' -Wl,--enable-new-dtags

Avoid dangerous patterns

# Bad: Relative path without $ORIGIN
gcc -o myapp myapp.c -Wl,-rpath,./lib

# Bad: World-writable directory
gcc -o myapp myapp.c -Wl,-rpath,/tmp/mylibs

Example

Fail: RUNPATH contains relative or dangerous paths

RUNPATH: ./lib:/tmp/mylibs

Pass: RUNPATH uses safe absolute paths

RUNPATH: /opt/myapp/lib:$ORIGIN/../lib

See Also