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
$ORIGINin SUID binaries: Can be exploited in certain scenarios- Empty entries: Treated as current directory
What This Rule Checks¶
- No empty path components (
::/libor/lib::) - No relative paths without
$ORIGIN - No paths to world-writable directories like
/tmp - Validates
$ORIGINusage 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
Pass: RUNPATH uses safe absolute paths
See Also¶
- AD3012: DoNotUseRpath - Avoid deprecated RPATH