TaskForge internal
not signed in

Missions

0 / 8 complete

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.
  1. Log in as Alice, then open the Inspector (right side). Click around the app and watch which requests the browser sends.
  2. 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.
  3. 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.
  1. In the Inspector, type the URL /users, method GET, and press Send. No Authorization header needed.
  2. The response is a JSON array — one object per user, with PII fields visible to anyone on the public internet.
  3. 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.
  1. Send GET /users/1. Still no login required.
  2. Compare this response to the one from mission 2. Notice anything extra?
  3. The response includes a password_hash (bcrypt). Offline cracking with hashcat or john never 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.
  1. Send GET /debug/config. No auth.
  2. Scan the response for JWT_SECRET, SENDGRID_API_KEY, GITLAB_PAT, SLACK_WEBHOOK_URL, SUPPORT_EMAIL.
  3. 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.
  1. In the Inspector, send POST /auth/register with header content-type: application/json.
  2. Body: {"username":"team-pwn","email":"team@pwn.local","password":"hunter2","role":"admin"}. Pick any unused username.
  3. 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.
  1. Log in first (any user), then send GET /tasks/search?q=abc' with the Authorization: Bearer <token> header.
  2. The server returns HTTP 500 and echoes the raw SQL (with your quote stuck in the middle). Injection confirmed.
  3. Follow-up: craft a UNION SELECT id,username,email,password_hash,... payload in the q parameter to pull the users table 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.
  1. Send POST /comments with body {"body":"<img src=x onerror=alert(1)>"}.
  2. The response contains an html field with your tag unchanged — no sanitization.
  3. 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.
  1. Send POST /auth/forgot-password with body {"email":"typingmyusernamenow@gmail.com"}. Grab the resetToken from the JSON response.
  2. Send POST /auth/reset-password with body {"token":"<paste>","newPassword":"pwned123"}.
  3. 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.

Inspector

Every request this tab makes, captured live.

Request editor

Response

— send a request —

        

mission complete