Skip to main content
  1. Posts/

API Testing - Applied Review

·8 mins
web BSCP
Table of Contents

What is an API?
#

Application Programming Interfaces (APIs) allow for different software systems and applications to share data. An example of this might be a weather application, the application logic doesn’t have the inherent ability to determine the weather but it rather makes calls to some weather forecast API that is responsible for returning accurate data based on the request.

All dynamic websites are composed of APIs, so you could classify things like SQLi as part of an API test. This post is going to focus on testing RESTful and JSON APIs, in addition to going over server-side parameter pollution vulnerabilities that could impact APIs.

API Reconnaissance
#

When starting an API test, it will be easier if you have more information about the API itself and the context in which it is meant to be used. Sometimes you’ll have access to this information and other times you’ll need to infer it based on the environment you are testing in.

As always, it is wise to map out as much of the attack surface as possible. We can start this process by looking for endpoints - this would be some location where the API is designed to receive requests and respond accordingly.

For example, imagine an API for movie theatres that allows you to see where a movie is playing. A request to the /api/currentMovies endpoint might return a list of showings that are available for viewing:

GET /api/currentMovies HTTP/1.1
Host: vulnerable.com

This would return which movies are currently being shown in the theatre. You might have more endpoints that refine the query, like using /api/currentMovies/horror to retrieve horror movies being shown in theatres.

Once you’ve found the endpoints being used, you’ll want to have some way to interact with them. This means you’ll need to be able to construct valid HTTP requests that the API can understand.

You’ll need to find out:

  • The input data the API processes, both mandatory and optional
  • The types of requests the API accepts, like supported methods and media formats
  • Rate limit protections and authentication mechanisms

For white-box type testing, make sure to communicate with your client before testing to make sure you have everything you need to test before the testing window begins. If you don’t have test data, documentation, or additional details, you’ll spend a good amount of time figuring it out.

APIs aren’t really something that I would say are easy to grey-box or black-box test because a lot of their behavior is obscured.

If you’re in the market for an API test, it might become tempting to provide little information and get a more red-teamy kind of test, but this will certainly be limited by a time constraint that an auditor or consultant would be subject to, but an adversary would not have to worry about.

So, if you’re the tester or project manager getting ready for an API test, make sure your communication and expectations are clear when communicating with clients, and be prepared to demand more information than what might be given for a more conventional web application test.

API Documentation
#

Documentation for APIs are usually written for developers so that they can effectively integrate and use the APIs in their applications. Some of the documentation will be human readable and other documentation might require some special environment for debugging to read through the data.

This documentation is often publicly available, depending on the use case for the API. Always start your recon by reading the documentation if possible, as it will prevent you from backtracking later on.

Enumerating API Endpoints
#

You might still be able to access the documentation if the API being used contains some documentation within itself. You might see endpoints like /api, /swagger/index.html, or /openapi.json that contain documentation references or information.

You might also want to work backwards if you see something like /api/swagger/v1/users/gabe/posts, you might want to investigate the endpoints that precede it.

You can do this with the Burp scanner, or public tools like Ffuf. Having access to documentation of any kind really changes the game if you are starting from scratch.

To actually find these you might also be able to just proxy your traffic through Burp and observer the API calls as they are happening.

Once you’ve found the API endpoints, you’ll need to determine the HTTP methods that they support and the data they want to accept.

For example, an online shop using an API to get the current price for an item might use the following GET request:

GET /api/products/6/price HTTP/2

This would return the price of the item and some comment about it:

HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN

{"price":"$0.07","message":"very cool item description"}

You might be able to try different methods to get a different response that reveals more information about the API:

PATCH /api/products/6/price HTTP/2

This would return the following error:

HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 93


{"type":"ClientError","code":400,"error":"Only 'application/json' Content-Type is supported"}

This tells you that you need to include the Content-Type header and when you include it with not data, it replies with the following:

HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 77


{"type":"ClientError","code":400,"error":"'price' parameter missing in body"}

Now you might want to add some JSON data that fits the error message and see the reply:

HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 80


{"price":"$0.00","message":"This item is in high demand - 9 purchased recently"}

In this little example, we were able to change the price of an item by just messing with the HTTP methods and JSON data in our requests without needing any authentication.

Mass Assignment Vulnerabilities
#

