Post

API testing module - Web Security Academy - PortSwigger

API testing module - Web Security Academy - PortSwigger

General Information

APIs (Application Programming Interfaces) enable software systems and applications to communicate and share data. API testing is important as vulnerabilities in APIs may undermine core aspects of a website’s confidentiality, integrity, and availability. All dynamic websites are composed of APIs, so classic web vulnerabilities like SQL injection could be classed as API testing. In this topic, we’ll teach you how to test APIs that aren’t fully used by the website front-end, with a focus on RESTful and JSON APIs. We’ll also teach you how to test for server-side parameter pollution vulnerabilities that may impact internal APIs.

API recon

Recon, which stands for reconnaissance, is a step to find out as much information about the API in website as possible.

From above information, hackers can discover multiple ways to exploit the website.

At first, comes to the definition of API endpoints: This is the location where API receives requests about a specific resource on its server.

For example, look at the following GET request:

1
GET /api/books HTTP/1.1

The API endpoint in this case is /api/books. Maybe this endpoint results in a request for getting information of books from a library (for example).

Another endpoint: /api/books/detective: Request for interaction with detective books from a library.

After successfully identify the API endpoints, hackers need to determine how to interact with these endpoints. Maybe hackers can explore via valid HTTP request to the API.

For example:

  • Find out the input data that the API processed.

  • The types of input that the API accepts, including supported HTTP methods and media formats.

  • Rate limits and authentication mechanisms.

API documetation

Documentation can be in both human-readable and machine-readable forms.

Human-readable documentation is designed for developers to understand how to use the API. It may include detailed explanations, examples, and usage scenarios. This document is often available for everyone to access, so hackers also can use it to know about how the API works.

Machine-readable documentation is designed to be processed by software for automating tasks like API integration and validation. It’s written in structured formats like JSON or XML.

Note

  • Use Burp Suite Scanner to crawl the APIs.

  • After have identified an API endpoint for a particular resource, remember to investigate through the path of that API endpoint. For example: /api/swagger/v1/users/123 → also explore these paths: /api/swagger/v1/users/api/swagger/v1/api/swagger/api.

Lab 1: Exploiting an API endpoint using documentation

image

Our mission is find the exposed API documentation and delete carlos.

I will use Burp Suite to control the request and find out the exposed API refered to user data.

image

After login, there’s a form to update email. Let’s try submitting a temporary email.

image

There’s an API endpoint: /api/user/wiener.

It seems that I can change the endpoint into /api/user/wiener and change the HTTP method into DELETE (try this method at first, if it is not supported in this API, I will find another way).

image

The returned status is User deleted, it seems that I have successfully deleted the user named carlos.

Let’s reload the website to check.

image

Identifying API endpoints

Some notes on how to identify API endpoints effectively:

  • Users can use Burp Scanner to crawl information about API, then manually investigate how to attack via that API endpoints.

  • Also remember to look at JavaScript files or something as the source code of the website. These files can contain information about API used in the website.

Interacting with API endpoints

After identifying API endpoints, use Burp Repeater or Burp Intruder to explore how API responds to request, in order to change HTTP method, add some fields, …

Identify supported HTTP methods

An API endpoint may support different HTTP methods. It’s therefore important to test all potential methods when investigating API endpoints. This may enable users to identify additional endpoint functionality, opening up more attack surface.

Users can use built-in HTTP verbs in Burp Suite to explore which HTTP methods are supported with each API endpoint.

Identify supported content types

API endpoints often expect data in a specific format. They may therefore behave differently depending on the content type of the data provided in a request.

Changing the content type may enable us to:

  • Triggering hidden errors to access important information.

  • Bypass flawed security.

  • Take advantage of differences in processing logic. For example, an API is secured with JSON format, but is insecured with XML.

To change the content type, modify the Content-Type header, then reformat the request body accordingly.

Lab 2: Finding and exploiting an unused API endpoint

image

My mission in this Lab is to buy a product named Lightweight "l33t" Leather Jacket without any payment.

