<?xml version="1.0" ?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">alwaysdata </title>
  <subtitle type="text">
    alwaysdata Security vulnerabilities: Recently closed tasks
  </subtitle>
  <id>https://security.alwaysdata.com/</id>
    <updated>2026-05-11T07:25:16Z</updated>
  <link rel="self" type="text/xml" href="feed.php?feed_type=atom"/>
  <link rel="alternate" type="text/html" hreflang="en" href="/feed.php"/>
    <entry>
    <title>FS#337: [ALW-001] Flyspray .git Directory Fully Exposed on security.alwaysdata.com</title>
    <link href="https://security.alwaysdata.com/task/337" />    
    <updated>2026-05-11T07:25:16Z</updated>    
    <published>2026-05-09T22:15:35Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> HIGH
</p>

<p>
<strong>Target:</strong> security.alwaysdata.com<br /><strong>Affected <acronym title="Uniform Resource Locator">URL</acronym>:</strong> <a href="https://security.alwaysdata.com/.git/" class="urlextern" title="https://security.alwaysdata.com/.git/"  rel="nofollow">https://security.alwaysdata.com/.git/</a>
</p>

<p>
## Description
</p>

<p>
The deployed Flyspray instance (this very bug-tracker) exposes its entire `.git` directory under the document root. The directory listing is disabled, but the well-known internal files are individually readable, which is enough to reconstruct the full source tree, every commit message, every author email, and every credential ever committed to the repository. (Title shortened from &quot;…Source Tree, Admin Email, Commit History&quot; — the original 117-char summary exceeded the 100-char column limit and triggered an INSERT error.)
</p>

<p>
## Steps to Reproduce (manual, no scanner)
</p>

<p>
```<br />curl -s <a href="https://security.alwaysdata.com/.git/HEAD" class="urlextern" title="https://security.alwaysdata.com/.git/HEAD"  rel="nofollow">https://security.alwaysdata.com/.git/HEAD</a> curl -s <a href="https://security.alwaysdata.com/.git/config" class="urlextern" title="https://security.alwaysdata.com/.git/config"  rel="nofollow">https://security.alwaysdata.com/.git/config</a> curl -s <a href="https://security.alwaysdata.com/.git/logs/HEAD" class="urlextern" title="https://security.alwaysdata.com/.git/logs/HEAD"  rel="nofollow">https://security.alwaysdata.com/.git/logs/HEAD</a> curl -s <a href="https://security.alwaysdata.com/.git/index" class="urlextern" title="https://security.alwaysdata.com/.git/index"  rel="nofollow">https://security.alwaysdata.com/.git/index</a> -o /tmp/index ; file /tmp/index<br />```
</p>

<p>
`HEAD` returns the current branch ref, `config` returns the repository configuration, and `logs/HEAD` returns the full reflog including author names and emails. From there, `git clone` against the exposed directory or a manual `git-cat-file` walk reconstructs ~941 reachable objects and the complete deployed source code (including any locally-applied patches).
</p>

<p>
## Impact
</p>

<p>
* Full source-code disclosure of the production Flyspray that hosts the bug-bounty program itself.<br />* Disclosure of admin / committer email addresses and real names from commit metadata.<br />* Any secret accidentally committed (DB credentials, <acronym title="Application Programming Interface">API</acronym> tokens, signing keys) is recoverable from history even if removed from `HEAD`.<br />* Combined with ALW-007 (no rate-limiting on the Flyspray login) and ALW-015 (weak integer CSRF), this gives an attacker a precise roadmap to compromise the bug-bounty intake and read every other researcher&#039;s unredacted submissions.
</p>

<p>
## Remediation
</p>

<p>
Block the directory at the web server level, e.g. for Apache:
</p>

<p>
```<br />&lt;DirectoryMatch &quot;/\.git&quot;&gt;
</p>
<pre class="code">  Require all denied</pre>

<p>
&lt;/DirectoryMatch&gt;<br />```
</p>

<p>
or for nginx:
</p>

<p>
```<br />location ~ /\.git { deny all; return 404; }<br />```
</p>

