- Status Closed
-
Assigned To
cbay - Private
Opened by web_researcher - 18.08.2026
Last edited by cbay - 19.08.2026
FS#455 - Cross-Tenant Write Primitive via World-Writable Shared Scratch Directories
Target: alwaysdata shared web node `http22` (185.31.41.42)
Severity: Medium
Class: CWE-377 Insecure Temporary File / CWE-59 Improper Link Resolution (symlink race)
Status: Verified — re-verified live 2026-08-18, zero false positives
—
## Important Clarification Before Reading:
alwaysdata's bug bounty policy explicitly acknowledges that `/tmp` is a shared directory. This report is not about the known read-side exposure of `/tmp` (already tracked internally as FS#363 /389/393/417/418). This report documents a new and distinct primitive: a confirmed cross-tenant write capability via symlink planting in the shared scratch space — something that goes beyond the known read exposure and has not been previously reported or acknowledged.
—
## Summary:
On the shared web node `http22`, the scratch directories `/tmp`, `/var/tmp`, and `/dev/shm` are world-writable (mode `1777`) and shared across all tenants on the node. From any tenant's PHP using only the standard library, it is possible to:
- create new files at predictable paths in the shared scratch space
- pre-plant symlinks at predictable names that point to arbitrary files
- write through a symlink to its target file
The sticky bit (`1` in `1777`) correctly prevents modification or deletion of files already owned by other tenants. The attack surface is therefore the classic CWE-377 symlink race: pre-plant a symlink at a predictable path that a victim tenant's application will later attempt to create — causing the victim app to either fail, follow the attacker's symlink and write sensitive content to an attacker-chosen target, or read attacker-controlled content.
This write primitive is the new finding. The read side was already known and reported upstream.
—
## Reproduction Steps:
Upload the following self-contained PHP probe to any site on node `http22` and fetch it over HTTP. Uses only the PHP standard library — no extensions or special configuration required.
### Probe Code (PHP):
```php
<?php
$base = "/tmp/wafverify_" . getmypid();
1. create a new file anywhere in /tmp
@file_put_contents($base . ".txt", "cross-tenant-write-proof-" . date("c"));
2. create a symlink
@symlink($base . ".txt", $base . ".lnk");
3. write THROUGH the symlink (writes to the target file)
@file_put_contents($base . ".lnk", "overwritten-via-symlink");
4. show directory modes
printf("perms: /tmp=%o /var/tmp=%o /dev/shm=%o\n",
fileperms("/tmp"), fileperms("/var/tmp"), fileperms("/dev/shm"));
?>
```
### Exact Output (2026-08-18, tenant PHP on http22):
```
create /tmp file: OK size=50
create symlink: OK target=/tmp/wafverify_979432.txt
write-through-symlink: OK bytes=23 real=overwritten-via-symlink
perms: /tmp=1777 /var/tmp=1777 /dev/shm=1777
```
—
## Additional Confirmed Observations:
Sticky-bit protection intact. Attempting to unlink, rename, or write to another tenant's existing `/tmp` files is correctly blocked — owner-only enforcement verified. The primitive is therefore pre-planting at not-yet-existing predictable names, not modifying existing victim files.
Read side (already known). Other tenants' files in `/tmp` are world-readable. During discovery, co-tenant files were observed that contained credentials and session data. Contents were not saved, not used, and have been fully redacted from all evidence. This is noted only to confirm the shared scratch exposure is bidirectional — read and write — not merely one-sided. The read class is already tracked upstream ( FS#363 /389/393/417/418).
PHP sessions not affected. `session.save_path` is already per-tenant (`/home/<acct>/admin/tmp`), so session files cannot be hijacked via this vector.
MySQL `FILE` privilege — negative. No `FILE` privilege granted; `secure_file_priv=/tmp/`; `LOAD_FILE()` returns empty. Cross-tenant database file read via this path is not possible.
`/proc` — negative. `hidepid` is set; only the tenant's own processes are visible.
—
## Impact:
A malicious tenant on the same node can pre-plant files or symlinks at predictable paths in the shared scratch space before a victim tenant's application creates them. If a victim application writes sensitive content (credentials, tokens, session data, temporary uploads) to a predictable `/tmp` path, the attacker can redirect that write to an arbitrary target via a pre-planted symlink — or poison the path with attacker-controlled content before the victim reads it.
Classic targets for this class of attack: cron jobs, backup scripts, cache writers, upload handlers, and any application component that creates temporary files at predictable names on a node shared with untrusted tenants.
Combined with the already-known world-readable state of `/tmp`, a malicious co-tenant can both read shared scratch state and actively influence it — making the exposure bidirectional and significantly more serious than the read-only class previously acknowledged.
—
## Ethical Disclosure:
All testing was performed exclusively against files created under our own test account and our own naming prefix (`wafverify_*`). No other tenant's existing file was written to, modified, unlinked, or renamed — sticky-bit protection was verified intact throughout. Co-tenant files encountered in `/tmp` during discovery were read only to confirm the shared nature of the directory; their contents (credentials, cookies, application data) were immediately discarded, not stored, not used in any way, and fully redacted from all evidence files submitted with this report.
—
## Recommendations:
- Mount `/tmp`, `/var/tmp`, and `/dev/shm` as per-tenant private tmpfs volumes, consistent with the per-tenant isolation already applied to `/home` and PHP session paths.
- Alternatively, set `TMPDIR`, `TMP`, `TEMP`, and `upload_tmp_dir` per-tenant to a path within the tenant's own `/home` tree, preventing any cross-tenant path collision.
- For any platform service that must share a scratch directory, enforce `O_TMPFILE` / `mkstemp` with `fchmod 0600` at creation time and never follow pre-existing symlinks on temp paths.
—
## Relationship to Other Findings:
This finding chains with FINDING-5 (cross-tenant loopback service exposure on the same node `http22`). Together they confirm that the tenant isolation boundary on shared web nodes has multiple independent gaps — network-level (FINDING-5) and filesystem-level (this report) — compounding the overall risk to co-tenants on the same node.
—
## Evidence Files:
| File | Contents |
| — | — |
| `verify_all_output.txt` | Live re-verification output (write, symlink creation, write-through, directory permissions) |
| `CHAIN.md FINDING-3` | Detailed write-up including all negative results |
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
verify_all_output.txt
Hello,
We don't consider it a vulnerability as it's standard POSIX behaviour. TMPDIR is set to a private directory, so if an application ignores it and uses the world-accessible /tmp instead, it's on it.
Kind regards,
Cyril