image

I have found an API endpoint: /api/products/1/price.

I guess that 1 is the product’s id number.

Let’s send this request to Repeater to view how this API responds.

image

There’s a field called price, its value is the price of this product.

I will try sending a POST request (to change information), including a field price with the value of 0.

image

Based on the respond, POST method is not allowed on this API endpoint, I can only use GET and PATCH.

Besides that, I have to add a header named Content-Type and set the value to application/json like in the respond. This is to satisfy the condition about supported types.

image

image

Now the price of this product is turn into 0. Let’s add to cart and place order to finish this Lab.

image

Finding hidden parameters

Sometimes, there are some parameters which supported by API but are not documented. In this case, there’re some tools in Burp to find hidden parameters like that.

Burp Intruder enables users to automatically discover hidden parameters, using a wordlist of common parameter names to replace existing parameters (but only available with Burp Suite Pro, can find the same version on Github) or add new parameters.

Besides, make sure that users also include names that are relevant to the application based on initial recon.

Mass assignment vulnerabilities

Mass assignment (also known as auto-binding) can inadvertently create hidden parameters. It occurs when software frameworks automatically bind request parameters to fields on an internal object. Mass assignment may therefore result in the application supporting parameters that were never intended to be processed by the developer.

Identifying hidden parameters

Users can identify hidden parameters base on the object returned from the server.

Let’s look at this example:

1
2
3
4
5
PATCH /api/users/
{
    "username": "wiener",
    "email": "wiener@example.com",
}

A concurrent GET /api/users/123 request returns the following JSON:

1
2
3
4
5
6
{
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "isAdmin": "false"
}

This may indicate that the hidden id and isAdmin parameters are bound to the internal user object, alongside the updated username and email parameters.

Testing mass assignment vulnerabilities

To test if user can modify the hidden id and isAdmin parameters, add it to the PATCH request. For example:

1
2
3
4
5
{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": false,
}

In addition, send the request with an invalid value of the isAdmin parameter, like this:

1
2
3
4
5
{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": foo,
}

If the server respond differently when receive the invalid value, it means that the invalid value affects the logic of the program, but the valid one doesn’t → users can change the value of the parameter.

Try sending this request:

1
2
3
4
5
{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": true,
}

If the isAdmin value is bound to the user object without any strictly checking, the user will be incorrectly escalated to the admin privileges.

LAB 3: Exploiting a mass assignment vulnerability

image

After testing all functions of the website, there’s 2 API endpoints which seems the key of this problem.

image

image

The API endpoint named /api/checkout, supports both GET and POST HTTP method → maybe this is the API for checking information about the order.

image

Try sending the GET request, I have received a notable object returned: chosen_discount. It seems that if I want to solve this LAB, I have to modify the value of the field called percentage to 100, which means I have a 100 percent discount for the product.

image

Insert the object called chosen_discount to the POST request, then send it, and the LAB is solved.

alt text

Server-side parameter pollution

Systems often contain internal APIs that aren’t directly accessible via internet. Server-side parameter pollution occurs when users try to inject input in the request to internal API.

This can lead to:

  • Override existing parameters.

  • Modify the application behavior.

  • Access unauthorized data.

User can inject input in query parameters, for example, from fields, headers, URL path, …

Testing for server-side parameter pollution in the query string

User can inject syntax characters like #, &, = in the input and observe how the application responds to the request.

Consider a vulnerable application the enables users to search for other users based on their username.

This is the request:

1
GET /userSearch?name=peter&back=/home

Then, the server make this request to internal API:

1
GET /users/search?name=peter&publicProfile=true

Truncating query strings

We can use a URL-encoded # character to attempt to truncate the server-side request.

For example, we could modify the URL like:

1
GET /userSearch?name=peter%23foo&back=/home

The front-end will try to access the following URL:

1
GET /users/search?name=peter#foo&publicProfile=true