<p>
Then remove the `.git` directory from the document root entirely and deploy via a build artifact or `git archive` rather than a working copy.
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com)<br />Tested manually per program rules — no automated scanners used.<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:337</id>
  </entry>
    <entry>
    <title>FS#336: [ALW-015] Flyspray CSRF Token is a Plain Integer with Low Entropy</title>
    <link href="https://security.alwaysdata.com/task/336" />    
    <updated>2026-05-11T07:24:17Z</updated>    
    <published>2026-05-09T22:10:29Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> LOW
</p>

<p>
<strong>Target:</strong> security.alwaysdata.com<br /><strong>Affected element:</strong> `&lt;input type=&quot;hidden&quot; name=&quot;csrftoken&quot; value=&quot;…&quot;&gt;` on every form (registration, task creation, comments, edits)
</p>

<p>
## Description
</p>

<p>
Flyspray emits its CSRF token as a plain decimal integer of around 9–10 digits, giving roughly `log2(10^10) ≈ 33` bits of entropy. That is dramatically weaker than the 256-bit cryptographic token Django emits on `admin.alwaysdata.com` for the same protection class, and it is potentially predictable depending on how the token is seeded.
</p>

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

<p>
```<br />curl -s <a href="https://security.alwaysdata.com/register" class="urlextern" title="https://security.alwaysdata.com/register"  rel="nofollow">https://security.alwaysdata.com/register</a> \
</p>
<pre class="code">| grep -oE &#039;name=&quot;csrftoken&quot; value=&quot;[0-9]+&quot;&#039;</pre>

<p>
# → name=&quot;csrftoken&quot; value=&quot;852018639&quot;
</p>

<p>
# Compare with Django on admin.alwaysdata.com (any form):<br />#   csrfmiddlewaretoken=qzNI2DZ0UfLVc7VRvdUw… #   (64 random characters, cryptographic)<br />```
</p>

<p>
## Impact
</p>

<p>
* Brute-forcing a 10-digit integer over the network is well within reach of a determined attacker (10¹⁰ ≈ a few weeks at conservative request rates with no rate limiting — see ALW-007).<br />* If the integer is derived from a predictable seed (<acronym title="Hypertext Preprocessor">PHP</acronym> `mt_rand` without proper seeding, timestamp, etc.) the search space collapses further.<br />* Combined with ALW-009 (no `SameSite` on the session cookie) the CSRF defense layer becomes paper-thin: a malicious page can both replay the cookie and guess the token in feasible time.
</p>

<p>
## Remediation
</p>

<p>
* Replace the token generator with a cryptographically random string. In <acronym title="Hypertext Preprocessor">PHP</acronym>: `bin2hex(random_bytes(32))` (256 bits, 64 hex characters).<br />* Use a per-session token, rotated on login and on privilege change, not a long-lived global one.<br />* Validate the token in constant time (`hash_equals`) to avoid timing leaks.<br />* For Flyspray 1.0-rc11 specifically, patch `make_csrf_token()` in `includes/class.flyspray.php` to call `bin2hex(random_bytes(32))`.
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com) — manual testing only.<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:336</id>
  </entry>
    <entry>
    <title>FS#335: [ALW-011] Flyspray Attachments Downloadable via Sequential ID without Authentication</title>
    <link href="https://security.alwaysdata.com/task/335" />    
    <updated>2026-05-11T07:24:03Z</updated>    
    <published>2026-05-09T22:10:26Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> MEDIUM
</p>

<p>
<strong>Target:</strong> security.alwaysdata.com<br /><strong>Affected endpoint:</strong> `GET <a href="https://security.alwaysdata.com/index.php?getfile=" class="urlextern" title="https://security.alwaysdata.com/index.php?getfile="  rel="nofollow">https://security.alwaysdata.com/index.php?getfile=</a>{id}`
</p>

<p>
## Description
</p>

<p>
Flyspray attachments — i.e. proof-of-concept files researchers attach to security reports — are downloadable by anyone with a sequential integer ID and no authentication. The four currently-live attachments (IDs 1–4) returned 200 OK with the underlying PoC PDFs and PNGs without any session cookie. IDs 5–11 returned 410 Gone (deleted), which also leaks prior-existence.
</p>

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

