Top 10 OWASP API-Security (Walkthrough on vAPI)
Recently, I have been studying and enhancing my knowledge of API security, and throughout this journey, I have come across OWASP’s recommendations. These recommendations have led me to want to understand practically how the main vulnerabilities found in APIs can be exploited. To this end, some applications intentionally simulate these gaps, exposing in practice what these risks can lead to by not implementing basic controls and not following good security practices.
In this article, we will document the entire exploration journey of 10 exercises that demonstrate the main vulnerabilities reported by OWASP. To achieve this, we will use a testing API called vAPI.
vAPI is Vulnerable Adversely Programmed Interface which is Self-Hostable API that mimics OWASP API Top 10 scenarios in the means of Exercises.
Link to the official page: vAPI - http://vapi.apisec.ai/
For educational purposes, no flags will be disclosed through the walkthrough. The goal is to guide and show each step, encouraging you to follow all the steps and discover through practice.
I recommend that before each exercise, you consult the item directly on the OWASP page to understand the entire scenario and the risk of the exercise to be studied in that specific context.
Link to OWASP API Security: OWASP API Security - https://owasp.org/API-Security/
Let’s get started on the walkthrough!
API1:2019 - Broken Object Level Authorization
You can register yourself as a User , Thats it ….or is there something more?
In the first exercise, following the hint provided in the documentation, we will create a new user by following the instructions described in the API’s documentation itself.
Navigate to the API1 > Create User item, and copy the payload presented on the right side to use as a template:
Using Postman, let’s create a new user following the example copied:
The response must be presented as shown in the following image:
We can now verify the information of the new user. To do so, we will only need the user’s id and an Authorization-Token, which can be generated using the following command:
echo -n "rod:P@ssw0rd" | base64
Copy and paste the output:
Update the user’s id to be queried in the first tab:
The response should be as per the following output:
Now, what can happen if we use the same token but any other id:
It looks like we successfully got our first flag ;)
We can go further and try to abuse this flow by taking advantage of the user update privilege. First, let’s check the user data id 2:
We’ll try updating the course and password for the account with id 2 using the token generated from id 9. We’ll accomplish this using the PUT method. Remember to adjust the parameter to id 2, include the Authorization-Token, and provide the necessary body:
The expected response is an HTTP 200 in this case with the following content in its body:
Let’s check if the data was updated successfully?
Yup, the data was updated successfully, it seems that the API and its controls do not correctly validate permissions when returning data, allowing data query and update permission from any user registered in the database.
API2:2019 - Broken User Authentication
We don’t seem to have credentials for this, How do we login? (There’s something in the Resources Folder given to you)
In this exercise we will need to use a resource previously and intentionally left in the Resources/API2_CredentialStuffing directory called creds.csv We will need to put these pieces together for a brute-force attack:
To confirm our theory, let’s set up the proxy on Postman to intercept all requests using Burp.
On Postman, go to Settings > Proxy and change your settings as shown in the image below:
Now we can view all requests made in Postman through Burp, as well as responses:
On the first try we got an unauthorized response, let’s now put the pieces together and test each email and password from the creds.csv file. Click on the request in Burp and send it to Intruder. Adjust the Attack type to Pitchfork since we will be using multiple payloads and mark the payloads in the body as shown in the image below:
Open the file and copy only the first values before the comma, which in this case are the email addresses. Click on Payloads and paste all emails.
Do the same with the values after the comma, however, this time we will configure it as payload 2 as passwords, copy and paste:
Don’t forget to uncheck the Payload encoding option for both payloads; we won’t be using it in this case. Next, click on Start attack.
To make it easier to view, sort by Status code, and the first results with http status code 200 are users and passwords that were successfully validated. Let’s run a test to validate the results. Go back to Postman and now, using the Get Details endpoint, fill in using the discovered information.
It looks like we have one more flag as per the JSON response.
API3:2019 - Excessive Data Exposure
We have all been there , right? Giving away too much data and the Dev showing it. Try the Android App in the Resources folder
On the next exercise, we will need to use a resource located in the directory Resources/API3_APK called TheCommentApp.apk. However, this time, we will also need additional resources and a mobile device.
Don’t worry, I have prepared an article on how to install and configure these resources using an emulator device.
How to route your traffic through (Burp Suite) from a mobile emulator (Android-x86)
After having the environment ready, you only need to install the APK and run it for the first time, during the first execution you will be asked for the configuration URL which in our example can be used as follows:
After clicking Save, the login screen will be presented as follows:
As we don’t have any accounts yet, let’s click on Create Account:
Click Register and now is the time to test the new account created:
Authentication completed successfully:
Let’s go back to Burp to check the requests. Right after the login, we notice that the response for the next request has more information than we need. Although the app shows just one comment, the Proxy reveals that many other details were returned.
This includes our third flag!
API4:2019 - Lack of Resources & Rate Limiting
We believe OTPs are a great way of authenticating users and secure too if implemented correctly!
For the next exercise we have 3 endpoints available, the first one only requires a mobile number as follows:
As observed in the response, a token containing 4 digits is sent to the mobile number. However, since we don’t have the sent code, we can use the next endpoint to check and try to guess the code, as the resource does not prevent or restrict the number of attempts.
This time, we will use wfuzz to try to discover the code. First, we need to generate a list of possible combinations using crunch to create a list of OTPs from 0000 to 9999.
crunch 4 4 0123456789 >> otps.txt
Then, we have to perform a test for each of the values generated on the endpoint using a brute-force attack:
wfuzz -d '{"otp":"FUZZ"}' -H 'Content-Type: application/json' \
-z file,otps.txt -u http://127.0.0.1/vapi/api4/otp/verify --hc 403
Once the correct value is found, we can abort the execution:
Now we can test the value using Postman to obtain our Authorization-Token:
Move on to the third and final endpoint to validate our Authorization-Token:
The key was successfully validated, resulting in another flag!
API5:2019 - Broken Function Level Authorization
You can register yourself as a User. Thats it or is there something more? (I heard admin logins often but uses different route)
In this exercise, through the tip above it is possible to understand that there are other undocumented routes or endpoints, for this we will need to use the brute-force technique.
In our example, we will use a tool created by a friend of mine that can be found at the link:
Turbo Search - https://github.com/helviojunior/turbosearch
The tool is well-documented and can be used in our case using the command and parameters below:
./turbosearch.py -w endpoints.txt -t http://127.0.0.1/vapi/api5/ \
--ignore-result 403
The file endpoints.txt contains possible directories or paths. We will test each of them to discover if they are validated or not. I recommend creating and maintaining your own custom dictionary for each objective. Several examples can be found on the internet.
It looks like there is a users path, let’s investigate through Postman:
It seems we will need access credentials. Let’s create a user using the path and payload recommended in the documentation.
Let’s test using the same previous technique to generate the Authorization-Token, basically combining the username and password in base64:
echo -n "testuser2:test123" | base64
Don’t forget to set the api5_id parameter followed by the id of the new user:
What would happen if we use the same token that we used before against the undocumented path users?
We got it, another flag was successfully captured!
API6:2019 - Mass Assignment
Welcome to our store , We will give you credits if you behave nicely. Our credit management is super secure
In the next exercise we can see that we will somehow play with credits, first, we will create a new account following the documentation:
There is nothing new here. Let’s proceed and use the GET method to validate our new user. But first, don’t forget to create the Authorization-Token in the same way we did in previous exercises.
echo -n "rod:P@ssw0rd" | base64
The answer should be as shown in the image below:
A new attribute called credit was not initially included but was later assigned in the backend. What might happen if we add this new attribute along with a credit to the payload when creating an account? Even though it has not been specified in the documentation and is not mandatory, it is worth testing.
From the response we can assume that the payload was accepted, let’s validate the user, and don’t forget to generate the token according to the new credentials:
Okay, another flag was captured, along with good credit to our account!
API7:2019 - Security Misconfiguration
Hey , its an API right? so we ARE expecting Cross Origin Requests . We just hope it works fine.
For the next exercise we will need to interact with an attribute in the request called Origin, but first, let’s create a new user:
Next, we will test the new account, don’t forget to generate the Authorization-Token according to the previous exercises in base64:
Adjust the proxy in Postman’s settings, capture the request and send it to Repeater, finally just add a new attribute as shown in the image below Origin: Any Domain:
Done! We have successfully captured another flag!
API8:2019 - Injection
I think you won’t get credentials for this. You can try to login though.
In the next exercise, we will follow the instructions given in the documentation and attempt to log in:
As expected, since we don’t have any users, we got an access denied message. As this is an injection exercise, we will test some hypotheses and check the API’s return using different payloads.
We can send a single quote to force an error that leads us to signs of use of a database:
In this case, when using a single quote in the login, we received an error that guided us in the right direction. Now, let’s adjust the payload to make the application assume that the login was legitim.
After getting the authkey we can now proceed to the next endpoint:
Another flag was successfully captured!
API9:2019 - Improper Assets Management
Hey Good News!!!!! We just launched our v2 API :)
In this next exercise, we can see that it is a login path and after running some preliminary tests nothing was returned despite the status 200:
In the response we can also see that a rate limit was implemented, which will limit us to a brute-force attack:
We can see that after 5 consecutive attempts, we started receiving a 500 error:
We can try different approaches. The first would be to try to measure the duration of the blocks and check if it is feasible to perform 5 attempts and wait for the interval before proceeding. However, this still limits our testing capacity and can take a long time.
Alternatively, we can search and try to find out if the previous version of this API is still available. Since the current path shows v2, let’s try v1 and see if it’s still accessible:
In this case, all we had to do was change our path from v2 to v1. We noticed that in the previous version, the rate limit method and control had not been implemented.
We will use the same file used in exercise 4, otps.txt with an attack carried out through wfuzz:
wfuzz -d '{"username":"richardbranson","pin":"FUZZ"}' \
-H 'Content-Type: application/json' -z file,otps.txt \
-u http://127.0.0.1/vapi/api9/v1/user/login --hw 0
Once the correct value is found, we can abort the execution:
Now that we have the pin, we can confirm it using the second available endpoint:
We can test the correct pin on API v1 or v2, and the result will be the same. Another flag was captured!
API10:2019 - Insufficient Logging & Monitoring
Nothing has been logged or monitored , You caught us :( !
In this next and final exercise, nothing is being monitored and the application does not record any activity. This way, the attack does not require anything complex and is just for demonstration:
Finally, the last flag was successfully captured!
If you have followed all the steps and have made it this far, congratulations on completing all the exercises. If any of them were unclear, feel free to leave a comment or redo the exercise while carefully reading about the type of attack on the official OWASP page and try your best to understand the examples.
Last but not least, remember to practice and keep up the good work!