Security vulnerabilities

  • Status Closed
  • Assigned To
    cbay
  • Private
Attached to Project: Security vulnerabilities
Opened by awais0x01 - 21.07.2026
Last edited by cbay - 22.07.2026

FS#418 - Cross-Tenant Data Exposure via Shared /tmp Directory

## Severity
High (CVSS 3.1: 7.7 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)

## Weakness
CWE-668: Exposure of Resource to Wrong Sphere
CWE-732: Incorrect Permission Assignment for Critical Resource

## Summary

The shared hosting nodes use a single shared `/tmp` directory across all tenant accounts without polyinstantiation. Files created by any tenant with world-readable permissions (either explicitly or by application default) are accessible to every other tenant on the same physical node. This allows a low-privileged attacker with a free hosting account to read other tenants' temporary files, which routinely contain credentials, API tokens, database paths, configuration data, and application state.

This finding reproduces the issue reported in  FS#363 , which was partially mitigated by changing the default umask to 0007. However, the fix is incomplete: applications and scripts that set explicit permissions (e.g., `chmod 644`, Python `open()` without restricted mode, `tar -xvf` preserving archive permissions) still create world-readable files in the shared namespace.

## Steps to Reproduce

Environment: Free hosting account on alwaysdata.com (public cloud, Paris datacenter). SSH access at `ssh-ACCOUNTNAME.alwaysdata.net`.

1. Create a free hosting account at `https://admin.alwaysdata.com/admin/account/add/` (select "Free" plan, Paris 1 datacenter).

2. Connect via SSH to the hosting account:

```
ssh ACCOUNTNAME@ssh-ACCOUNTNAME.alwaysdata.net ```

3. List files in the shared /tmp directory:

```
ls -la /tmp/ | head -30
```

Output shows 663+ files owned by multiple different tenant accounts (different UIDs/usernames), confirming `/tmp` is shared across all accounts on this node.

4. Find world-readable files owned by other tenants:

```
find /tmp -maxdepth 1 -not -user ACCOUNTNAME -readable -type f 2>/dev/null | wc -l
```

Returns 22+ files readable by the attacker account.

5. Read a cross-tenant file containing credentials:

```
cat /tmp/check_webhook.php
```

This file, owned by another tenant, contains a Telegram Bot API token in plaintext (the token has been redacted in this report but was confirmed live). The file also reveals the tenant's username and application structure.

6. Read another cross-tenant file revealing database paths:

```
head -10 /tmp/test_callback.php
```

This file reveals the full filesystem path to another tenant's SQLite database (`/home/TENANT/www/…/rshq.db`), including their application directory structure.

## Impact

An attacker with a free hosting account (no payment required via API registration) can:

1. Read other tenants' credentials from temporary files: API tokens, database credentials, session tokens, and authentication secrets that applications write to `/tmp`
2. Map other tenants' filesystem layout: leaked paths reveal application structures, database locations, and deployment details (e.g., `/home/TENANT/www/…`)
3. Enumerate tenant accounts: the file ownership in `/tmp` reveals other tenants' account names, which can be used for targeted attacks
4. Access application state: log files, debugging output, and cached data from other tenants

The boundary crossed is the fundamental tenant isolation guarantee of a shared hosting platform. A free-tier attacker can read data belonging to paying customers on the same physical node.

Confirmed data exposed from other tenants during testing:

1. Live JWT session token (tenant `0xf12c`): A file `kg_tmail_sessions.json` contains a HS512-signed JWT issued on 2026-07-11, granting `ROLE_USER` access to an email service (web-library.net) with account ID `6a54692908c866daeb017a6e`. The token includes a Mercure real-time subscription channel. This is a live, replayable authentication credential.

2. Dolibarr ERP installation log (tenant `demo7`): A 1.5 MB log file `dolibarr_install.log` contains the full installation trace including server IP (102.117.59.202), database configuration steps, and filesystem paths.

3. SQLite database (tenant `pablomon`): A complete database file `sistema_envios.db` (12 KB) containing application data.

4. PHP/Python source code from multiple tenants with embedded API calls and application logic.

5. Build logs, session files, debug output from tenants including `jolafstore`, `gdpshost1`, `cittapet`, `data-test`, `apexcodex`, and others.

Scale: 334 total files in shared `/tmp`, with 32 readable by any other tenant on the same node. Files belong to 15+ distinct tenant accounts.

## Root Cause

The `/tmp` directory on shared hosting nodes is not polyinstantiated (each tenant does not get their own isolated `/tmp`). While the default umask was changed to 0007 after  FS#363 , this only prevents NEW files from being world-readable by default. It does not protect against:

- Applications that explicitly set file permissions (e.g., `chmod 644`)
- Archive extraction that preserves original permissions
- Programming languages/frameworks whose default file creation mode is world-readable
- Files created before the umask fix
- Any process that resets or overrides the umask

## Remediation

1. Polyinstantiate /tmp: Configure PAM (`pam_namespace.so`) to give each tenant their own isolated `/tmp` directory. This is the standard solution on shared hosting platforms.
2. Alternative: Use `PrivateTmp=yes` in systemd service units for each tenant's processes.
3. Additional defense: Periodic cleanup of `/tmp` to remove files with overly permissive modes.

Closed by  cbay
22.07.2026 07:10
Reason for closing:  Duplicate
Additional comments about closing:  

https://security.alwaysda ta.com/task/389

Loading...

Available keyboard shortcuts

Tasklist

Task Details

Task Editing