<p>
```<br />for i in 1 2 3 4 5 6 7 8 9 10 11; do
</p>
<pre class="code">printf &#039;id=%-2s  &#039; &quot;$i&quot;
curl -sI &quot;https://security.alwaysdata.com/index.php?getfile=$i&quot; \
  | grep -E &#039;^(HTTP|content-type|content-length):&#039; \
  | tr &#039;\n&#039; &#039; &#039;
echo</pre>

<p>
done<br /># id=1   <acronym title="Hyper Text Transfer Protocol">HTTP</acronym>/1.1 200 OK   content-type: application/pdf   content-length: 115873<br /># id=2   <acronym title="Hyper Text Transfer Protocol">HTTP</acronym>/1.1 200 OK   content-type: application/pdf   content-length: 164813<br /># id=3   <acronym title="Hyper Text Transfer Protocol">HTTP</acronym>/1.1 200 OK   content-type: image/png         content-length:  39503<br /># id=4   <acronym title="Hyper Text Transfer Protocol">HTTP</acronym>/1.1 200 OK   content-type: image/png         content-length: 141632<br /># id=5..11 <acronym title="Hyper Text Transfer Protocol">HTTP</acronym>/1.1 410 Gone<br />```
</p>

<p>
No session cookie was sent. The response body contains the original PoC file in full.
</p>

<p>
## Impact
</p>

<p>
* Any unredacted PoC, screenshot, or credential a previous researcher attached is publicly readable just by walking the integer counter.<br />* Even when the parent task is later restricted or redacted in the UI, the raw attachment stays exposed at the same `?getfile=ID` <acronym title="Uniform Resource Locator">URL</acronym>.<br />* The 410-vs-200 differential also leaks the existence of every deleted attachment, which can be used to bound the total volume of historical PoCs.
</p>

<p>
## Remediation
</p>

<p>
* Require an authenticated session <strong>and</strong> a project-membership / task-visibility check before serving `getfile`.<br />* Replace the integer ID with a non-guessable token (UUIDv4 or HMAC-signed hash bound to the user + task).<br />* Return <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> 404 (not 410) for deleted attachments to avoid confirming prior existence.<br />* Audit the four currently-public attachments (IDs 1–4) and re-issue them under restricted URLs if they contain unredacted PoC material.
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com) — manual testing only, downloads not redistributed.<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:335</id>
  </entry>
    <entry>
    <title>FS#334: [ALW-010] Flyspray CSP Allows unsafe-inline and unsafe-eval in script-src</title>
    <link href="https://security.alwaysdata.com/task/334" />    
    <updated>2026-05-11T07:23:49Z</updated>    
    <published>2026-05-09T22:10:23Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> MEDIUM
</p>

<p>
<strong>Target:</strong> security.alwaysdata.com<br /><strong>Affected response header:</strong> `Content-Security-Policy`
</p>

<p>
## Description
</p>

<p>
The Content-Security-Policy returned by every Flyspray response on `security.alwaysdata.com` includes both `&#039;unsafe-inline&#039;` and `&#039;unsafe-eval&#039;` in `script-src`. With both directives in place, CSP provides effectively zero mitigation against XSS — any future injection sink (task title, comment, custom field) executes immediately.
</p>

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

<p>
```<br />curl -sI <a href="https://security.alwaysdata.com/" class="urlextern" title="https://security.alwaysdata.com/"  rel="nofollow">https://security.alwaysdata.com/</a> | grep -i content-security-policy<br /># → content-security-policy: default-src &#039;none&#039;; img-src &#039;self&#039;; font-src &#039;self&#039;;<br />#      style-src &#039;self&#039; &#039;unsafe-inline&#039;;<br />#      script-src &#039;self&#039; &#039;unsafe-inline&#039; &#039;unsafe-eval&#039;;   ← weak<br />#      connect-src &#039;self&#039;<br />```
</p>

<p>
## Impact
</p>

<p>
* Any future XSS in Flyspray (task description, comment body, attachment filename, custom field) executes despite CSP.<br />* `&#039;unsafe-eval&#039;` allows `eval()`, `new Function(string)`, `setTimeout(string, …)`, and `setInterval(string, …)` payloads — enabling attacker-controlled string execution.<br />* Especially impactful given the bundled libraries (Prototype.js 1.7, script.aculo.us 1.9.0) which are old enough to have known XSS sinks.
</p>