Note: It’s essential that user URL-encode the character input in URL. Otherwise, application will interpret it as a fragment identifier and will not pass it to the internal API.

For example, if user modify the URL like:

1
[URL]#section2

In this case, application will direct user to the element with the id named section2.

Back to the main topic, review the response for clues about whether the query has been truncated. For example, if the response returns the user peter, the server-side query may have been truncated. If an Invalid name error message is returned, the application may have treated foo as part of the username. This suggests that the server-side request may not have been truncated.

If you’re able to truncate the server-side request, this removes the requirement for the publicProfile field to be set to true.

Then, user may be able to exploit this to return private user profiles.

Injecting invalid parameters

Use an URL-encoded & character to attempt to add a second parameter to the server-side request.

For example, modify the request like this:

1
GET /userSearch?name=peter%26foo=xyz&back=/home

The front-end will try to access this following URL:

1
GET /userSearch?name=peter&foo=xyz&publicProfile=true

Review the response for clues about how the additional parameter is parsed. For example, if the response is unchanged this may indicate that the parameter was successfully injected but ignored by the application.

Injecting valid parameters

Based on the knowledge on Finding hidden parameters section, we can know which parameters are able to be injected to the query string.

For example, modify the request like this:

1
GET /userSearch?name=peter%26email=foo&back=/home

This results in the following server-side request to the internal API:

1
GET /users/search?name=peter&email=foo&publicProfile=true

Review the response for clues about how the additional parameter is parsed.

Overriding existing parameters

To confirm whether the application is vulnerable to server-side parameter pollution, we could try to override the original parameter. Do this by injecting a second parameter with the same name.

For example, modify the request like this:

1
GET /userSearch?name=peter%26name=carlos&back=/home

This results in the following server-side request to the internal API:

1
GET /users/search?name=peter&name=carlos&publicProfile=true

The impact of this depends on how the application processes the second parameter.

This varies across different web technologies. For example:

  • PHP parses the last parameter only. This would result in a user search for carlos.

  • ASP.NET combines both parameters. This would result in a user search for peter,carlos, 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 peter, giving an unchanged result.

If we’re able to overriding existing parameters, we may be able to conduct an exploit like: add a parameter like name=administrator to the request → enable to log in as administrator.

LAB 4: Exploiting server-side parameter pollution in a query string

image

This LAB requires me to log in as the administrator and delete the user named carlos.

image

It’s surprised that when I log in as the default username and password: {wiener, peter}, the application returns an error message.

Let’s try using the function Forgot password.

image

Submitting with the username administrator.

image

There’s a POST request appearing in Burp. Let’s add it to Repeater.

image

This is a normal response. Let’s apply some knowledge that I have studied in the previous section.

image

Try truncating the URL with the URL-encoded # character. In the response, there’s an error message: “Field not specified”.

Let’s add another parameter called “field” to the URL.

image

Invalid value for “field” parameter. Let’s use Intruder to brute-force the value for “field”.

(I don’t have Burp Pro, so I can’t use the default payload provided by Burp. Instead, I have to find another one on Github)

After brute-force, I have found 2 payloads: email and username, with the status code 200 returned.

image

This is a normal response. But I have to find more hints.

image

In the browser’s network tab, I have found a file called forgotPassword.js. It seems that this file is about the operation behind the function Forgot password.

I have noticed that the main function in this script is forgotPwdReady. This function checks if the URL contains a parameter called reset_token and its value.

But now, how can I find out the value for reset_token? Try inserting reset_token on field. Notice that in the previous step, when I type email as the value of field, it returns the email of the username. So, as logical, application will returns the reset_token value when I insert it as the value of field.

image

That’s right. The reset_token is 13520f6h8xy4qljek2wwks4djlna4fsf.

Now, insert this parameter and value to the URL with the syntax provided in the script: /forgot-password?reset_token=${resetToken}

image

Then log in with the password and delete the user carlos. The LAB is solved.

This post is licensed under CC BY 4.0 by the author.

Trending Tags