- Status Closed
-
Assigned To
cbay - Private
Opened by hardikdevaliya7 - 26.09.2026
Last edited by cbay - 26.09.2026
FS#508 - Shared world-readable /tmp across tenants on the shared SSH gateway (ssh-[account].alwaysdata.net)
Summary
alwaysdata's shared SSH gateway (ssh2, which serves every ssh-[account].alwaysdata.net alias) and shared web server (http22, which runs every customer's PHP site) each expose a single world-readable /tmp directory to all tenants, with no per-account namespace. /tmp is drwxrwxrwt (1777, sticky): the sticky bit prevents a tenant from deleting another tenant's files, but it does not prevent reading them. Any file a customer writes to /tmp with the default 0644 permissions is readable by every other customer on that host.
Confirmed with two self-owned, unrelated alwaysdata accounts (two separate customer identities, different UIDs, different account names): a canary written by account A is readable by account B, a different customer.
Affected hosts (both in scope)
- ssh-bbtestacctalfa.alwaysdata.net / ssh-bbtestacctbravo.alwaysdata.net — both resolve to the same shared ssh2 box; /tmp is shared across all customers on it. (ssh://ssh-[account].alwaysdata.net is explicitly listed in scope.)
- The web server http22 (reached via any customer's *.alwaysdata.net site) has a separate but equally shared /tmp.
Reproduction — SSH (primary, on the explicitly-listed ssh-[account] host)
Two self-owned accounts:
- Account A: bbtestacctalfa (UID 550359), SSH host ssh-bbtestacctalfa.alwaysdata.net
- Account B: bbtestacctbravo (UID 550366, a separate customer), SSH host ssh-bbtestacctbravo.alwaysdata.net
Step 1 — A writes a marker to /tmp on its own SSH host:
$ ssh bbtestacctalfa@ssh-bbtestacctalfa.alwaysdata.net \
"echo -n 'adpoc-tenantA-276c154e73ef' > /tmp/adpoc_a_marker.txt; chmod 644 /tmp/adpoc_a_marker.txt; ls -l /tmp/adpoc_a_marker.txt; id; hostname"
(bbtestacctalfa@ssh-bbtestacctalfa.alwaysdata.net) Password:
-rw-r–r– 1 bbtestacctalfa bbtestacctalfa 26 Sep 26 15:14 /tmp/adpoc_a_marker.txt
uid=550359(bbtestacctalfa) gid=504127(bbtestacctalfa) groups=504127(bbtestacctalfa)
ssh2
Step 2 — B (different customer, different SSH host) reads A's marker from /tmp:
$ ssh bbtestacctbravo@ssh-bbtestacctbravo.alwaysdata.net \
"cat /tmp/adpoc_a_marker.txt; id; hostname; ls -l /tmp/adpoc_a_marker.txt"
(bbtestacctbravo@ssh-bbtestacctbravo.alwaysdata.net) Password:
adpoc-tenantA-276c154e73ef
uid=550366(bbtestacctbravo) gid=504133(bbtestacctbravo) groups=504133(bbtestacctbravo)
ssh2
-rw-r–r– 1 bbtestacctalfa bbtestacctalfa 26 Sep 26 15:14 /tmp/adpoc_a_marker.txt
B (UID 550366) successfully read a file owned by bbtestacctalfa (UID 550359) from /tmp. Both ssh-[account] hosts resolve to the same ssh2 box; /tmp is not namespaced per customer.
Step 3 — negative control (B cannot tamper with A's file):
$ ssh bbtestacctbravo@ssh-bbtestacctbravo.alwaysdata.net \
"rm /tmp/adpoc_a_marker.txt 2>&1; echo 'TAMPER' > /tmp/adpoc_a_marker.txt 2>&1; cat /tmp/adpoc_a_marker.txt"
rm: cannot remove '/tmp/adpoc_a_marker.txt': Operation not permitted
bash: /tmp/adpoc_a_marker.txt: Permission denied
adpoc-tenantA-276c154e73ef
The sticky bit confines this to a read-only cross-tenant disclosure (no delete, no overwrite).
Reproduction — web server (secondary)
The same issue exists on the shared web server http22. Two PHP files were placed on the two accounts' sites (via SSH): https://bbtestacctalfa.alwaysdata.net/poc_writer.php (account A) writes a marker to /tmp on http22; https://bbtestacctbravo.alwaysdata.net/poc_reader.php (account B, separate customer) reads /tmp on http22 and prints the result.
Request 1 (as A, writes the marker):
GET /poc_writer.php HTTP/1.1
Host: bbtestacctalfa.alwaysdata.net
Response:
marker='adpoc-tenantA-web-276c154e73ef' written by bbtestacctalfa uid=550359 on http22
Request 2 (as B, separate customer, reads A's marker from /tmp):
GET /poc_reader.php HTTP/1.1
Host: bbtestacctbravo.alwaysdata.net
Response:
reader=bbtestacctbravo uid=550366 host=http22
read_from_tmp=FOUND: adpoc-tenantA-web-276c154e73ef
A file written to /tmp by account A's PHP (UID 550359) is read back by account B's PHP (UID 550366, different customer) on the same shared web server.
Exposure extent
ls -la /tmp on ssh2 shows approximately 308 customer-owned files, of which roughly 187 are world-readable (0644), owned by many distinct alwaysdata customer accounts (for example slientstress, bkrmshtaq, edusync, axora-core, ksiiprn, madprojet, kodmai, proxy-iptv, passionbooking360, takervps, cacar, callmoon142). Observed file types include Python scripts (.py, up to 199 KB), SQL schema dumps (.sql), text files, and images.
I did not read the contents of any file owned by another customer (only listed filenames and owners as evidence of exposure), in line with the program's "use test accounts only / do not touch other customers' data" rule. The canary used for confirmation was written and read between my own two accounts.
Impact
- A customer can read any other customer's world-readable files in /tmp on the shared SSH gateway and web server — application source, scratch files, SQL dumps, and any secret a customer application writes directly to /tmp.
- Read-only (the sticky bit prevents deletion/overwrite of others' files).
- Bounded by a mitigation that should be noted: alwaysdata sets TMPDIR to a per-account directory (/home/<account>/admin/tmp) for SSH sessions, and PHP session.save_path, upload_tmp_dir, and sys_temp_dir are likewise per-account. So PHP session files and upload-temp files are not exposed (verified: a PHP session on one site wrote to /home/<account>/admin/tmp, not /tmp; glob('/tmp/sess_*') returned nothing). Customers who rely on $TMPDIR or default PHP temp handling are not affected. The exposure is limited to files written directly to /tmp (a hardcoded /tmp path, or a tool that ignores $TMPDIR) at default 0644.
- No alwaysdata-platform-owned secret was found in /tmp: all root/system-owned files in /tmp are 0700/0640 (not world-readable). The readable files are exclusively customer-account-owned.
Root cause / fix
The shared multi-tenant SSH gateway and web server expose a single /tmp to all tenants with no per-account namespace. Recommended fixes (platform-side):
- Per-account /tmp namespace on shared hosts (e.g. PrivateTmp=yes for systemd-managed customer processes, or a mount-namespaced per-tenant /tmp), or
- A per-account open_basedir that excludes the shared /tmp and redirects temp writes to the already-per-account admin/tmp, or
- The per-account TMPDIR/PHP-temp isolation already in place shows the intent to isolate temp files per customer; extending that default so that direct /tmp writes are also namespaced (or making the shared /tmp 0770 + per-account group) would close the read channel.
Scope note
The exposure is customer application data. The scope excludes "sites/third-party apps hosted by customers unless the flaw exposes customer data and stems from alwaysdata's own platform." The shared, un-namespaced /tmp is alwaysdata's platform configuration on a shared multi-tenant host, and it does expose customer data — so the exception applies. It is reachable via the explicitly-listed ssh://ssh-[account].alwaysdata.net host, which makes it unambiguous that this is a platform-level isolation gap rather than a per-customer app issue.
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
vuln 1.png