Missions
mission 1 The confidential task
You started this week as Alice on the ops team. At the welcome lunch you overheard
Bob mention a task titled "Confidential: salary review notes" that's supposedly
private to him. You don't think the app actually keeps it private.
Your goal: read the contents of that task from your own browser, without Bob's password.
HR sent you your own dev login: alice / alice123. Start there.
Stuck? Open three progressive hints.
- Log in as Alice, then open the Inspector (right side). Click around the app and watch which requests the browser sends.
- When the dashboard loads your tasks, look at the response of
GET /tasks. The UI shows only Alice's, but the server might return more. - Once you spot Bob's task in the raw response, find a single request the app already sent for one specific task, and change the id to Bob's before resending.
mission 2 The internal directory
Someone left a team-directory endpoint wide open. Dump everyone's email, phone, address, and role — no login, no token, no form on the page.
Stuck? Open three progressive hints.
- In the Inspector, type the URL
/users, methodGET, and press Send. No Authorization header needed. - The response is a JSON array — one object per user, with PII fields visible to anyone on the public internet.
- Real-world parallel: "team directory" or "About" pages left unauthenticated because "who would scrape them?"
mission 3 The bonus field
The directory is one thing. What if you ask the server for one specific user? Sometimes servers are more generous when you're specific.
Stuck? Open three progressive hints.
- Send
GET /users/1. Still no login required. - Compare this response to the one from mission 2. Notice anything extra?
- The response includes a
password_hash(bcrypt). Offline cracking withhashcatorjohnnever touches the server — and the server can't rate-limit what it doesn't see.
mission 4 The debug door
Someone wired up a "debug config" endpoint and forgot about it. Every secret the server has is one GET away.
Stuck? Open three progressive hints.
- Send
GET /debug/config. No auth. - Scan the response for
JWT_SECRET,SENDGRID_API_KEY,GITLAB_PAT,SLACK_WEBHOOK_URL,SUPPORT_EMAIL. - Each of these pwns something real: forge tokens, send mail as your brand, push to your repo, post to your Slack, target a real human's inbox.
mission 5 Promote yourself
TaskForge has an admin role. The UI has no "become admin" button. But the registration
endpoint doesn't know that.
Stuck? Open three progressive hints.
- In the Inspector, send
POST /auth/registerwith headercontent-type: application/json. - Body:
{"username":"team-pwn","email":"team@pwn.local","password":"hunter2","role":"admin"}. Pick any unused username. - The 201 response contains a JWT with
role: "admin". The server trusted whatever you typed. Mass-assignment bug — the user controls their own privilege level.
mission 6 Feed the database a quote
The task search glues your input straight into a SQL string. One stray quote and the server confesses.
Stuck? Open three progressive hints.
- Log in first (any user), then send
GET /tasks/search?q=abc'with theAuthorization: Bearer <token>header. - The server returns HTTP 500 and echoes the raw SQL (with your quote stuck in the middle). Injection confirmed.
- Follow-up: craft a
UNION SELECT id,username,email,password_hash,...payload in theqparameter to pull theuserstable through the search endpoint.
mission 7 Smuggle HTML through comments
The /comments endpoint renders markdown — with raw HTML allowed. Whatever tags you send
come back intact.
Stuck? Open three progressive hints.
- Send
POST /commentswith body{"body":"<img src=x onerror=alert(1)>"}. - The response contains an
htmlfield with your tag unchanged — no sanitization. - Any client that dumps this HTML into the DOM runs your script. Stored XSS — the payload lives on the server until it's served to the next reader.
mission 8 Take over the organizer
The workshop organizer has a real inbox: typingmyusernamenow@gmail.com. The password-reset
flow sends them a real email — AND hands you the token in the HTTP response. Race the owner to the reset.
Stuck? Open three progressive hints.
- Send
POST /auth/forgot-passwordwith body{"email":"typingmyusernamenow@gmail.com"}. Grab theresetTokenfrom the JSON response. - Send
POST /auth/reset-passwordwith body{"token":"<paste>","newPassword":"pwned123"}. - Sign in as
workshop_organizer / pwned123. You're an admin now — and the real inbox owner still has the unused reset email sitting in their mailbox.
Sign in to TaskForge
Use the credentials sent to you by HR Onboarding.
My tasks
Things assigned to you. Click View or Edit to work on one.
You don't have any tasks yet.