Skip to content

Instantly share code, notes, and snippets.

@scarf1nger
Last active January 3, 2026 09:33
Show Gist options
  • Select an option

  • Save scarf1nger/4cd862ec3afd243952886d178231cd77 to your computer and use it in GitHub Desktop.

Select an option

Save scarf1nger/4cd862ec3afd243952886d178231cd77 to your computer and use it in GitHub Desktop.
SantaCloud CTF challenge by Intigriti

Introduction

This write-up presents a possible approach for solving the SantaCloud CTF challenge by Intigriti.

If you prefer to save time and have a quick summary of the proposed solution, feel free to skip ahead to Steps to capture the flag.

About the challenge

challenge-rules

The challenge rules state that the solution should require no bruteforcing. This suggests that aggressive fuzzing or blind guessing are not needed. The tip published later by Intigriti confirms this line of reasoning.

intigriti-tip

Initial reconnaissance

We start by examining the main page of the challenge server at https://santacloud.intigriti.io/home. Nothing more than a landing page that allows users to sign in to the portal.

/home

By inspecting the HTML code of /home we notice that, after a successful login, the user is redirected to /dashboard. We can try to directly access the dashboard: indeed, the page is briefly rendered before redirecting the user to the login form.

The behavior observed for the /dashboard endpoint turns out to be consistent throughout the portal, meaning that we can map the application and inspect the HTML code of every page without being authenticated.

We end up with the following site map:

  • /home
  • /login
  • /dashboard - Dashboard Overview
  • /map - Global Delivery Map
  • /inventory - Inventory Management
  • /profile - User Profile
  • /notes - Internal Notes

In addition, we notice that the application performs some API requests, such as:

  • GET /api/gifts
  • GET /api/notes
  • GET /api/notes/{noteId}
  • POST /api/notes
  • PUT /api/notes/{noteId}
  • DELETE /api/notes/{noteId}

Unfortunately, all these APIs are protected by a Bearer token issued upon login, and any attempt to call them results in the following error message: "Invalid or expired token".

Finally, the HTML code of /map reveals that the /api/gifts endpoint accepts user_id=1 as a query parameter. Maybe something that could be of use later on.

...
// Try to fetch all users' gifts for better map visualization
// This will work if there's an endpoint or parameter
const allResponse = await fetch('/api/gifts?user_id=1', {
...

Credential exposure

Another valuable reconnaissance step is reviewing web server metadata files for potential information leakage. With this in mind, we inspect the content of the /robots.txt file.

User-agent: *
Allow: /

# Disallow indexing of sensitive config files
Disallow: /package.json
Disallow: /backup.json
Disallow: /artisan
Disallow: /.env
Disallow: /.env.local
Disallow: /composer.json
Disallow: /composer.json*
Disallow: /composer.json~

Out of the listed "sensitive config files", only /composer.json~ turns out to be accessible.

composer.json~

Interestingly, this file contains the credential of the elf_supervisor user and... the flag!

Wait... remember the challenge rules: we must find a flag in the format INTIGRITI{.*}. So, that secret contains only a part of the flag.

We need to dig further...

Unauthorized access to private notes

We sign in as the elf_supervisor user and start exploring the application, playing around with all the available functionalities. Unfortunately, no trace of the flag.

The "User Profile" page tells us that our "User ID" is 2.

/profile

Something rings a bell and we review the notes taken during the initial reconnaissance: the /api/gifts endpoint accepts user_id=1 as a query parameter, so the identifier of another user. Performing such an API request effectively returns the gifts of the admin user. However, the gift we were hoping to receive is not among them.

Most importantly, this indicates a lack of proper authorization checks. Maybe other APIs are affected by the same issue.

Indeed, this is the case for the /api/notes endpoint. This API is called by the application while loading the "Internal Notes" page from the user profile. By intercepting this request and adding user_id=1 as a query parameter, we can access the private notes of the admin user.

flag

And here it is! the "Secret Key" note contains the flag we need to solve this challenge: INTIGRITI{019b118e-e563-7348-a377-c1e5f944bb46}.

Conclusion

This challenge was about identifying exposed information through careful reconnaissance, highlighting how valuable this phase is. By inspecting the content of the /robots.txt file, we were ultimately able to obtain a valid user credential for the portal. In addition, HTML code review gave us clues that allowed to identify and exploit an authorization flaw to retrieve the flag.

Steps to capture the flag

  1. Inspect the content of the /robots.txt file. Focus on the line Disallow: /composer.json~.
  2. Use the credential exposed within the /composer.json~ file to sign in as the elf_supervisor user.
  3. Navigate to /notes with Burp Intercept on.
  4. Intercept the request to the /api/notes endpoint and add user_id=1 as a query parameter.
  5. The "Secret Key" note contains the flag: INTIGRITI{019b118e-e563-7348-a377-c1e5f944bb46}.

Comments are disabled for this gist.