Learning API Security Through Hands-On Exploration with crAPI
As someone diving deeper into ethical hacking and security testing, I wanted a way to get practical experience working with insecure APIs in a controlled environment. That’s why I decided to take on a personal project using crAPI (Completely Ridiculous API) — a vulnerable API-based application designed specifically for learning and testing API security flaws.
Instead of just reading about vulnerabilities or watching tutorials, I wanted to interact directly with real API endpoints, manipulate requests, test authentication logic, and try to find ways systems could be abused if implemented incorrectly. Throughout this project, I documented every step I took, including the API calls I made, the tools I used, and the vulnerabilities I discovered.
The goal wasn’t just to follow a path — it was to explore, test, and better understand the world of API security through active experimentation. In the process, I used tools like Postman, Burp Suite, FFUF, and MailHog, and learned how to think more like an attacker when looking at API design.
Objectives
At the start, I laid out some clear goals for what I wanted to achieve with this project:
By the end of the project, I wanted to have a clearer picture of what makes an API secure — and more importantly, what makes one vulnerable.
What is crAPI?
crAPI (short for Completely Ridiculous API) is an intentionally vulnerable API-based web application built for security testing and education. Unlike traditional web applications that might have static pages or limited interaction, crAPI is built entirely around API calls. It includes features like user authentication, vehicle profiles, shopping carts, media uploads, and community forums — all powered by backend APIs.
It’s designed to reflect how many real-world apps function today: the frontend (web or mobile) talks to the backend using RESTful JSON APIs. This makes it a great learning tool for understanding how attackers target APIs and what happens when the underlying security isn’t properly implemented.
What Are APIs and Why Do They Matter?
APIs — or Application Programming Interfaces — are how different software systems talk to each other. In modern apps, especially those with separate frontends and backends, APIs are what handle user data, manage sessions, update content, and process business logic.
For example, when you open a mobile app and view your profile, it’s likely using a GET /user/profile API call behind the scenes. When you update your settings, it might use a PUT /user/settings call. APIs are the backbone of modern application logic.
Because they sit between users and sensitive backend services, APIs have become a major target for attackers. If not properly secured, APIs can expose private data, allow unauthorized access, or enable manipulation of system behavior. That’s why understanding API security — and the common mistakes developers make — is so important.
OWASP API Security Top 10: What I Kept in Mind
As I worked through this project, I constantly found myself referencing the OWASP API Security Top 10. These are the most common and dangerous security issues found in APIs, and they acted like a checklist of what to watch out for.
Here’s a quick breakdown of each one and what it means in simple terms:
1. Broken Object Level Authorization (BOLA): This happens when the app doesn’t check if you’re allowed to see or change something — like reading someone else’s messages just by changing a number in the URL.
2. Broken Authentication: If login or password systems aren’t built securely, attackers can log in as someone else without permission.
3. Broken Object Property Level Authorization: Sometimes you shouldn’t see everything about an object — like someone’s private info on their profile — but the API gives it to you anyway.
4. Unrestricted Resource Consumption: If the API doesn’t limit how much you can ask from it, someone can overload it and slow it down or crash it — kind of like spam calling a phone line.
5. Broken Function Level Authorization: Even if you’re not an admin, the API may still let you do admin things — like deleting other users — if it doesn’t double-check your role.
6. Unrestricted Access to Sensitive Business Flows: Important business actions (like canceling orders or transferring money) might be open to anyone using the API — even if they’re not supposed to have access.
7. Server-Side Request Forgery (SSRF): If the API fetches web pages or files and doesn’t verify what you give it, you could trick it into accessing internal company systems.
8. Security Misconfiguration: APIs sometimes come with settings left open or unprotected — like leaving a door unlocked. Attackers look for these easy wins.
9. Improper Inventory Management: Old or hidden APIs may still be running without anyone noticing — and if they aren’t patched or protected, attackers can find and abuse them.
10. Unsafe Consumption of APIs: If your app uses third-party APIs and blindly trusts whatever they send back, attackers can use that to sneak in harmful data or commands.
Environment Setup and Preparation
Before diving into the challenges, I first set up the necessary environment to interact with crAPI and perform API security testing.
crAPI Deployment
I deployed crAPI (Completely Ridiculous API) locally using Docker and Docker Compose. After pulling the required containers and starting the services, I verified that the application was running correctly by accessing the crAPI web interface at http://localhost:8888.
User Creation
To simulate realistic user behavior and test access controls, I created two separate accounts:
These users served as the foundation for authorization and privilege testing throughout the challenges.
Authentication and JWT Setup
After logging in as both Edward and Sam via the /identity/api/auth/login endpoint, I captured each user’s JWT from the API response.
To streamline testing, I saved these tokens as global variables in Postman:
This allowed me to easily switch between user sessions by referencing these variables in the Authorization header:
Authorization: Bearer {{jwt_ed}} // or {{jwt_sam}}
With the environment, users, and authentication in place, I was ready to begin testing the crAPI endpoints and uncovering vulnerabilities.
Challenge 1: Accessing Details of Another User’s Vehicle (BOLA)
This challenge demonstrated a Broken Object Level Authorization (BOLA) vulnerability, where the API failed to properly restrict access to objects belonging to other users.
To start, I authenticated as Edward and explored the API using Postman. While looking through the community-related endpoints, I sent a request to:
GET /community/api/v2/community/posts/recent
The response contained several public user posts. What stood out was that each post also included sensitive user information such as their nickname, email address, and most importantly, a vehicleid in UUID format. These UUIDs were not tied to my user and shouldn’t have been visible.
I copied one of the exposed vehicle UUIDs and used it in another request while still authenticated as Edward:
GET /identity/api/v2/vehicle/{uuid}/location
After replacing the path variable with the leaked vehicleid, the endpoint returned that user’s vehicle location data (GPS coordinates). This confirmed the vulnerability: there was no authorization check to confirm whether Edward should have access to that vehicle’s location. Any authenticated user could simply plug in another user’s UUID and retrieve private information.
Challenge 2: Access Mechanic Reports of Other Users (BOLA)
This next BOLA vulnerability involved mechanic reports submitted by users through the crAPI interface.
Before I could submit a report, I ensured Edward had a vehicle registered to his account. I added one through the web interface using a VIN and a pincode that was sent via email, accessed using the MailHog interface on port 8025. Once the vehicle was added, I verified it with:
GET /identity/api/v2/vehicle/vehicles
The response confirmed that a vehicle was linked to Edward’s account. With this in place, I submitted a mechanic request using the following endpoint:
POST /workshop/api/merchant/contact_mechanic
In the body, I included the mechanic code, a test problem description, Edward’s VIN, and a mechanic API callback URL pointing to the receive_report endpoint. The response included a direct report link and a numeric report ID. This report belonged to Edward.
To explore the vulnerability, I made a new request to view that report:
GET /workshop/api/mechanic/mechanic_report?report_id=6
To escalate the test, I modified the report_id parameter manually and sent additional requests with nearby values like 5 and 3. I used Burp Suite’s Intruder tool to automate this and observe the responses.
Several of the modified IDs returned valid mechanic reports submitted by other users. These reports contained sensitive service request information and user identifiers. No authentication or ownership check was done on the mechanic report endpoint. Even though I was logged in as Edward, I was able to retrieve other users’ reports simply by changing the ID value.
This was another example of Broken Object Level Authorization — the API did not verify ownership or apply access restrictions on a per-object basis.
Challenge 3: Reset the Password of a Different User (Improper Inventory Management & Broken Authentication)
This challenge focused on gaining unauthorized control of another user’s account by manipulating the password reset process. My target for this test was the user adam007@example.com, whose email I had already discovered earlier through a community post response.
I began by initiating a password reset request using the following endpoint:
POST /identity/api/auth/forget-password
In the request body, I used Adam’s email. The API responded with a success message indicating that an OTP (one-time password) had been sent to the specified address. Since I had access to MailHog at http://localhost:8025, I opened it and retrieved the OTP sent to Adam’s email.
Initially, I tried submitting the OTP using this endpoint:
POST /identity/api/auth/v3/check-otp
However, this version of the endpoint was rate-limited and quickly locked me out after a few incorrect attempts. Recognizing that the API used versioning, I tested the previous version:
POST /identity/api/auth/v2/check-otp
This version of the endpoint lacked brute-force protections entirely. It accepted unlimited attempts without locking me out. I fuzzed the OTP using a wordlist of 0000–9999 and quickly found the correct 4-digit code.
With that OTP, I was able to submit a password reset request with a new password of my choice. After confirmation, I attempted to log in as Adam using:
POST /identity/api/auth/login
I entered Adam’s email and the new password I set, and successfully received a new JWT. This meant I had fully taken over his account.
The vulnerability here was two-fold. First, there was a Broken Authentication issue — the OTP reset flow didn’t require validation against the current user session or ensure the user owned the email. Second, it demonstrated Improper Inventory Management — the older version of the API (v2) was still accessible and lacked security checks that the newer version (v3) had implemented. The existence of a legacy but insecure version of a critical endpoint created an exploitable gap.
Challenge 4: Find an API Endpoint That Leaks Sensitive Info of Other Users (Excessive Data Exposure)
This challenge was about identifying endpoints that returned more information than necessary, particularly sensitive data like emails.
Since I already had working tokens for both ed@example.com and sam@gmail.com, I used those to explore authenticated GET requests. While browsing the API in Postman, I came across this endpoint:
GET /community/api/v2/community/posts/recent
This endpoint returned recent community posts, but more importantly, it included detailed user info for each post author — including email addresses and vehicle UUIDs, which are both sensitive identifiers.
Recommended by LinkedIn
Here’s the actual response I received:
{
"posts": [
{
"id": "5we2dJ2siog8cZvR7fzyrQ",
"title": "Title 3",
"content": "Hello world 3",
"author": {
"nickname": "Robot",
"email": "robot001@example.com",
"vehicleid": "4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5",
"profile_pic_url": "",
"created_at": "2025-04-22T13:56:38.296Z"
},
"comments": [],
"authorid": 3,
"CreatedAt": "2025-04-22T13:56:38.296Z"
},
{
"id": "xCqTFAKyfM7jcFwCwqdMrC",
"title": "Title 2",
"content": "Hello world 2",
"author": {
"nickname": "Pogba",
"email": "pogba006@example.com",
"vehicleid": "cd515c12-0fc1-48ae-8b61-9230b70a845b",
"profile_pic_url": "",
"created_at": "2025-04-22T13:56:38.296Z"
},
"comments": [],
"authorid": 2,
"CreatedAt": "2025-04-22T13:56:38.296Z"
},
{
"id": "9WZZmpeYYJyFQDNWMHnvUo",
"title": "Title 1",
"content": "Hello world 1",
"author": {
"nickname": "Adam",
"email": "adam007@example.com",
"vehicleid": "f89b5f21-7829-45cb-a650-299a61090378",
"profile_pic_url": "",
"created_at": "2025-04-22T13:56:38.292Z"
},
"comments": [],
"authorid": 1,
"CreatedAt": "2025-04-22T13:56:38.292Z"
}
],
"next_offset": null,
"previous_offset": null,
"total": 3
}
Right away, it was clear that this endpoint exposed:
None of this information is necessary just to view a list of community posts. In a real-world API, you’d typically only want to return a display name or username — not contact info or internal identifiers.
This is a textbook example of Excessive Data Exposure. The API trusted the frontend too much and pushed data that could be used for user enumeration, spam, or further privilege escalation attacks.
Challenge 5: Leak Internal Property of a Video (Excessive Data Exposure)
After uploading a profile video through the crAPI web interface under Edward’s account, I returned to the API and inspected the user’s data using this endpoint:
GET /identity/api/v2/user/dashboard
This revealed a video_id, which I used in the following PUT request:
PUT /identity/api/v2/user/videos/{video_id}
In the response body, I discovered a field called:
"conversion_params": "-v codec h264"
This parameter wasn’t meant to be user-visible — it’s an internal configuration related to how the server processes uploaded video content. Knowing this would later help with another challenge, but on its own, it was already an Excessive Data Exposure issue.
Internal properties like conversion_params should never be exposed to users unless there's a legitimate need, and even then, only with proper access controls and filtering. Instead, I was able to freely access and view it just by being a regular authenticated user.
Challenge 6: Triggering a Layer 7 DoS via Unrestricted Resource Consumption
This challenge was all about identifying and exploiting a vulnerability where the API doesn’t properly limit how many times a request can be repeated — a setup for a denial-of-service attack at the application layer.
After previously using the POST /workshop/api/merchant/contact_mechanic endpoint during earlier testing, I already knew it accepted a few interesting parameters, including:
{
"mechanic_code": "TRAC_JHN",
"problem_details": "My car is haunted.",
"vin": "8CDEX84ZSDE097388",
"mechanic_api": "http://localhost:8888/workshop/api/mechanic/receive_report",
"repeat_request_if_failed": false,
"number_of_repeats": 1
}
For this challenge, I modified the last two fields to look like this:
"repeat_request_if_failed": true,
"number_of_repeats": 500
I sent the updated payload using Postman with one of my authenticated users (in this case, Edward), and got this response:
{
"message": "Service Unavailable. Seems like ou caused a layer 7 DoS :)"
}
This error response confirmed the system couldn’t handle the volume of repeated requests — a classic Unrestricted Resource Consumption vulnerability. The server tried to fulfill the repeated calls but hit a processing limit, crashing under the load.
In a production environment, this kind of behavior could be abused to cause major performance issues or even full outages by overwhelming the backend with resource-heavy operations — all without needing elevated permissions.
This was a clear example of why rate limiting and resource controls are essential parts of API security, especially for endpoints that trigger workflows or external API calls.
Challenge 7: Deleting Another User’s Video (Broken Function Level Authorization)
This challenge explored how poorly implemented function-level access control can allow users to perform administrative-level actions they shouldn’t have access to — a clear Broken Function Level Authorization vulnerability.
To begin, I used Sam’s account to upload a profile video through the web interface (via the profile section). Once the video was uploaded, I switched over to Postman and sent a request to the GET /identity/api/v2/user/dashboard endpoint while authenticated as Sam to retrieve the details, including the video_id. The response looked like this:
{
"id": 2,
"name": "Sam",
"email": "sam@example.com",
"video_id": 7,
...
}
With Sam’s video ID in hand, I switched back to Edward’s account (using the jwt_ed token) and tested the following endpoint:
PUT /identity/api/v2/user/videos/7
The request was accepted but didn’t delete the video. It returned a message saying the video couldn’t be found for Edward, which made sense — the system still tried to match the video ID to the authenticated user.
At this point, I changed two things:
Final request:
DELETE /identity/api/v2/admin/videos/7
With Edward’s JWT still in use, I sent the request, and it successfully deleted Sam’s video. I confirmed it by sending another request to the GET /identity/api/v2/user/dashboard endpoint while authenticated as Sam — the video_url and video_name fields were now gone.
This confirmed that non-admin users could delete other users’ content by modifying the endpoint path and HTTP method — a serious flaw in how function-level permissions are enforced.
Challenge 8: Getting an Item for Free (Mass Assignment)
In this challenge, I focused solely on Edward’s user account to explore a Mass Assignment vulnerability.
After logging into crAPI as Edward, I used the Shop section to place an order for a product. Once the order was placed, I grabbed Edward’s order ID by sending:
GET /workshop/api/shop/orders/all
This returned the order list specific to Edward. From that response, I identified the order ID — in my case, it was something like:
{
"order_id": 6,
...
}
Then, I tested whether I could manipulate this order post-purchase. I sent a PUT request to:
PUT /workshop/api/shop/orders/6
With a modified request body:
{
"product_id": 2,
"quantity": 5
}
Surprisingly, the server accepted the update — despite this order already being finalized via the UI. This confirmed that the API didn’t restrict what properties a user could change after purchase.
To finish the flow, I submitted a return request:
GET /workshop/api/shop/orders/return_order?order_id=6
The response returned a QR code indicating a return for five items instead of one, which could be used to get a refund for more than I had originally purchased. This was a clear example of a mass assignment vulnerability, where the API allowed unrestricted manipulation of object properties without validation.
Challenge 9: Increasing Account Balance via Negative Value Product (Mass Assignment)
This challenge built directly on the previous one, continuing with Edward’s account.
I started by reviewing the products available in the store via:
GET /workshop/api/shop/products
This showed me the current list of store products. Then I tested whether the API would accept new product creation by submitting a POST request to the same endpoint with the following body:
{
"name": "Refund Exploit",
"price": "-1000.0"
}
Surprisingly, the API allowed this, and the product appeared in the product list with a negative price. This suggested that the backend lacked proper validation on submitted product values.
Next, I placed an order for the newly created item using Edward’s credentials:
The order processed successfully, and Edward’s account balance increased by $2000 as a result.
This challenge again showcased a mass assignment vulnerability — the API trusted user input when it shouldn’t have, and lacked validation checks for values like price. It also exposed a business logic flaw, where the API failed to restrict actions that could result in profit rather than loss.
Challenge 10: Update Internal Video Properties (Mass Assignment)
In this challenge, the goal was to find and modify an internal property of a video that should not be user-editable, specifically targeting the conversion_params value.
First, I made sure Edward had a profile video uploaded by using the dashboard endpoint:
GET /identity/api/v2/user/dashboard
This returned key information including the video_id, video_name, and a field called conversion_params, which was set to:
-v codec h264
To test whether this parameter could be modified, I used the following endpoint:
PUT /identity/api/v2/user/videos/{{video_id}}
I crafted a request body that attempted to modify the conversion_params field:
{
"conversion_params": "-v codec h265"
}
After sending the request with Edward’s JWT (jwt_ed), I got a successful 200 OK response. This confirmed that the conversion_params value was changed from h264 to h265.
This shouldn’t have been allowed — the system exposed a low-level video processing parameter to the user, and allowed it to be edited without restriction. This confirmed a Mass Assignment vulnerability, where users can update object properties that should only be managed internally.
I did not go further to test command injection or chain it with SSRF. My focus was limited to confirming that conversion_params could be modified and that the application didn’t perform any validation or restriction on that internal field.
Conclusion
This project gave me a practical, in-depth look into API security testing by working directly with OWASP’s intentionally vulnerable API platform, crAPI. What began with a simple setup quickly evolved into a hands-on learning experience uncovering real, high-risk vulnerabilities that mirror the kinds of flaws found in modern production APIs.
Throughout this project, I was able to:
Each of these steps helped me connect theoretical API security concepts with their real-world impact. It also taught me how to test authorization flows, manipulate request payloads, work with JWTs, and identify improper access control mechanisms using tools like Postman and Burp Suite.
Why This Matters
APIs are the backbone of most modern applications. They connect services, power user-facing features, and are often exposed directly to the internet. As a result, they’ve become a primary target for attackers. If APIs aren’t thoroughly tested and secured, the smallest oversight can result in major breaches — from unauthorized data exposure to complete system compromise.
By working through crAPI myself and focusing on exploitation techniques used in real-world testing scenarios, I’ve gained a stronger foundation in how attackers think and operate. More importantly, I now know what to look for — and how to report or remediate it — when dealing with live APIs in real environments.
#APIsecurity #OWASPTop10 #crAPI #BugBounty #PenetrationTesting #Cybersecurity #WebAppSecurity #JWT #MassAssignment #BOLA #BurpSuite #Postman #Docker #EthicalHacking #RedTeam #CTF #InfoSec #APIhacking #SecurityTesting #DevSecOps
Internet marketing analyst at AI CERTS | Digital marketing | PGDM |
3wIt's inspiring to see your journey through API security, Jose. I thought you might be interested in AI Ethical hacking related events. Here's one for you! Join AI CERTs for a free webinar on "The Future of Ethical Hacking: AI as an Ally in Cyber Defense" on April 30, 2025. You can register at: https://bit.ly/m-ai-ethicalhacking. Participants will receive a certification, so consider sharing this with anyone interested!