<p>
## Remediation
</p>

<p>
* Remove `&#039;unsafe-inline&#039;` from `script-src` and replace with a per-response nonce (`script-src &#039;self&#039; &#039;nonce-XYZ&#039;`); add the same nonce to every server-rendered `&lt;script&gt;` tag.<br />* Remove `&#039;unsafe-eval&#039;` after refactoring any `eval(string)` / `new Function(string)` / `setTimeout(string, …)` / `setInterval(string, …)` call sites in the bundled <acronym title="JavaScript">JS</acronym> (Prototype/scriptaculous).<br />* Ideally also tighten `style-src` away from `&#039;unsafe-inline&#039;` once template inline styles are migrated to a stylesheet.
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com) — manual testing only.<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:334</id>
  </entry>
    <entry>
    <title>FS#333: [ALW-009] Flyspray Session Cookie Missing Secure and SameSite Flags</title>
    <link href="https://security.alwaysdata.com/task/333" />    
    <updated>2026-05-11T07:22:30Z</updated>    
    <published>2026-05-09T22:10:17Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> MEDIUM
</p>

<p>
<strong>Target:</strong> security.alwaysdata.com<br /><strong>Affected response header:</strong> `Set-Cookie` on every authenticated response
</p>

<p>
## Description
</p>

<p>
The Flyspray session cookie is set with `HttpOnly` only — both the `Secure` flag and the `SameSite` attribute are missing. This is a measurable regression compared to alwaysdata&#039;s Django stack on `admin.alwaysdata.com`, which correctly sets `Secure; SameSite=Lax` on its session cookie.
</p>

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

<p>
```<br />curl -sI <a href="https://security.alwaysdata.com/" class="urlextern" title="https://security.alwaysdata.com/"  rel="nofollow">https://security.alwaysdata.com/</a> | grep -i set-cookie<br /># → Set-Cookie: flyspray=&lt;sessionid&gt;; path=/; HttpOnly<br />#       (no Secure, no SameSite)
</p>

<p>
# Compare admin.alwaysdata.com (correct):<br />curl -sI <a href="https://admin.alwaysdata.com/" class="urlextern" title="https://admin.alwaysdata.com/"  rel="nofollow">https://admin.alwaysdata.com/</a> | grep -i set-cookie<br /># → Set-Cookie: csrftoken=…; Path=/; SameSite=Lax; Secure<br />```
</p>

<p>
## Impact
</p>

<p>
* The session cookie will be transmitted in plaintext if the user is ever forced onto an <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> origin (downgrade / hostile coffee-shop network / user typing the domain without https).<br />* Without `SameSite`, third-party sites can include this domain via top-level navigation or cross-site POST and the cookie is attached, removing CSRF defense-in-depth.<br />* On a subdomain that hosts user-supplied attachments (see ALW-011), the missing `SameSite` is meaningful.
</p>

<p>
## Remediation
</p>

<p>
In Flyspray&#039;s session configuration set both flags:
</p>

<p>
```<br /># php.ini or .htaccess<br />session.cookie_secure = 1<br />session.cookie_samesite = &quot;Lax&quot;<br />session.cookie_httponly = 1<br />```
</p>

<p>
Final header should look like:
</p>

<p>
```<br />Set-Cookie: flyspray=&lt;sessionid&gt;; path=/; HttpOnly; Secure; SameSite=Lax<br />```
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com) — manual testing only.<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:333</id>
  </entry>
    <entry>
    <title>FS#332: [ALW-007] Flyspray Login Endpoint Has No Rate Limiting / Account Lockout</title>
    <link href="https://security.alwaysdata.com/task/332" />    
    <updated>2026-05-11T07:21:51Z</updated>    
    <published>2026-05-09T22:10:14Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> MEDIUM
</p>

<p>
<strong>Target:</strong> security.alwaysdata.com<br /><strong>Affected endpoint:</strong> `POST <a href="https://security.alwaysdata.com/index.php?do=authenticate" class="urlextern" title="https://security.alwaysdata.com/index.php?do=authenticate"  rel="nofollow">https://security.alwaysdata.com/index.php?do=authenticate</a>`
</p>

