Prototype Pollution Lab: Become Admin Without an Admin Flag

✨ AI Summary
ProfileApp lets you save settings as JSON, and merges them into your user object. Your account has no isAdmin field — but the admin check still reads user.isAdmin. In JavaScript, if an object does not have a property, the lookup walks up the prototype chain. Pollute the prototype and the check passes for everyone.
Lab at a glance: Intermediate · 90 XP · about 15 minutes · runs in your browser, no signup or setup. Try it yourself first; the full solution is behind a spoiler below.

What this lab teaches

Prototype pollution is a JavaScript-specific bug. A recursive merge or deep-copy routine copies attacker-controlled keys into an object, and if it does not guard against the special key __proto__, the attacker can write properties onto Object.prototype — the object every other object inherits from.

The lesson: in JavaScript, missing-property lookups fall through to the prototype. Change the prototype and you change the default answer for every object in the app at once.

How ProfileApp saves your settings

You submit settings as JSON and a merge function copies each key into your user record. It recurses into nested objects without checking for dangerous key names, so a nested __proto__ key is followed straight onto the shared prototype rather than onto your own object.

The attack approach

  • Notice settings are saved as nested JSON and merged into the user object.
  • Recall that the admin gate checks user.isAdmin, and your object has no such property.
  • Send a settings object whose nested structure targets __proto__ and sets isAdmin to true.
  • Because the merge is recursive and unguarded, the write lands on Object.prototype — so every object, including yours, now answers true.

Full walkthrough (spoiler)

If the profile still shows you as a normal user, open the steps.

Show the step-by-step solution
  1. In the settings JSON, add a nested object keyed __proto__ containing isAdmin: true.
  2. Save it. The recursive merge follows the __proto__ key and writes isAdmin onto Object.prototype instead of onto your record.
  3. Open View Profile. The check user.isAdmin === true now passes, because the lookup falls through your (flagless) object to the polluted prototype.
  4. The lab detects the admin view and marks you solved.

Why it works

JavaScript resolves user.isAdmin by checking your object, then its prototype, then Object.prototype. Your object has no such key, so normally the answer is undefined. By writing to the prototype through an unguarded merge, you set the default value every object inherits — so the admin check passes without your record ever gaining the field.

Core principleReject the keys __proto__, constructor and prototype in any merge or deep-copy of untrusted input, and create data objects with no prototype.

Where this happens in the real world

Prototype pollution has hit major libraries — Lodash carried a documented prototype-pollution flaw (CVE-2019-10744) that affected a huge number of dependent projects, and jQuery, minimist and others have shipped the same class of bug. On the client it can lead to DOM XSS; on Node.js servers it can corrupt application logic or enable further attacks.

How to defend against it

  • Block dangerous keys (__proto__, constructor, prototype) in every merge, assign and deep-copy of user input.
  • Create lookup objects with Object.create(null) so they have no prototype to pollute.
  • Use Map instead of plain objects for key/value data driven by user input.
  • Keep merge libraries patched, and prefer ones that are hardened against this by default.
Mr Elite tip: When auditing Node or front-end code, search for recursive merge/extend/clone helpers and check whether they ever copy a key literally named __proto__ from user input.

Try it, then go further

Reading about it is the easy part. Open the lab, try your own approach before peeking at the spoiler, and see how many attempts it takes.

❓ Frequently asked questions

What is prototype pollution?
A JavaScript vulnerability where attacker-controlled keys get written onto Object.prototype through an unguarded merge or copy, changing the default properties every object inherits.
Why does setting __proto__ affect other objects?
Because objects that lack a property inherit it from their prototype. Writing to Object.prototype sets a default value that every object in the app sees.
Is it only a front-end problem?
No. On the client it can cause DOM XSS; on Node.js servers it can corrupt logic, bypass checks or enable denial of service and further attacks.
How do I prevent it?
Reject the keys __proto__, constructor and prototype in merges, build lookup objects with Object.create(null) or Map, and keep merge libraries patched.
⚡
Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free
Lokesh N. Singh aka Mr Elite
Lokesh N. Singh aka Mr Elite
Founder, Securityelites · AI Red Team Educator
Founder of Securityelites and creator of the SE-ARTCP credential. Working penetration tester focused on AI red team, prompt injection research, and LLM security education.
About Lokesh ->

Leave a Comment

Your email address will not be published. Required fields are marked *