July 11, 2026
Lab 2: Broken Access Control “Unprotected Admin Functionality with Unpredictable URL” (PortSwigger…
Intro

By Nitish Mukhiya
4 min read
Intro
This is the second lab in the Broken Access Control series from PortSwigger Web Security Academy. It's a step up from the first one: this time the admin panel isn't sitting at a simple, guessable path like /administrator-panel. Instead, its URL is randomly generated — but as we'll see, that alone doesn't make it secure.
As always, this is a legal, sandboxed training lab. Only apply these techniques on systems you're authorized to test.
By Nitish Mukhiya
Lab Objective
The lab description tells us exactly what we're up against:
"This lab has an unprotected admin panel. It's located at an unpredictable location, but the location is disclosed somewhere in the application. Solve the lab by accessing the admin panel, and using it to delete the user
carlos."
So this time, brute-forcing or guessing the URL won't work directly. The clue is that the app itself leaks the URL somewhere — we just need to find where.
Step 1 — Access the Lab
Click "Access the lab" to launch a fresh instance. It looks like a typical e-commerce site again, with no visible admin link on the page.
Step 2 — Check robots.txt (Ruled Out)
The first instinct, based on the previous lab, is to check robots.txt. But this time it won't help — the admin path is randomly generated per lab instance, so it can't simply be hardcoded there.
Step 3 — Inspect the Page Source
Since the lab description hints that the location is "disclosed somewhere in the application," the next logical step is to look at the raw HTML and JavaScript sent to the browser.
Right-click the page and choose "View page source" (or navigate to view-source:https://<lab-id>.web-security-academy.net/).
Scrolling through the source, we find an inline <script> block in the page:
javascript
var isAdmin = false;
if (isAdmin) {
var topLinksTag = document.getElementsByClassName("top-links")[0];
var adminPanelTag = document.createElement('a');
adminPanelTag.setAttribute('href', '/admin-lza0w7');
adminPanelTag.innerText = 'Admin panel';
topLinksTag.append(adminPanelTag);
var pTag = document.createElement('p');
pTag.innerText = '|';
topLinksTag.appendChild(pTag);
}var isAdmin = false;
if (isAdmin) {
var topLinksTag = document.getElementsByClassName("top-links")[0];
var adminPanelTag = document.createElement('a');
adminPanelTag.setAttribute('href', '/admin-lza0w7');
adminPanelTag.innerText = 'Admin panel';
topLinksTag.append(adminPanelTag);
var pTag = document.createElement('p');
pTag.innerText = '|';
topLinksTag.appendChild(pTag);
}This is the vulnerability. The developers assumed that since isAdmin is false for a normal user, this code block would simply never run — so no admin link would ever be shown. But the entire script, including the hardcoded admin panel path /admin-lza0w7, is still sent to and visible in every visitor's browser. Client-side conditions can hide a link visually, but they can't hide the source code itself.
Step 4 — Visit the Leaked Admin Path
With the path in hand, visit it directly:
https://<lab-id>.web-security-academy.net/admin-lza0w7https://<lab-id>.web-security-academy.net/admin-lza0w7Just like before, no authentication is required. The Users page loads directly, showing wiener and carlos, each with a Delete link.
Step 5 — Delete the User
Click Delete next to carlos.
The page confirms:
User deleted successfully!User deleted successfully!Only wiener remains in the users list.
Step 6 — Lab Solved
The status badge flips from "Not solved" to "Solved", with the congratulations banner confirming success.
Steps in summary:
- Ruled out
robots.txt(URL is randomized) - Viewed the page source and found the admin path hardcoded inside a client-side
if (isAdmin)block - Visited that path directly — no login required
- Deleted
carlos
Why Does This Vulnerability Happen?
This is a more subtle version of the same root cause as Lab 1: relying on the client to enforce access control. Even though the developers added a randomized URL and a client-side isAdmin check, both are cosmetic:
- Randomizing the URL only adds obscurity, not real protection.
- Client-side JavaScript conditions (
if (isAdmin)) only control what's displayed — never what's accessible. Anyone can read the full source, extract the path, and hit it directly, bypassing the check entirely.
The server never verified whether the requesting user actually had admin privileges before serving the admin panel.
How to Fix This (Prevention)
- Never place access-control logic on the client side. JavaScript checks like
if (isAdmin)can always be read or bypassed by the user. - Enforce authorization on the server for every request to a sensitive endpoint — check the user's session/role before returning any data.
- Don't embed sensitive paths, tokens, or logic in client-side code, even conditionally — anything sent to the browser is visible, regardless of whether it executes.
- Combine random/unpredictable URLs with proper server-side checks, not as a replacement for them — obscurity should only ever be a secondary layer, never the primary control.
Conclusion
This lab builds nicely on the first one: instead of an easily discoverable admin URL, we have to actually inspect the client-side source code to leak it. It's a great reminder that any logic living in the browser is not a security boundary — real protection always has to happen on the server. Broken Access Control keeps proving why it's #1 on the OWASP Top 10.
Happy (ethical) hacking!
👤 About the Author
Nitish Mukhiya is a security researcher and bug bounty hunter focused on web application security, currently sharpening his skills through PortSwigger's Web Security Academy.
Connect:
- 🐦 Twitter/X — @NITISHMUKHIYAJ
- 💼 LinkedIn — linkedin.com/in/nitishmukhiya
- 💻 GitHub — github.com/nitishmukhiyaji
- ✍️ Medium — medium.com/@nitishmukhiya
- 🌐 Portfolio — portfolio-livid-iota-e4saycgr44.vercel.app