crAPI
Last updated
Last updated
crAPI (completely ridiculous API) is a modern API, built on top of a microservices architecture, and vulnerable by design, released by OWASP, which contains the 10-top most critical API security risks.
The crAPI application will be installed on an Ubuntu 22.04.2 LTS (Jammy Jellyfish) server (IP address will be 10.1.1.11). The ports exposed are 8005 (for the HTTP web site), 8006 (HTTPS site), and 8007 (Mail Hog email server). The red box will be a Kali Linux machine running on IP address 10.1.1.22.
In case you do not want to download, install and run crAPI locally, it is also hosted publicly and available to be attacked through the web: http://crapi.apisec.ai/login
The exercise has been solved using Burpsuite, Firefox, ffuf
and curl
https://github.com/OWASP/crAPI
https://owasp.org/API-Security/editions/2023/en/0x11-t10/
https://owasp.org/www-project-crapi/
https://www.cloudflare.com/learning/ddos/what-is-layer-7
https://www.radware.com/cyberpedia/application-security/http-flood
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection
https://www.postgresql.org/docs/current/index.html
The aim is to disclose sensitive information about another user’s vehicle, keeping in mind that vehicle IDs are not sequential numbers, but UUIDs. We need to find an endpoint that receives a vehicle UUID and returns information about it.
Sign up a new account with the following data:
Username: test
Email: test@test.com
Number: 123456789
Password: Password123!
After the signup process is finished, we can login with our recently created user. In the user dashboard click on "Add vehicle". The application asks for a VIN and a PIN code in order to add a car. These can be obtained in an email received in the MailHog server (port 8007) during the the signup process.
Insert the VIN 7MOAK60QPBG686751 and the pin code 7630 to add a new vehicle. A message "Vehicle successfully added" is reported and the user is redirected to the dashboard, where the new vehicle is presented.
Inspect the captured traffic with Burpsuite.
The endpoint /identity/api/v2/vehicle/vehicles
provides data about the added car. Notice the car UUID is ee389276-4040-4960-8603-11c1c0560027
, not sequential, and we cannot guess them initially. A BOLA vulnerability is related to the unauthorized access to another user's car using the car UUID; however,since we cannot guess this UUID, we have to find another endpoint leaking the car identifiers.
In the user dashboard click on "Community" and capture the traffic with Burpsuite. A user forum appears where the users of the application post their comments. The posts are requested by a GET request to endpoint /community/api/v2/community/posts/recent
In the HTTP response the endpoint provides data about all the users who have commented in the forum, including the car UUID assigned to each one.
"nickname":"Robot","email":"robot001@example.com","vehicleid":"797a2957-6bf1-43d8-abfc-d02309aff5b4"
"nickname":"Pogba","email":"pogba006@example.com","vehicleid":"f328043c-cf29-48f0-9ebb-4bac953b7ad9"
"nickname":"Adam","email":"adam007@example.com","vehicleid":"9351b721-aa14-40ae-b23b-3d52c358b9f2"
Next step is to find an endpoint where we can request car details using the car UUID. Enumerating the website we discover if we request a car's location by clicking on the "Refresh location" button, a GET request is sent to endpoint /identity/api/v2/vehicle/{{car id}}/location
. Refresh the location of our car, capture the traffic and send to repeater. Replacing our car UUID with any of the UUIDs previously discovered we can access the details of another user's car.
The application fails to verify user's authorization when requesting car details, and we can access anyone's car details just changing the car UUID.
In the user dashboard, click on "Contact mechanic" and fill in a service report. Inspect the traffic with Burpsuite.
We see a POST request to endpoint /workshop/api/mechanic/contact_mechanic
. The server assigns an id=10
to the case in the response. In the previous challenge we discovered the application is vulnerable to BOLA, so let's check if we can access other mechanic cases by just changing the report ID.
Prepare in Burpsuite repeater a GET request to /workshop/api/mechanic/mechanic_report?report_id=5
. Include your JWT in the header Authorization: Bearer <JWT>
, otherwise an error will be reported ("JWT token required!").
The application provides details about the mechanic report with id=5
in the response .
In conclusion, the application fails to verify the user authorization when requesting access at the object level, such as mechanic report IDs or vehicle UUIDs, and the BOLA vulnerability is confirmed.
When an user requests a password reset the application asks the user to enter an email address. First, let's try to reset our own password, after entering the email the application reports an OTP has been sent to our inbox. The mail inbox is available in the MAilHog server, where we notice the OTP is just a 4-digit code.
Next step is to try with one of the email addresses we have disclosed in the previous challenge, for example adam007@example.com
. We enter this email in the password reset box and the application notifies an OTP has been sent to the user's inbox. Enter any OTP code in the box and let process fail, then inspect the traffic with Burpsuite.
We see a POST request to endpoint /identity/api/auth/v3/check-otp
. Since the OTP is just 4-digit, we can try to bruteforce it. This can be done with either ffuf
or Burpsuite intruder.
Right-click on the request and send to intruder, mark the OTP field with section signs §
and load a wordlist. This wordlist must contain codes from 0000 to 9999, and can be generated with Bash command seq
. Then launch the attack and inspect the output.
Unfortunately, shortly after the process is started the server begins responding with status code 503 and error message "You've exceeded the maximum number of attempts". It seems a countermeasure is in place to avoid bruteforce attacks.
In principle, this prevents us from resetting the password of another user. However, if we inspect the endpoint, we see it is labeled /v3
. Let's fuzz for older API versions which hopefully does not implement bruteforcing protections.
Start querying with curl
for API v1.
So API v1 does not exist. Let's try API v2.
We receive an "Invalid OTP" error message, so the API v2 exists. Let's try bruteforcing this endpoint.
Follow the same procedure as before, just change the API version in the intruder payload.
Then load the same wordlist payload as before and launch the attack. Indeed, the bruteforce protection is not in place in API v2, and a status 200 is reported for OTP 4043.
Finally, reset the user adam007@example.com
password with OTP 4043 and verify you can login as this user with the new password Password123!
We have already found the endpoint leaking sensitive information in challenge 1. It is endpoint /community/api/v2/community/posts/recent
, which leaks email and vehicle UUID of any user who post comments in the forum.
Navigate to your profile dashboard and click on "Upload video". Add an .mp4
file and click on "Upload", capture the traffic with Burpsuite and inspect the request.
It seems the application leaks information about the video conversion process, along with the video name and the video ID.
Layer 7 attacks are attacks aimed at OSI layer 7 (application layer). You can find information about these kind of attacked here:
https://www.cloudflare.com/learning/ddos/what-is-layer-7
https://www.radware.com/cyberpedia/application-security/http-flood
In short, an application layer attack overwhelms network or server resources with a flood of traffic, typically HTTP traffic. For example, sending thousands of requests per second for a certain webpage until the server is overwhelmed and cannot respond to all of them, or calling an API over and over until the service crashes.
Create a new mechanic report and capture the traffic. It seems you can configure the request to be repeated if it fails as many times as you want. We will abuse this functionality by provoking a failed request (for example, entering an incorrect VIN), then intercept the request and configure it so it is repeated 100 times.
Send the request and let if fail. The server will hang and send a status code 500 until the requests are processed. In theory, you could choose for how long you want the server to be unavailable by adjusting the number of repeats.
Once a video is uploaded into our profile, we can replace it, rename it or share it with the community. Let's rename our video and capture the traffic.
It seems the video renaming is performed by means of a PUT request to the API endpoint /identity/api/v2/user/videos/{{video_id}}
. If the application was vulnerable to BFLA we could delete the video just changing the request method. Let's try it first with our own video, send the captured request to repeater and change the request method to DELETE.
The application returns an error indicating this method is reserved for admins, and suggests to try the admin API. We are not admin, but we can try to exploit the BFLA by guessing the admin endpoint, just replacing the word "user" with "admin".
In this case, we verify that if we try the endpoint /identity/api/v2/admin/videos/36
the video is successfully deleted.
So we have discovered the admin endpoint to delete videos and confirmed the application is vulnerable to BFLA. To delete another user's video, we should send a DELETE request to identity/api/v2/admin/videos/{{video_id}}
. Now the point is to find which is the ID of the videos uploaded by the other users. We will find out this with ffuf
Right click on the DELETE request and save as request.txt
. Open the file with a text editor and mark the video id field with the string "FUZZ".
Now generate a wordlist with 100 ID's from 1 to 100.
And fuzzing with ffuf
a video with id=31
is discovered and successfully deleted (status 200).
In summary, an application vulnerable to BFLA fails to verify user authorization when accessing elements at a function level. The attacker that exploits this vulnerability performs restricted actions just by changing the request method on a known endpoint, or by guessing the URL of restricted endpoints.
The crAPI application allows users to return items they have ordered by simply clicking the "Return order" button, then a QR code is displayed which the client has to show in a USPS store. The shop orders have 3 status: "delivered", "return pending", or "returned". When an order is placed, the price is discounted from the user credit wallet. If the user clicks on "Return order", the order moves to status "return pending". Only when the the order is verified returned, the shop admin sets status to "returned", and the user automatically receives refund in his credit wallet.
The goal is to move an order to status to "returned" without having previously requested a return, so we are automatically refunded while we keep the article.
We start with a $50 credit wallet and place an order for a $10 seat. If we inspect the traffic with Burpsuite, after the order is placed, it is assigned order id=12
and $10 credit is discounted from our wallet.
Then the server redirects to endpoint /workshop/api/shop/orders/all
where we see the id=12
is in status "delivered", meaning we have already received the article. Now the point is to move the article to status "returned" without having returned it (i.e. without having clicked on "Return order"), so we are automatically refunded while keeping the article and thus having effectively got it for free.
Let's try if we can access the order directly using the order ID in the endpoint.
In fact we can, and it seems we are also allowed to send PUT requests, so let's try to change the order to "status":"returned"
The server replies the change has been successfully applied. If we check our wallet we verify we have been refunded, and our wallet is again $50 credit as it was at the beginning.
Wrapping up, we have been able to change the order status so we are automatically refunded without having to return the article. Thus, we have got it for free.
You may ask how we did know the accepted status to receive the refund was "returned". This can be discovered just putting any status you want in the PUT request, the application will kindly indicate which statuses are allowed.
Navigate to the application shop and browse the items, capture the traffic with Burpsuite.
It seems we are allowed to POST new items in the shop. Let's add a new item with a negative price of -$1000.
Now just place an order for this new product, your wallet will be charged -$1000 (this is, you will receive $1000).
We had finished challenge 5 uploading a video with id=37
on endpoint /identity/api/v2/user/videos
. Since we know the app is vulnerable to BOLA, we will try to access the video just sending a GET request to this endpoint. Just adding the video ID we can access video properties individually.
Moreover, if we change the request method to OPTIONS, we see what actions are allowed on the endpoint.
It seems we are allowed to modify internal properties (PUT methods), such as the video name (as we did in the challenge 7) or the conversion parameters. Let's try to change the conversion parameters as conversion_params=h.265
Using the API we have changed internal video parameters in such a way not accessible from the user dashboard.
For this we just need to find an endpoint which makes calls to web sites. Enumerating the application we find the /workshop/api/merchant/contact mechanic
endpoint takes an argument mechanic_api
which makes requests to HTTP sites.
Just edit this argument, insert http://www.google.com
, click on "Send", and let SSRF do its magic.
Navigate to the shop and click on the "Add coupon" feature, capture the traffic and inspect the request, you see an injectable parameter called coupon_code
You can get a list of NoSQL injection payloads in the site https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection. For example, injecting the payload "$ne":null
will make the application grant us a a $75 coupon code named TRAC075
In the previous challenge we have managed to discover a $75 coupon code TRAC075
. We redeem the code by clicking on "Add coupon" and entering the code. Once the coupon is successfully applied, $75 credit is added to our wallet.
Now, if we try to redeem the same coupon code again we receive an error message.
Let's focus on the coupon_code
parameter. If we try several SQL injection payloads we are able to retrieve database information. For example, database version can be dumped with the following payload.
And the response we retrieve from the database.
So the database is PostgreSQL 14.10. We can also dump the database name.
And the current user.
We could continue manually dumping the database, but to speed up the process we will use sqlmap
to do the hard job. Right-click on the HTTP request and save it as request.txt
. The file looks like this.
Then launch sqlmap
passing the text request and the parameter to inject as arguments.
The tool returns 25 tables.
Looks like the table we are looking for is called applied_coupon
, let's dump its contents.
This contains the redeemed coupon code TRAC075
, and the id=9
of the user who has redeemed it (us). Although not strictly necessary for this challenge, we will also dump the user_login
table for interesting stuff.
The table contains JWT tokens and Blowfish password hashes.
As we have said, the applied_coupon
table contains a coupon code TRAC075
which has already been redeemed by user id=9
, which is our user. The challenge is asking us to modify the database using SQL injections so we can redeem the same coupon again. We could modify the database in several ways to meet the challenge, but probably the less noisy option would be to just delete the row associated to our user in the database.
The payload for this injection would be.
Although the API replies with a 500 server error status, the command is successfully applied by PostgreSQL. The way the API replies depends on how the backend has been programmed to respond in each case, but it does not prevent the database to be updated.
Let's verify the database has been successfully updated by trying to apply the coupon again.
We see the coupon is applied again, so we have successfully modified PostgreSQL by means of a SQL injection to reuse a coupon.
Another way to modify the database could be to update the ID of the user who has redeemed the coupon. In this case the payload would be.
Again, the server replies with code 500; however, we can verify the coupon can be redeemed again as we did in the previous injection.
Enumerate the application to find the endpoint that does not perform authentication. This is /workshop/api/shop/orders/1
where, in fact, the application is disclosing critical information such as credit card details and private data without user authentication.
Intercept the traffic during the login process and capture a token.
The captured token is the following.
You can inspect it at https://jwt.io or with jwt_tool
, which also scans the token for vulnerabilities.
The tool reports a vulnerability called "alg" : "none"
, which means crAPI is actually not checking the signature of the JWT tokens. Therefore, we can forge our own JWTs to login, the application will not check its signature at all.
To forge the JWT we need to install the "JWT editor" extension in Burpsuite. Once the extension is installed, capture a login attempt and send to repeater. Then click on the "JWT editor" tab to modify the token as you want. The crAPI application will just accept it as is, since no signature is checked.
For example, we will create a token for the admin user. If you remember from the SQLi chapter, we have admin login email admin@example.com
. Add the email in the token "Payload" section, and click on Attack -> None signing algorithm, finally, click on "Attack". The token will be used to successfully get authorization and the application will send a response containing the admin data.
From challenges 5 and 10 we know the application assigns uploaded videos a property containing details to be used when the videos are compressed and shared with the community. If you notice, the conversion parameters look like arguments for a binary executable. Therefore, since we can manipulate this parameter, it could be an entry point for command injection.
Following the same procedure as in challenge 10 we insert a reverse shell command in the conversion_params
parameter.
Now we have to find a way to trigger the video conversion. This is done by clicking on "Share video with community", in the user dashboard video section. However, if we inspect the HTTP response when we start the conversion, a message appears.
This definitely points to SSRF. If we review our notes, we exploited SSRF in challenge 11 by abusing the contact mechanic endpoint. So let's open another mechanic case, intercept the capture and add a call to the internal endpoint. Do not forget to add the video ID.
Add the internal endpoint, start a listener on port 1919, send the request and... BOOM, a reverse shell is receive... Oh, wait! another message appears.
This is a base64 encoded message, let's decode it.
It seems they disabled command injections to avoid people abusing SSRF and command injection in the web-based crAPI interface, which sounds quite understandable. However, we are hosting our own version of crAPI so it seems this is not yet over for us.
I found several occurrences of this ENABLE_SHELL_INJECTION
flags in the source code. The following is a table containing all the flags I had to modify.
After editing these config files, just restart crAPI and trigger again the conversion process. This time the API replies with a status code 200 and it does not contain the message, meaning the conversion process has successfully started with the command injected.
We successfully meet the 15 challenges proposed by OWASP and found and documented top ten API vulnerabilities in the crAPI application. Furthermore, we found a way to inject commands which potentially could lead to a reverse shell in the host.