Sometimes, like what we did before, you can use valid data instead of errors to construct your API calls. Mass assignment might come in if some of these parameters are hidden in one place but revealed in another.

Think of if an application used the isAdmin parameter when making an account to determine if the user had admin permissions, but the application does not show this when creating a user but only when viewing them.

You might be able to exploit this mass assignment by just including that isAdmin parameter in your request to make an account.

Server-Side Parameter Pollution
#

Some APIs will be internal and won’t be accessible from the public internet. Server-side parameter pollution is when a website embeds user input to a server-side request to an internal API without sanitizing the data properly.

This might allow you to:

  • Override existing parameters
  • Modify the application behavior
  • Access data without proper authorization

You can test any user input for any kind of parameter pollution. For example, query parameters, form fields, headers, and URL path parameters may all be vulnerable.

Pollution in Query Strings
#

You can look for this kind of pollution by placing characters like #, &, and = in your inputs and see if the application behaves strangely.

Imagine a social media application that allows you to search for users and view their information if their profile is public. You might search for a user like this:

GET /userSearch=?name=josh&back=home

The API being run on the web application’s network might send this request to verify search for the user and respond if their profile is public:

GET /users/search?name=josh&publicProfile=true

Truncating Query Strings
#

You can use the # character to attempt to truncate the server-side request. This could cause the client-side and server-side requests to look like this:

GET /userSearch=?name=josh%23gaming&back=home
GET /users/search?name=josh#gaming&publicProfile=true

URL encoding the # character is essential because of how the front-end application and internal API will be transferring/handling the data.

If the truncation worked, it would return the user’s information regardless of whether or not their profile information was public.

Injecting Parameters
#

You could use the & character to add more parameters to the request. You would still need to URL encode this character like before. If you utilize the error information to figure out the possible parameters, you might be able to change something about the target user like their screen name or account email address.

GET /userSearch?name=josh%26email=hacker@evil.com&back=/home
GET /users/search?name=josh&email=hacker@evil.com&publicProfile=true

This is pretty dependent on the purpose of the API and what kinds of operations are being shared by a single endpoint.

Overriding Existing Parameters
#

Including duplicates of a certain parameter might cause the application to behave strangely. Take the following API call as an example:

GET /users/search?name=josh&name=gabe&publicProfile=true

Depending on the framework or web technology being used, it might handle this kind of request a certain way:

  • PHP parses the last parameter only. This would result in a user search for gabe.
  • ASP.NET combines both parameters. This would result in a user search for josh,gabe, which might result in an Invalid username error message.
  • Node.js / express parses the first parameter only. This would result in a user search for josh, giving an unchanged result.

Overriding parameters could cause you to take control over certain accounts or modify other accounts in malicious ways.

Server-Side Parameter in REST Paths
#

A RESTful API may place parameter names and values in the URL path, rather than the query string. For example, consider the following path:

/api/users/1337

In this case, /api is the root API endpoint, /users is a resource, and /1337 is a specific user represented by their user ID.

You might be able to use this information to influence API requests being made - for example using a path traversal sequence.

If the intended request and API call are like the following:

GET /edit_profile.php?name=josh

GET /api/private/users/josh

You might be able to submit the follwoing path traversal requests to edit another account:

GET /edit_profile.php?name=josh%2f..%2fadmin

GET /api/private/users/josh/../admin

If the server-side client or back-end API normalize this path, it may be resolved to /api/private/users/admin.

Prevention
#

When designing an API, make sure that security is a concern from the start. Make sure that:

  • Documentation is only accessible internally and avoid using public API keys.
  • Keep documentation up to data so testers can be efficient when they are testing your environment.
  • Use an allow list of HTTP methods.
  • Use generic error messages to avoid giving away more information than needed.
  • Use protective measures on all API versions, not just the production environment.

Related

CSRF - Applied Review
·13 mins
web BSCP
What is CSRF? # Cross-site request forgery allows an attacker to perform any actions that a normal user is able to.
Clickjacking - Applied Review
·4 mins
BSCP web
Before we get into the content here I want to clarify that I wasn’t able to find any CTF-type examples of clickjacking, so if you know of one please let me know.
Cross-Origin Resource Sharing - Applied Review
·10 mins
BSCP web
What is CORS? # Cross-origin resource sharing (CORS) is a browser mechanism that allows for controlled access to resources located outside the original domain.