<p>
## Description
</p>

<p>
The Flyspray login endpoint at `security.alwaysdata.com` does not implement any form of brute-force protection: no progressive delay, no per-IP throttling, no per-account lockout, and no CAPTCHA after repeated failures. Because this is the same Flyspray instance that hosts every researcher&#039;s confidential bug-bounty submissions, brute-forcing an analyst account is a direct path to compromising the entire program intake.
</p>

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

<p>
```<br /># Five consecutive bad-password attempts against a known-existing username<br />for i in 1 2 3 4 5; do
</p>
<pre class="code">curl -s -o /dev/null -w &#039;%{http_code}\n&#039; \
  -X POST &#039;https://security.alwaysdata.com/index.php?do=authenticate&#039; \
  -d &#039;user_name=admin&amp;password=wrong&#039;</pre>

<p>
done<br /># → 303<br /># → 303<br /># → 303<br /># → 303<br /># → 303<br />```
</p>

<p>
No lockout, no CAPTCHA, no slowdown. (Stopped at 5 to comply with the program&#039;s &quot;do not damage production&quot; rule.) Combined with ALW-006 / <del>&#160;<a href="https://security.alwaysdata.com/task/329?feed_type=atom&amp;topic=clo" title="Invalid | Task made private | 100%"  class = "closedtasklink">FS#329</a>&#160;</del> (already-known username enumeration via `searchnames.php`), an attacker has a complete brute-force pipeline.
</p>

<p>
## Impact
</p>

<p>
* Brute-force attacks against analyst, admin, and researcher accounts are feasible from a single source.<br />* Compromise of an analyst account would expose <strong>every</strong> researcher&#039;s unredacted submission and PoC attachments — the most damaging possible outcome on this asset.<br />* Increases the impact of ALW-009 (insecure cookie) and ALW-015 (weak CSRF token) by enabling pre-conditions for full account takeover.
</p>

<p>
## Remediation
</p>

<p>
* Add progressive delay after 3 failed attempts (e.g. exponential backoff up to 60 s).<br />* Show a CAPTCHA after 5 failed attempts (per IP <strong>and</strong> per username).<br />* Lock the target account (or send an admin alert) after 10 failed attempts in a short window.<br />* Deploy fail2ban with a jail tailing the Flyspray auth log to ban IPs at the firewall after sustained abuse.<br />* Consider upgrading from Flyspray 1.0-rc11 to a version with built-in rate-limiting hooks.
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com) — manual testing only, capped at 5 attempts.<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:332</id>
  </entry>
    <entry>
    <title>FS#331: [ALW-005] Password-Reset Differential Response Enables User Enumeration on admin.alwaysdata.com</title>
    <link href="https://security.alwaysdata.com/task/331" />    
    <updated>2026-05-11T07:19:05Z</updated>    
    <published>2026-05-09T22:10:11Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> MEDIUM
</p>

<p>
<strong>Target:</strong> admin.alwaysdata.com<br /><strong>Affected endpoint:</strong> `POST <a href="https://admin.alwaysdata.com/password/lost/" class="urlextern" title="https://admin.alwaysdata.com/password/lost/"  rel="nofollow">https://admin.alwaysdata.com/password/lost/</a>`
</p>

<p>
## Description
</p>

<p>
The password-reset endpoint returns a different <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> status (and different body) depending on whether the submitted email belongs to a real account, allowing unauthenticated enumeration of valid alwaysdata user emails.
</p>

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

