Apiculture 2 write-up

The Apiculture challenges are dedicated to API attacks. The second level basically looks like a webpage dedicated to beehives:

A quick look in the Developer Tools reveals a call to the /api/v4/products/ endpoint:

This endpoint indeed permits to get the beehives JSON. It is also impacted by an Improper Data Filtering vulnerability since it contains data [i.e. the vendorID field] that is not used by the Angular front-end to render the webpage in the browser:

Anyway, this leak is not very useful in that challenge. A most interesting finding is to notice that a Swagger file is available at /api/v4/swagger.json:

The /api/v4/reset endpoint sounds interesting, since it may permit to change someone else’s password:

Based on this Swagger, the /users/reset endpoint relies on reset1ModelInput and resetModelOutput schemas. We should therefore investigate the definitions to see what is expected by this interesting endpoint:

The /users/reset endpoint basically expects to receive a POSTed email string and will then respond with another message string. Let’s check:

It seems that this endpoint is not very verbose and can’t be leveraged to identify valid mailboxes:

Unfortunately for us, the password reset process relies on a secret link that is sent to the owner’s mailbox. Since none of them could apparently be attacked, we should find another approach.

The trick here was to notice that we are facing the 4th version of the API. And that there may be older versions, like staging or deprecated APIs. Indeed, a few attempts quickly reveal the existence of a v2 and its Swagger file is available at /api/v2/swagger.json:

Contrary to the production ecosystem, there is not a single /users/reset endpoint on this deprecated API. There are indeed 3 distinct reset endpoints:

The /users/reset1 endpoint expects a POSTed email string and apparently responds with an AES-256 cipher:

The /users/reset2 endpoint then expects to receive this AES-256 cipher through a string variable called aes256PayloadFromReset1. It also apparently expects a challengeResponse variable as an integer, and it will also respond with an AES-256 cipher:

Finally, the /users/reset3 endpoint expects to receive this AES-256 cipher through a string variable called aes256PayloadFromReset2. It also apparently expects to receive a string variable called sms4digits, and it will then respond with a string message that contains the new password:

So, the password reset feature on this old API only involves a 4 digits PIN presumably sent to the owner by SMS. This smells very good! We can’t brute force a secret link that is for example based on an UUID, but we can attack a 4 digits PIN… So, if both versions of the API do use the same database then we have a nice way to compromise an account in prod by attacking the deprecated API. This scenario is very indeed similar to the one that impacted Facebook in 2016, except that this /api/v2/reset endpoint also involves a captcha protection (as we will see soon).

We therefore need to identify a victim of choice and use his email address to hijack his account through this deprecated API. Contrary to the production interface, the reset endpoint on the deprecated API is quite verbose and we now have a way to verify if a target exists on the backend:

Thus, we should now focus on finding a target of choice. And it turns out that the robot.txt references a sitemap.xml file:

This sitemap permits to discover a file called giftcard.pdf in the marketing_legacy directory:

The PDF file itself is useless but Directory Indexing is allowed on the parent directory, which permits to discover other Office documents:

Both the DOCX and PPTX files do contain metadata which reveal the CEO’s name:

Some quick OSINT queries permit to quickly get information on “Winny BÄRENJUNGEN”:

The CEO is easily findable on LinkedIn, Twitter, Youtube, Copaindavant and Viadeo. We can quickly discover that the CEO’s email is winnybarenjungen@gmail.com:

Une image contenant texte, objet d’extérieur, nid d’abeille, capture d’écran

Description générée automatiquement

So now that we have identified a target of choice, let’s attack the forgotten API by starting on the /users/reset1 endpoint:

Une image contenant texte

Description générée automatiquement

As expected, we receive an aes256Payload (which is most probably used to host server-side secrets to provide some stateless functionalities) as well as a captcha to resolve through the challenge variable.

This captcha is a very simple mathematic challenge, so we can call the /users/reset2 endpoint and provide it both our answer and the AES-256 payload we received from the first endpoint:

Une image contenant texte, capture d’écran, moniteur

Description générée automatiquement

Unfortunately, such a manually crafted request is apparently too slow. So we need to automate that action to be faster:

Une image contenant texte

Description générée automatiquement

This works better, we don’t get any expired challenge issue:

Une image contenant texte

Description générée automatiquement

The last step is therefore to use the /users/reset3 endpoint to post that AES-256 cipher along with the 4-digits PIN that has been sent to the target. Let’s try to add a small brute forcing feature for that PIN:

Une image contenant texte

Description générée automatiquement

As there is no protection against brute forcing on the PIN, we can finally reset the CEO’s password in a few seconds and get our flag:

Une image contenant texte

Description générée automatiquement

Thanks for playing with Apiculture 2,

Frédéric BOURLA & Simon DENEL