Sitemap

The Admin Panel That Was Meant to Be Forgotten — But Was Still Live 🔐

3 min readJul 4, 2025

--

Some bugs don’t hide in broken code. They hide in broken assumptions.

Let me walk you through a bug I found that wasn’t supposed to exist. Not because the code was clean — but because the developers thought it was already gone.

This is the story of how I found a legacy admin panel that was never removed from production, uncovered using nothing but curiosity, patience, and a few overlooked headers.

The Ghost of Admins Past 👻

I was testing an old CRM web app for a private bounty program. At first glance, it looked well-maintained — clean UI, minimal endpoints, and standard login/auth flows.

Nothing suspicious showed up in the recon phase. All the usual stuff was locked down. Swagger.json? 403. Robots.txt? Clean. No juicy subdomains. Even the /admin route redirected properly to the login page.

But here’s where it gets interesting.

While intercepting traffic during normal user flows, I saw a 403 response from a strange endpoint:
/staff/settings — a route that shouldn’t appear to non-staff users at all.

Even weirder: it returned 403 Forbidden, not 404 Not Found.

That was my first clue.

Spoofing My Way Through the Door 🚪

I’ve seen this pattern before. Some older apps rely on internal headers — like X-Forwarded-For, Referer, or even User-Agent — to allow internal access during dev/testing.

So I started tweaking:

I resent the request to /staff/settings.

200 OK.

That’s when I knew I was in.

The “Lite” Admin Panel That Shouldn’t Exist ✨

From there, I discovered a new route mentioned in the JavaScript bundle:
/admin-lite — a stripped-down admin interface built for internal training or limited use.

It had:

  • Order history
  • Refund initiation
  • Access to user contact data
  • Basic analytics

It was clearly never intended for public access. The devs probably used it for QA or customer support training — and then forgot it existed.

But here’s the kicker:
No CSRF protection, no input validation on refund reason field, and no IP rate limits.

I created a test user, placed an order, logged in via the main site — and then spoofed a POST request to refund that same order via /admin-lite/refund.

Refund succeeded. No admin login. No alerts.

Why This Bug Existed 🧠

The vulnerability wasn’t in the code. It was in the mindset.

“Nobody uses this anymore.”
“It’s hidden.”
“It’s only on staging.”

Those are developer assumptions. But in production — if it responds, it matters.

This wasn’t even a feature flag issue. This was a forgotten legacy route still deployed to prod, unintentionally accessible via basic header spoofing.

Tips to Catch Bugs Like This 🔍

This bug taught me to never stop digging — even when recon looks clean. Here are tactics that helped me, and might help you too:

✅ Check error codes carefully. 403 vs 404 tells you if the route exists behind a wall.
✅ Spoof headers like Referer, X-Forwarded-For, and User-Agent. You’d be shocked how many apps still use them for access control.
✅ Read JavaScript bundles. Tools like source-map-explorer or just a grep for “admin” can reveal forgotten routes.
✅ Search GitHub or Wayback Machine. Old documentation sometimes references retired endpoints that are still live.
✅ Try legacy routes. Think /v1/admin, /beta-dashboard, /internal, /staff, /console, etc. You’ll be surprised how often they work.

Final Thought: Every App Has a Memory 🧠

Even clean apps can carry ghosts from past versions.
Even mature teams forget to remove test features.
And even “non-bugs” can become critical if no one’s watching.

The internet never forgets — so make sure you don’t either.

Have you ever found an old feature or tool that should’ve been killed off, but wasn’t?
Drop your legacy bug stories in the comments👇

Let’s share, learn, and keep each other sharp.

Until next time,
Happy Hunting 👾

--

--

Responses (1)