The Admin Panel That Was Meant to Be Forgotten — But Was Still Live 🔐
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:
- Changed User-Agent to mimic a browser on their internal IP range:
Mozilla/5.0 (compatible; CompanyAdminBot/1.0; +http://192.168.0.2) - Added X-Forwarded-For: 127.0.0.1
- Set the Referer header to https://admin.companyname.com/dashboard
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 👾
—