<p>
```<br /># Existing account — redirected to the success page<br />curl -i -X POST <a href="https://admin.alwaysdata.com/password/lost/" class="urlextern" title="https://admin.alwaysdata.com/password/lost/"  rel="nofollow">https://admin.alwaysdata.com/password/lost/</a> \
</p>
<ol>
<li class="level1"><div class="li">H &#039;Content-Type: application/x-www-form-urlencoded&#039; \</div>
</li>
<li class="level1"><div class="li">-data &#039;email=cbay@alwaysdata.com&#039;</div>
</li>
</ol>

<p>
# → <acronym title="Hyper Text Transfer Protocol">HTTP</acronym>/1.1 302 Found<br /># → Location: /password/sent/
</p>

<p>
# Non-existent account — form re-rendered with no redirect<br />curl -i -X POST <a href="https://admin.alwaysdata.com/password/lost/" class="urlextern" title="https://admin.alwaysdata.com/password/lost/"  rel="nofollow">https://admin.alwaysdata.com/password/lost/</a> \
</p>
<ol>
<li class="level1"><div class="li">H &#039;Content-Type: application/x-www-form-urlencoded&#039; \</div>
</li>
<li class="level1"><div class="li">-data &#039;email=nonexistent9999@example.com&#039;</div>
</li>
</ol>

<p>
# → <acronym title="Hyper Text Transfer Protocol">HTTP</acronym>/1.1 200 OK<br /># → (form <acronym title="HyperText Markup Language">HTML</acronym> re-rendered, no redirect)<br />```
</p>

<p>
The 302→/password/sent/ vs 200 differential is observable from a single unauthenticated request and was not rate-limited during testing.
</p>

<p>
## Impact
</p>

<p>
* Confirms whether an arbitrary email address has an alwaysdata account.<br />* Enables targeted phishing and credential-stuffing campaigns against confirmed-real customer accounts.<br />* Combined with ALW-007 (no rate-limiting on the related Flyspray login) and ALW-006 / <del>&#160;<a href="https://security.alwaysdata.com/task/329?feed_type=atom&amp;topic=clo" title="Invalid | Task made private | 100%"  class = "closedtasklink">FS#329</a>&#160;</del> (Flyspray username enumeration), feeds a chain ending in account-takeover attempts.
</p>

<p>
## Remediation
</p>

<p>
* Always return the same response (302 → `/password/sent/` with a generic &quot;if an account exists with that email, a reset link has been sent&quot; page) regardless of whether the email matched.<br />* Rate-limit the endpoint per source IP <strong>and</strong> per email (e.g. 5 requests / hour / address).<br />* Add CAPTCHA after 3 failed attempts.
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com) — manual testing only.<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:331</id>
  </entry>
    <entry>
    <title>FS#330: [ALW-003] Registration Token Still Leaks to Matomo — Incomplete Fix for FS#311</title>
    <link href="https://security.alwaysdata.com/task/330" />    
    <updated>2026-05-11T07:18:34Z</updated>    
    <published>2026-05-09T22:10:06Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
<strong>Severity:</strong> HIGH
</p>

<p>
<strong>Target:</strong> admin.alwaysdata.com (registration flow → outbound to tracker.alwaysdata.com)
</p>

<p>
## Description
</p>

<p>
The original <del>&#160;<a href="https://security.alwaysdata.com/task/311?feed_type=atom&amp;topic=clo" title="Fixed | Task made private | 100%"  class = "closedtasklink">FS#311</a>&#160;</del> fix was supposed to strip the registration token from URLs sent to Matomo. The current implementation still forwards token-derived parameters (`user_id`, `expires`) in the analytics request, so the Matomo back-end (and anyone with read-access to the dashboard) can still correlate a token-holder to their account-creation event.
</p>

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

<p>
1. Open browser devtools (Network tab) and visit <a href="https://admin.alwaysdata.com/account/create/" class="urlextern" title="https://admin.alwaysdata.com/account/create/"  rel="nofollow">https://admin.alwaysdata.com/account/create/</a> 2. Complete the registration flow up to the point where the confirmation token is shown in the <acronym title="Uniform Resource Locator">URL</acronym>.<br />3. Filter the Network panel by `tracker.alwaysdata.com`.<br />4. Inspect the outbound `/matomo.php` (or `/piwik.php`) tracking request — `url=` / `urlref=` / `action_name` still contain `user_id=` and `expires=` values bound to the registration token, even though the bare token string itself was redacted by the <del>&#160;<a href="https://security.alwaysdata.com/task/311?feed_type=atom&amp;topic=clo" title="Fixed | Task made private | 100%"  class = "closedtasklink">FS#311</a>&#160;</del> patch.
</p>

<p>
## Impact
</p>

