https://github.com/masterwok/sohps
Scans ELF binaries for insecure search paths by emulating the Linux dynamic linker.
https://github.com/masterwok/sohps
elf elf-parser exploit-development linux security-tools vulnerability-scanner
Last synced: about 1 month ago
JSON representation
Scans ELF binaries for insecure search paths by emulating the Linux dynamic linker.
- Host: GitHub
- URL: https://github.com/masterwok/sohps
- Owner: masterwok
- License: mit
- Created: 2026-05-26T20:14:21.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-28T03:18:23.000Z (2 months ago)
- Last Synced: 2026-05-28T05:11:11.275Z (2 months ago)
- Topics: elf, elf-parser, exploit-development, linux, security-tools, vulnerability-scanner
- Language: Go
- Homepage:
- Size: 40 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# sohps: Shared Object Hijack Path Scanner
`sohps` is a security auditing tool designed to identify shared object hijack vulnerabilities in Linux ELF binaries and AppImage containers. By simulating the behavior of the Linux dynamic linker (`ld.so`), sohps maps how binaries resolve their shared object dependencies and uncovers vulnerabilities introduced by insecure search paths, writable directories, or misconfigured environments.
---
## Vulnerability Classes
`sohps` categorizes findings into several distinct attack vectors, providing researchers with immediate context for exploitation:
| Category | Description |
| :--- | :--- |
| **Implicit CWD** | Triggered by empty entries in `RPATH` or `RUNPATH` (e.g., a trailing colon). The linker falls back to the Current Working Directory, allowing an attacker to drop a malicious library. |
| **Environment Poisoning** | Triggered by insecure script wrappers (e.g., in AppImages) that blindly append to `LD_LIBRARY_PATH`. If the environment variable is unset, the trailing colon falls back to the CWD. |
| **$ORIGIN Hijack** | Vulnerabilities within directories resolved via the `$ORIGIN` macro. If the binary's directory or its relative neighbors are writable, the dependency tree can be hijacked. |
| **Writable Path** | Absolute search paths (system or user-defined) that are writable by the current user, allowing for direct replacement or tree recreation. |
| **Relative Path** | Hardcoded relative paths (e.g., `lib/`) that resolve against the CWD rather than the binary's location. |
| **Absolute Path** | Occurs when a `DT_NEEDED` entry contains a `/` (e.g., `/tmp/lib.so`). The linker bypasses all search paths and loads the file directly from the specified location. |
| **System Preload** | Checks for writable or missing `/etc/ld.so.preload` files, which can lead to system-wide hijacking of every executed process. |
---
## Capabilities
`sohps` simulates the following `ld.so` logic:
- **Native AppImage Support**: Extracts and analyzes Type-2 AppImages natively (using a pure-Go SquashFS implementation) to detect vulnerable `AppRun` wrappers and insecure internal binaries.
- **Linker Macro Expansion**: Full support for `$ORIGIN`, `$LIB`, and `$PLATFORM` expansion based on the target binary's architecture.
- **Search Path Prioritization**: Correctly handles the precedence of `DT_RPATH` vs. `DT_RUNPATH` and their interaction with `LD_LIBRARY_PATH`.
- **Recursive Configuration Parsing**: Parses `/etc/ld.so.conf` and all nested `include` directives to build an accurate map of system search paths.
- **HWCAP Resolution**: Simulates hardware capability searches (e.g., `tls/aarch64/aarch64`) to identify "hidden" search directories.
- **AT_SECURE Awareness**: Automatically detects SUID/SGID bits and ignores untrusted paths (relative paths, `$ORIGIN`) just as the kernel would.
- **Symlink De-duplication**: Efficiently scans large directories by resolving symlinks and analyzing each unique binary only once.
- **Transitive Analysis**: Fully maps the transitive dependency tree to uncover vulnerabilities deep within required libraries.
---
## Shortcomings & Limitations
While `sohps` is a powerful tool, users should be aware of its technical boundaries:
- **Static vs. Runtime**: `sohps` performs advanced static analysis of the ELF structure and filesystem permissions. It does not account for libraries loaded dynamically at runtime via `dlopen()`.
- **User-Space Perspective**: Permission checks are performed as the user running the tool. Findings may differ when run as a low-privileged user vs. root.
- **Kernel-Linker Variance**: While `sohps` mimics standard `glibc` behavior, custom or highly specialized linkers (like those in embedded systems or `musl`) may have subtle behavioral differences.
- **Environment Variables**: Outside of simulated flags like `--ld-path`, `sohps` does not automatically account for every possible environment variable that could influence `ld.so`.
---
## Installation & Building
`sohps` is written in Go and requires version 1.21 or later.
### Local Build
```bash
go build -o sohps ./cmd/sohps/main.go
```
### Cross-Platform Compilation
You can compile `sohps` for different architectures using Go's built-in cross-compilation support.
**Build for x86_64 Linux:**
```bash
GOOS=linux GOARCH=amd64 go build -o sohps_amd64 ./cmd/sohps/main.go
```
**Build for AArch64 (ARM64) Linux:**
```bash
GOOS=linux GOARCH=arm64 go build -o sohps_arm64 ./cmd/sohps/main.go
```
---
## Testing & Verification
### Running Unit Tests
The project includes a comprehensive suite of unit tests for all internal packages:
```bash
go test ./internal/... -v
```
### The `testenv` Sandbox
The `testenv/` directory contains a specialized research environment that generates intentionally vulnerable binaries. This allows researchers to verify the tool's detection logic against real-world scenarios.
1. **Build the Sandbox**:
```bash
cd testenv
make all
```
2. **Scan the Sandbox**:
```bash
./sohps --root /tmp/sohps_test testenv/bin
```
This will enumerate all 6 primary vulnerability classes, including **System Preload**, providing a clear benchmark for the tool's capabilities.
---
## Usage
```bash
# Scan a single binary
./sohps /usr/bin/clang-query
# Scan a directory recursively
./sohps /usr/local/bin
# Specify a custom system root (e.g. for firmware or rootfs analysis)
./sohps --root /mnt/rootfs /mnt/rootfs/bin
# Simulate an attacker-controlled LD_LIBRARY_PATH
./sohps --ld-path /tmp/evil /usr/bin/sudo
# Verbose scan (show safe targets) with no color for logging
./sohps -v -nc / > scan_report.txt
```
## Example
```
$ ./sohps testenv/bin
[*] Scanning testenv/bin...
[*] Found 14 ELF binaries. Starting analysis...
[*] testenv/bin/test_implicit_cwd
[!] Implicit CWD
Vulnerable Path : Empty Path (Implicit CWD)
Resolved Dir : Runtime Current Working Directory
Action : CWD HIJACK: Execute binary from an attacker-controlled writable directory containing a malicious payload.
Libraries (4) : libcustom.so, libcustom.so, libc.so.6, libc.so.6
[*] testenv/bin/test_colon_split
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/one
Resolved Dir : /tmp/sohps_test/one/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/one/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/two
Resolved Dir : /tmp/sohps_test/two/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/two/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/one
Resolved Dir : /tmp/sohps_test/one/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/one/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/two
Resolved Dir : /tmp/sohps_test/two/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/two/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] testenv/bin/test_nodeflib
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/nodeflib
Resolved Dir : /tmp/sohps_test/nodeflib/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/nodeflib/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/nodeflib
Resolved Dir : /tmp/sohps_test/nodeflib/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/nodeflib/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] testenv/bin/test_needed_abs
[!] Absolute Path
Vulnerable Path : Hardcoded Absolute Path
Resolved Dir : /tmp/sohps_test
Action : OVERWRITE: Delete existing library and replace with payload: rm /tmp/sohps_test/libcustom.so && mv payload.so /tmp/sohps_test/libcustom.so
Libraries (1) : /tmp/sohps_test/libcustom.so
[*] testenv/bin/test_missing_writable
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/missing
Resolved Dir : /tmp/sohps_test/missing/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test/missing). Recreate full path and drop payload at: /tmp/sohps_test/missing/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/missing
Resolved Dir : /tmp/sohps_test/missing/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test/missing). Recreate full path and drop payload at: /tmp/sohps_test/missing/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] testenv/bin/test_relative
[!] Relative Path
Vulnerable Path : ./lib
Resolved Dir : /home/foo/dev/sohps/lib/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/home/foo/dev/sohps). Recreate full path and drop payload at: /home/foo/dev/sohps/lib/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] Relative Path
Vulnerable Path : ./lib
Resolved Dir : /home/foo/dev/sohps/lib/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/home/foo/dev/sohps). Recreate full path and drop payload at: /home/foo/dev/sohps/lib/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] testenv/bin/test_origin
[!] $ORIGIN Hijack
Vulnerable Path : $ORIGIN/../lib
Resolved Dir : /home/foo/dev/sohps/testenv/lib/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/home/foo/dev/sohps/testenv). Recreate full path and drop payload at: /home/foo/dev/sohps/testenv/lib/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] $ORIGIN Hijack
Vulnerable Path : $ORIGIN/../lib
Resolved Dir : /home/foo/dev/sohps/testenv/lib/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/home/foo/dev/sohps/testenv). Recreate full path and drop payload at: /home/foo/dev/sohps/testenv/lib/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] testenv/bin/test_rpath
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/rpath
Resolved Dir : /tmp/sohps_test/rpath/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/rpath/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/rpath
Resolved Dir : /tmp/sohps_test/rpath/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/rpath/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] testenv/bin/test_appimage.AppImage
[!] Environment Poisoning
Vulnerable Path : Internal AppRun:1
Resolved Dir : Runtime Environment
Action : CWD HIJACK: Script poisons LD_LIBRARY_PATH when empty. Ensure it is unset, drop malicious library in CWD, and execute.
Libraries (2) : ld-linux-aarch64.so.1 (Proxy Required)
libc.so.6
[*] testenv/bin/test_trailing_colon
[!] Writable Path
Vulnerable Path : /tmp
Resolved Dir : /tmp/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp). Recreate full path and drop payload at: /tmp/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[!] Implicit CWD
Vulnerable Path : Empty Path (Implicit CWD)
Resolved Dir : Runtime Current Working Directory
Action : CWD HIJACK: Execute binary from an attacker-controlled writable directory containing a malicious payload.
Libraries (1) : libc.so.6
[*] testenv/bin/test_writable_file
[!] Writable Path
Vulnerable Path : /tmp/sohps_writable_file
Resolved Dir : /tmp/sohps_writable_file
Action : RECREATE: Rename writable parent (/tmp) and recreate path to drop payload: mv /tmp/sohps_writable_file && mkdir -p /tmp/sohps_writable_file
Libraries (1) : libcustom.so
[!] Writable Path
Vulnerable Path : /tmp/sohps_writable_file
Resolved Dir : /tmp/sohps_writable_file/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp). Recreate full path and drop payload at: /tmp/sohps_writable_file/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] testenv/bin/test_writable_path
[!] Writable Path
Vulnerable Path : /tmp/sohps_test
Resolved Dir : /tmp/sohps_test/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[!] Writable Path
Vulnerable Path : /tmp/sohps_test
Resolved Dir : /tmp/sohps_test
Action : OVERWRITE: Delete existing library and replace with payload: rm /tmp/sohps_test/libcustom.so && mv payload.so /tmp/sohps_test/libcustom.so
Libraries (1) : libcustom.so
[*] testenv/bin/test_runpath
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/runpath
Resolved Dir : /tmp/sohps_test/runpath/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/runpath/tls/aarch64/aarch64/libcustom.so
Libraries (1) : libcustom.so
[!] Writable Path
Vulnerable Path : /tmp/sohps_test/runpath
Resolved Dir : /tmp/sohps_test/runpath/tls/aarch64/aarch64
Action : RECREATE: Writable parent (/tmp/sohps_test). Recreate full path and drop payload at: /tmp/sohps_test/runpath/tls/aarch64/aarch64/libc.so.6
Libraries (1) : libc.so.6
[*] Progress: [14/14] 100.0%
[*] Scan complete.
```