← Back to list

API-RTA Exam Walkthrough — Passed | CyberWarFare Labs

Certification: API-RTA (API Red Team Analyst)

Nitesh Ghimire · 2026-07-28 04:54 · 0 claps · 5.3 min read
#cybersecurity #api #cwl #api-rta #cyberwarfare-labs
Open on Medium ↗
Wiki topics: SAF · Safety & Alignment 🔒 · Cybersecurity

API-RTA Exam Walkthrough — Passed | CyberWarFare Labs

Certification: API-RTA (API Red Team Analyst)

Issued by: CyberWarFare Labs (CWL)

Difficulty: Easy

Format: Practical, black-box, flag-based exam against a single live target

Author: Nitesh Ghimire

API-RTA is CyberWarFare Labs’ dedicated API security exam — a single deliberately vulnerable e-commerce target, a set of flags, and a format that pushes you toward chaining several smaller logic flaws together rather than leaning on one big exploit. The target follows a familiar shape for anyone who’s worked through OWASP API Top 10 material: a storefront, a login, a wallet, a cart, and a checkout flow, all sitting on top of a REST API most of the frontend never fully exposes.

The exam’s own framing lays out the intended attack path clearly:

  1. Explore — browse the store anonymously and observe API behavior
  2. Authenticate — log in to receive a JWT and unlock user-level functionality
  3. Exploit — manipulate tokens, parameters, and logic to access restricted assets
  4. Purchase — complete checkout of premium products using chained vulnerabilities

This write-up walks through how I actually moved through those four stages — including the flag that nearly beat me — and explains the reasoning behind each pivot, not just the commands.

Getting Oriented

Before touching a single crafted payload, I pulled every static asset the frontend ships:

bash

curl -s http://TARGET:PORT/static/checkout.html
curl -s http://TARGET:PORT/static/payment.html
curl -s http://TARGET:PORT/static/admin.html
curl -s http://TARGET:PORT/static/app.js

This step alone is worth doing exhaustively, because it tells you what’s real and what’s decoration. The checkout page’s “card payment” option, for instance, turned out to be entirely non-functional — processPayment() for the card method never sends cardNumber, expiry, or pin anywhere. It just displays a fake "Processing Payment…" message and redirects to an error state after a setTimeout. Only the gift-card path actually posts data to the API. Recognizing that early saved a lot of wasted effort probing a form that was never going to yield anything.

Mapping the Data Model Through SQL Injection

Once I had an authenticated session, a SQL injection point let me pull the schema directly rather than guessing table structure blindly:

bash

curl -s -G "http://TARGET:PORT/api/v1/products/search" \
  --data-urlencode "name=x' UNION SELECT name,sql,3 FROM sqlite_master WHERE type='table'--" \
  -H "Cookie: vulncart_token=$TOKEN"

The dump confirmed something important that shaped the rest of the exam: the orders table only contains id, user_id, product_id, and price — no name field anywhere near it. That single fact ruled out an entire class of approach I'd otherwise have kept chasing, and it's a good example of why reading the schema directly beats guessing at it through trial-and-error queries.

Forging Admin-Scoped Access

With the JWT signing mechanism recovered, I was able to mint a token carrying role: admin and broader scopes than a normal session should have. That access unlocked endpoints the storefront UI never links to directly — order records outside my own account, and admin-facing routes worth probing systematically once obtained:

bash

curl -s http://TARGET:PORT/api/v1/admin/orders -H "Cookie: vulncart_token=$TOKEN"
curl -s http://TARGET:PORT/api/v1/orders/1337 -H "Cookie: vulncart_token=$TOKEN"

This is the BOLA/BFLA core of the exam in practice: once the token’s contents are attacker-controlled, “who am I” and “what am I allowed to see” collapse into the same unenforced assumption.

Reversing the Gift Card Algorithm

The wallet top-up flow accepts gift-card codes behind a simple math captcha — but the code-generation logic itself sits in plain, unminified client-side JavaScript rather than behind any server-side secret. Reading the relevant function directly exposed a fixed character-substitution scheme applied to a hardcoded seed string, with a static suffix appended. No randomness, no server round-trip, fully reproducible by hand. Redeeming it afterward was a normal POST to the payments endpoint.

The lesson here echoes the checkout price field elsewhere in the app: client-side JavaScript is not a place to hide logic that determines value. If a browser can compute it, an attacker can compute it identically, outside the browser entirely.

The Cloud Layer: Lambda and S3

Order status checks in this environment route through an AWS Lambda Function URL, which itself appears to proxy objects straight out of an S3 bucket based on a path query parameter:

Bash:

LAMBDA="https://<function-id>.lambda-url.<region>.on.aws"
curl -s "$LAMBDA/fetch_order_status?path=orders/order_status.json"

Nothing in this flow ties the requester’s identity to which object paths they’re allowed to read — the function fetches whatever path it’s given, provided the object exists. That’s functionally SSRF-adjacent even without the classic “hit an internal IP” shape: a server-side component is still fetching attacker-chosen data on the attacker’s behalf.

Distinguishing the two error states mattered a lot here:

  • **NoSuchKey** — the request reached S3 fine; the path just doesn't exist. A good sign, structurally.
  • **400 Bad Request** — the path parameter itself was malformed, unrelated to whether the target object exists.

The Flag That Almost Beat Me: Chasing “Sherlock Holmes”

One flag referenced “Sherlock Holmes” somewhere in the environment, and it resisted every direct approach. The orders and users tables had no name column anywhere close to it — already established through the earlier schema dump. Every static page came back clean on a targeted grep. Every S3 path I guessed by hand for a sherlock_holmes-style filename came back NoSuchKey.

At that point the honest move was to stop guessing one path at a time and treat it as a bounded search problem instead. I built a targeted wordlist crossing plausible folder names against plausible filenames, and fuzzed the Lambda endpoint systematically with ffuf:

bash

ffuf -u "$LAMBDA/fetch_order_status?path=FUZZ" \
  -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt \
  -mc all -fr "NoSuchKey"

Then a targeted double-loop combining folder and filename candidates:

bash

for folder in orders users customers payments vip premium; do
  for name in sherlock_holmes sherlock holmes 221b baker_street watson; do
    result=$(curl -s "$LAMBDA/fetch_order_status?path=$folder/${name}.json")
    if [[ "$result" != *"NoSuchKey"* ]] && [[ "$result" != *"400"* ]]; then
      echo "HIT: $folder/${name}.json -> $result"
    fi
  done
done

What manual guessing couldn’t resolve in an evening, a bounded, automated search resolved in minutes. The real takeaway wasn’t the flag itself — it was the discipline of recognizing when to stop guessing and change the shape of the search entirely.

How the Chain Fits Together

[Static asset recon] → app.js and static pages map real vs. decorative endpoints
        ↓
[SQL injection] → full schema dump → confirms no name field, hidden product data
        ↓
[Forged admin-scoped token] → BOLA/BFLA on orders and admin routes
        ↓
[Client-side gift-card algorithm, reversed by hand] → wallet top-up
        ↓
[Lambda fetch_order_status] → S3-proxied objects, no path-level authorization
        ↓
[Systematic ffuf fuzzing] → the flag that hand-guessing couldn't reach

What I Took Away From This

Read every static asset before authenticating. The checkout page’s fake card-payment flow and the gift card’s exposed algorithm were both sitting in plain sight — the frontend told the truth about the API’s shape long before any exploitation started.

A clean schema dump is worth more than a dozen guesses. Once I knew definitively that orders had no name column, I stopped wasting time on approaches built on a wrong assumption.

When manual guessing stalls, fuzz systematically. The Sherlock Holmes flag wasn’t hiding cleverly — it was just sitting past the point where hand-typed curl requests stop being a reasonable search strategy.

Closing Thoughts

API-RTA rewards the instincts its own four-step framing promises: explore before authenticating, treat client-side code as documentation the developers didn’t mean to publish, and assume any value or path the client controls is one you should try controlling yourself. The cloud-proxy portion — a Lambda function fetching S3 objects with no authorization layer between the request and the object store — is a pattern worth recognizing on sight; it shows up constantly in real-world serverless architectures built fast and never revisited.

If you’re working through API-RTA yourself or working in AppSec more broadly, I’d like to hear what you’re building — feel free to connect.

#APIsecurity #PenetrationTesting #CyberSecurity #RedTeam #InfoSec #CyberWarfareLabs

[embed]API Red Team Analyst (API-RTA) - Nitesh Ghimrie Got a Certificate from CyberWarFare Labslabs.cyberwarfare.live


메타데이터
post_id
42620bbafc8b
slug
api-rta-exam-walkthrough-passed-cyberwarfare-labs-42620bbafc8b
url
https://medium.com/@ghimirenitesh8/api-rta-exam-walkthrough-passed-cyberwarfare-labs-42620bbafc8b
canonical_url
https://medium.com/@ghimirenitesh8/api-rta-exam-walkthrough-passed-cyberwarfare-labs-42620bbafc8b
author_url
https://medium.com/@ghimirenitesh8
status
ok
fetched_at
2026-09-17 10:18:52