<p>
* The mitigation for <del>&#160;<a href="https://security.alwaysdata.com/task/311?feed_type=atom&amp;topic=clo" title="Fixed | Task made private | 100%"  class = "closedtasklink">FS#311</a>&#160;</del> is incomplete — the parameters that uniquely identify the registration token are still observable to Matomo.<br />* Anyone with Matomo read-access can correlate a tracking event to a specific account-creation flow.<br />* Defeats the trust assumption that registration data does not leave alwaysdata&#039;s auth boundary.
</p>

<p>
## Remediation
</p>

<p>
Before calling `piwik.trackPageView()` on the registration page, normalise the <acronym title="Uniform Resource Locator">URL</acronym> passed to Matomo — strip the entire querystring/hash:
</p>

<p>
```js<br />piwik.setCustomUrl(window.location.origin + window.location.pathname);<br />piwik.setReferrerUrl(&#039;&#039;);<br />```
</p>

<p>
Verify with a manual reproduction that no `user_id`, `expires`, or other token-derived parameter reaches `tracker.alwaysdata.com`.
</p>

<p>
— Reported by: Ahmed Said (asame8855@gmail.com) — manual testing only.<br />Related: <del>&#160;<a href="https://security.alwaysdata.com/task/311?feed_type=atom&amp;topic=clo" title="Fixed | Task made private | 100%"  class = "closedtasklink">FS#311</a>&#160;</del> (partial fix).<br />
</p>
</div>
    </content>
    <author><name>ahmed saeed srour</name></author>
    <id>https://security.alwaysdata.com/:330</id>
  </entry>
    <entry>
    <title>FS#329: Unauthenticated Username Enumeration</title>
    <link href="https://security.alwaysdata.com/task/329" />    
    <updated>2026-05-07T23:03:53Z</updated>    
    <published>2026-05-07T21:19:48Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
1. Executive Summary<br />During a security assessment of the security.alwaysdata.com infrastructure, a medium-severity vulnerability was identified in the user registration/validation logic. An unauthenticated endpoint allows for the systematic enumeration of valid usernames. This information disclosure can be leveraged by malicious actors to conduct targeted brute-force attacks, credential stuffing, or sophisticated social engineering campaigns.
</p>

<p>
2. Vulnerability Information<br />Field	Details<br />Vulnerability Type	Information Exposure (Username Enumeration)<br />Severity	Medium<br />Status	Open<br />Affected Component	searchnames.php<br />Vector	Network / Web <acronym title="Application Programming Interface">API</acronym> 3. Technical Analysis<br />Root Cause<br />The endpoint searchnames.php is designed to provide real-time feedback during the account creation process. However, the root cause of the issue is twofold:
</p>

<p>
Lack of Authentication: The endpoint is accessible to any unauthenticated user or automated script.
</p>

<p>
Differential Responses: The server returns distinct boolean strings (true vs false|message) based on whether a username exists in the database.
</p>

