Help - API Security

About

Most modern web apps use some kind of API, either as Single Page Apps (SPAs) or to retrieve data to populate traditional apps. As these APIs are behind the scenes, developers sometimes feel they can cut corners in areas such as authentication, authorisation or data validation. As testers, we can get behind the curtains and directly access these seemingly hidden calls to take advantage of these weaknesses.

This module will look at three weaknesses, versioning, mass assignment, and .....




Objective

Each level has its own objective but the general idea is to exploit weak API implementations.




Low Level

The call being made by the JavaScript is for version 2 of the endpoint, could there be other, earlier, versions available?

Either by looking at the JavaScript or watching network traffic, you should notice that there is a call being made to /vulnerabilities/api/v2/user/ to retrieve the data used to generate the user table.

As the call is being made against version two (v2) of the endpoint, the obvious thing to try is to see if version one is available, and what it offers. The easiest way to do this is to access it directly in the browser by visiting /vulnerabilities/api/v1/user/, but sometimes API calls require extra headers or authentication tokens which it is easier to let the site add rather than trying to do it manually. Two ways to do this are to modify the URL used in the JavaScript as the page loads by setting a breakpoint on it and changing it before the request is made, or to intercept the call in a proxy, such as BurpSuite.

Whatever approach you try, by accessing version one of the endpoint, you should be able to see the password hashes as part of the data.

Medium Level

Look at the call made by the site, but also look at the swagger docs and see if there are any other parameters you might be able to add that are not currently passed.

When you update your name, a PUT request is made to /vulnerabilities/api/v2/user/2 with the following content:

{
  "name":"morph"
}

If you look at the swagger docs, the definition for UserUpdate is:

UserUpdate:
  required:
    - name
  properties:
    name:
      type: string
      example: fred
    type: object

Which is what you are currently passing, but if you have a look at UserAdd you will see an extra parameter:

UserAdd:
  required:
    - level
    - name
  properties:
    name:
      type: string
      example: fred
    level:
      type: integer
      example: user
  type: object

Notice the extra level parameter?

In situations like this, it is always worth testing to see if extra parameters which exist on similar calls will also work on the one you are working on.

To try this, you can either intercept the request in a proxy, or you can modify the JSON before the request is sent to the server. To modify it in the page, you can set a breakpoint in the update_name function, right after the data variable has been created, and modify the variable by using the following in the console:

data = JSON.stringify({name: name, level: 0})

If you do this and then check the JSON sent in the PUT request, you should see:

{
  name: "hacked",
  level: 0
}

And hopefully a congratulations message.

High Level

Import the four health calls into your testing tool of choice and make sure they are running properly. When they are all working, test them for vulnerabilities.

The connectivity call takes a target parameter and pings it to check for a connection, this is done by calling the OS ping command and is vulnerable to command injection.

For more information on how to exploit this type of issue, see the command injection module.

Impossible Level

The challenge here is just to get the login process automated in Postman or your tool of choice. Read the documentation and experiment. To help get things working I piped everything through Burp and watched each call as it was made to see if it matched what I expected.

When the flow works correctly, the initial login will return an access token and a refresh token along with an expires_in value to say how long the access token is valid for. Once the access token has expired, the refresh token will be sent to the refresh endpoint to generate a new access/refresh token pair.

It should be noted that as well as the access token having a fixed lifespan, the refresh token also has a fixed lifespan, once it has expired, the login process has to begin again from scratch.