The Invite That Made an Owner: Reading What the Form Posts

Privilege Escalation & Post-Exploitation Level 2/4 ~3 min September 22, 2026

The challenge

Vantage found an Owner in its workspace that nobody remembers creating. The account belongs to a real colleague who was invited in an ordinary batch of four, and the team lead who sent that batch is certain they never touched a role. They are telling the truth: the invite screen offers no role control at all, and it says in plain text that everyone invited from it joins as a Member. But what a page shows and what a page posts are two different documents, and the server believed the second one. This is the screen exactly as it was submitted, captured by the support tool. Read the markup and name the address that arrived as Owner.

What you'll learn

  • Compare what a page renders against what its form would post
  • Read two positional arrays as co-indexed data
  • Explain why a hidden input is neither secret nor read-only
  • Recognise client-supplied authorisation as the underlying flaw
  • Describe why this escalation leaves a clean-looking audit trail

Skills tested

Client-side source reviewPrivilege escalation reasoningForm and parameter analysis

Prerequisites

  • What an HTML form and a hidden input are
  • Reading a comma-separated list

How it works

Every form is two documents. There is the one the user reads, and there is the set of name and value pairs the browser will post. They are supposed to agree. When they do not, the server almost always believes the second one, because the second one is the only one it ever sees.

Preview here is internally consistent: four invitees, the word Member beside each, and a sentence promising that this screen grants nothing else. There is no role control to misuse, which is exactly why the team lead's account of events is truthful and still wrong.

View source carries five fields the page never drew. Two of them are lists. invitees is four addresses in order and roles is member,member,owner,member. The comment above them states the contract: roles[] is positional and must stay in sync with invitees[]. Line them up and the third entry of each pairs [email protected] with owner.

The flaw is not that a field was hidden. display:none and type="hidden" are layout instructions, not access control. The value still goes over the wire, and anyone with developer tools, an intercepting proxy or a single curl command can change it. The flaw is that the server accepted a role from the client at all, so the promise printed on the page was enforced in exactly one place: the page.

Two things make this pattern worse than it looks. Parallel arrays joined by index are brittle, and one reordering anywhere in the stack silently reassigns every role. And the aftermath is invisible: the invite, the acceptance and the new Owner all read as legitimate in the audit log, because from the server's point of view an authorised user asked for precisely this.

Common mistakes

  • Answering [email protected]. The owner entry is third, not fourth. Count the positions rather than assuming it is the last one.
  • Answering with the role instead of the address. The question asks who, and the answer label says so.
  • Trusting the rendered rows. All four say Member, which is why the visible page is not evidence.
  • Calling the hidden attribute the vulnerability. Hiding a field changes nothing about whether the browser posts it.
  • Assuming a privileged account did this. A client-supplied role needs no privilege, only a request.

How to defend against it

Decide roles on the server, from who is asking rather than from what they sent.

  • Drop roles from the request entirely. If the screen can only create Members, the handler should assign Member and ignore any role in the body.
  • Where a role genuinely can be chosen, re-check on the server that the caller is allowed to grant it, and that the role is in the permitted set for that plan and organisation.
  • Bind the request to the organisation from the session, not from an org field the client controls.
  • Replace the parallel arrays with a list of objects, one address and one role per entry, so nothing depends on position.
  • Alert on any Owner grant that did not come from the role management screen. This escalation is silent precisely because every individual record looks normal.

Full solution

Pro and Max members unlock the complete step-by-step walkthrough.

Go Pro

Community stats

191 completions
96% success rate
Malekith First blood

Related Daily Hacks

32,000+ Hackers Real labs Free
Start Hacking Free or solve today's hack, no account needed