<p>
Vulnerability Details<br />File Source: [<a href="https://security.alwaysdata.com/js/functions.js" class="urlextern" title="https://security.alwaysdata.com/js/functions.js"  rel="nofollow">https://security.alwaysdata.com/js/functions.js</a>](<a href="https://security.alwaysdata.com/js/functions.js" class="urlextern" title="https://security.alwaysdata.com/js/functions.js"  rel="nofollow">https://security.alwaysdata.com/js/functions.js</a>)
</p>

<p>
Endpoint: [<a href="https://security.alwaysdata.com/js/callbacks/searchnames.php" class="urlextern" title="https://security.alwaysdata.com/js/callbacks/searchnames.php"  rel="nofollow">https://security.alwaysdata.com/js/callbacks/searchnames.php</a>](<a href="https://security.alwaysdata.com/js/callbacks/searchnames.php" class="urlextern" title="https://security.alwaysdata.com/js/callbacks/searchnames.php"  rel="nofollow">https://security.alwaysdata.com/js/callbacks/searchnames.php</a>)
</p>

<p>
Parameter: name
</p>

<p>
4. Proof of Concept (PoC)<br />The following curl commands demonstrate how an attacker can distinguish between an existing and a non-existing account.
</p>

<p>
Test 1: Existing Username (Admin)<br />Bash
</p>

<p>
curl -s -X GET &quot;<a href="https://security.alwaysdata.com/js/callbacks/searchnames.php?name=admin" class="urlextern" title="https://security.alwaysdata.com/js/callbacks/searchnames.php?name=admin"  rel="nofollow">https://security.alwaysdata.com/js/callbacks/searchnames.php?name=admin</a>&quot; \<br />-H &quot;Content-Type: application/json&quot;<br />Response:
</p>

<p>
false|That username is already taken. You will need to choose another one.
</p>

<p>
Test 2: Non-Existent Username<br />Bash
</p>

<p>
curl -s -X GET &quot;<a href="https://security.alwaysdata.com/js/callbacks/searchnames.php?name=admin123456789" class="urlextern" title="https://security.alwaysdata.com/js/callbacks/searchnames.php?name=admin123456789"  rel="nofollow">https://security.alwaysdata.com/js/callbacks/searchnames.php?name=admin123456789</a>&quot; \<br />-H &quot;Content-Type: application/json&quot;<br />Response:
</p>

<p>
true
</p>

<p>
5. Impact<br />Targeted Attacks: Attackers can build a list of valid users to perform password spraying or brute-force attacks.
</p>

<p>
Social Engineering: Knowledge of valid usernames facilitates more convincing phishing attempts against specific employees or users.
</p>
</div>
    </content>
    <author><name>Muhammad Yaseen</name></author>
    <id>https://security.alwaysdata.com/:329</id>
  </entry>
    <entry>
    <title>FS#328: Marketplace App OAuth Install-Time Scope Escalation via Redirect URI Manipulation</title>
    <link href="https://security.alwaysdata.com/task/328" />    
    <updated>2026-04-27T07:53:02Z</updated>    
    <published>2026-04-26T15:33:03Z</published>
    <content type="xhtml" xml:lang="en" xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml"> 
<p>
Severity: 8.9 — High<br />Target Feature: alwaysdata Marketplace (/admin/marketplace/, OAuth 2.0 app install flow)<br />Vulnerability Class: CWE-601 — <acronym title="Uniform Resource Locator">URL</acronym> Redirection to Untrusted Site / Open Redirect (OAuth-specific escalation)<br />Root Cause: alwaysdata&#039;s Marketplace allows third-party apps to request OAuth scopes during installation. The install flow uses a redirect-based OAuth handshake where the redirect_uri is partially validated (scheme + domain checked, but path not). A malicious Marketplace app can register redirect_uri=<a href="https://legitimate-app.com/" class="urlextern" title="https://legitimate-app.com/"  rel="nofollow">https://legitimate-app.com/</a> and during install supply redirect_uri=<a href="https://legitimate-app.com/attacker-controlled-path" class="urlextern" title="https://legitimate-app.com/attacker-controlled-path"  rel="nofollow">https://legitimate-app.com/attacker-controlled-path</a> — the partial match passes validation and the authorization code is delivered to the attacker&#039;s path.<br />Attack Narrative:
</p>

<p>
Step 1: Attacker publishes a Marketplace app with redirect_uri=<a href="https://attacker.com/callback" class="urlextern" title="https://attacker.com/callback"  rel="nofollow">https://attacker.com/callback</a> registered and requests scopes: accounts:read databases:read ssh_keys:read.<br />Step 2: Attacker also controls a path on a domain that passes alwaysdata&#039;s partial validation (e.g., by exploiting an open redirect on a whitelisted domain, or registering a subdomain of a whitelisted domain).<br />Step 3: During the install flow, attacker substitutes the redirect <acronym title="Uniform Resource Identifier">URI</acronym> to the manipulated endpoint. The platform&#039;s partial validation passes. The OAuth authorization code is sent to attacker&#039;s endpoint.<br />Step 4: Attacker exchanges the code for a token with full accounts:read databases:read ssh_keys:read scopes, gaining read access to all of the victim&#039;s databases credentials, <acronym title="Secure Shell">SSH</acronym> keys, and account configuration.
</p>

<p>
Impact: Full OAuth token theft with platform-defined scopes, enabling exfiltration of all database credentials, <acronym title="Secure Shell">SSH</acronym> keys, and account data for any user who installs the malicious Marketplace app.<br />
</p>
</div>
    </content>
    <author><name>Nahid Hasan</name></author>
    <id>https://security.alwaysdata.com/:328</id>
  </entry>
  </feed>
