Message to Garcia
Encryption and Key Management Challenge. - by melmols
Summary
Summary
In Message to Garcia we begin by enumerating a target hosting a web service on ports 80 and 5000, alongside SSH. The web application exposes multiple endpoints, including /fetch, /backup, and /status. The /fetch endpoint accepts user-supplied URLs without validation, enabling local file inclusion via file://. Using this, we access sensitive files such as /proc/self/environ, revealing the service context, and /proc/self/cmdline, confirming that the Flask app runs as app.py. Further traversal exposes the full source of app.py, its helper functions.py, and the success.html flag page.
Static analysis of functions.py reveals a hardcoded Fernet key and an expected plaintext message required to validate uploaded .enc or .gpg files. Using these, we craft a valid encrypted payload locally by replicating the backend encryption logic. After encrypting the known message with the discovered key, we upload message.enc through the web interface, triggering the application’s success condition. The server validates our ciphertext, sets the challenge token, and reveals the final flag.
Recon
We use rustscan -b 500 -a 10.81.183.107 -- -sC -sV -Pn to enumerate all TCP ports on the target machine, piping the discovered results into Nmap which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.
A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

Among the open ports, we have ports 22 serving SSH , 80, and 5000 serving a web server.

We visit the web site on port 80 and receive an introduction. We have to find and exploit vulnerabilities in the web application in order to access sensitive data on the server. With the information we obtain, we can then solve the cryptographic challenge.

In addition to the main dashboard, we have a /fetch endpoint to integrate resources via http:// or https://, but also internal files via file://. This is operated here as an obvious feature that allows us to perform local file inclusion or server-side request forgery. Here we see our entry point to access sensitive information.
In addition to these, we also have the /backup and /status endpoints. We can upload files via the backup endpoint. However, this is not vulnerable to path traversal attacks which in turn would allow, for example, a manipulated authorized_keys file to be placed in the .ssh folder of a user to whom we have write access in order to gain interactive access to the machine via SSH.
At the status endpoint, we can start the SFTP server, or rather, a service is started on port 2222, which does not appear to be vulnerable.

LFI
We try a simple inclusion of the /etc/passwd file and are successful.

Interesting loot can usually be found in /proc/self/environ and /proc/self/cmdline. This is methodologically, the first thing to look for besides SSH keys.
The files /proc/self/environ and /proc/self/cmdline often contain valuable loot because they expose the running process’s environment variables and invocation arguments, which frequently leak credentials, API keys, secrets, or internal paths passed at runtime.
From /proc/self/environ, we can determine the current user in the context:

In this case, /proc/self/cmdline reveals that the service is executed as python3 app.py, confirming that the application is a Python script and giving us a clear starting point for locating the source code and understanding the execution context.

We assume that app.py is running in the home directory of sftp-msg2g4arc1a and try to include it. Bingo, we have the source but no secret message or key yet.

Among the many imports, we also have a custom import of functions. This should also be found in the home directory. We include it as follows. We are successful and find the information needed to answer the first questions of the challenge. Including all information needed to obtain the flag, but more on that later.

From app.py, we also find he success.html, which is shown when the challenge is solved. This will probably reveal the flag.
Flask stores its rendered templates on disk under the application’s templates/ directory. So we can retrieve the success.html from templates/success.html. And we have the shortcut to the flag. The intended path will be described further below.

Bonus SSRFMap showcase
Before we continue, here's a quick bonus: the automated check of LFI and SSRF can be done via SSRFMap. SSRFmap takes a Burp request file as input and a parameter to fuzz. The manual methodological approach is also coverded.
We intercept the /fetch reuquest and store it to a file called request.txt.

We run it like the following, here we pass the intercepted request, the parameter to check - in this case url and also define the modes. We try to read file here and perform an enumeration of the available internal services via a portscan.

The manual methodological approach is also coverded.

Source Code Analysis
We continue with the analysis of app.py.
The app.py is the main Flask application that defines all routes, logic, flow for the web service we face on port 80 / 5000. It handles the encrypted file validation, uploads, a vulnerable SSRF fetch endpoint, management of a sftp service, and a success page where the flag may be revealed.
The main / route simply renders the homepage and acts as the entry point for the challenge. It provides access to the upload, backup, fetch, and service control functionality.
This /upload endpoint accepts encrypted .gpg or .enc files, briefly stores them on disk, validates their encrypted contents, and deletes them immediately after processing. If the validation succeeds, a session token is set and the user is redirected to the success page
The /backup endpoint implements a upload functionality, but strictly sanitizes filenames using secure_filename and enforces path checks to ensure uploads remain inside the designated directory. As a result, path traversal is effectively prevented, despite the comment suggesting vulnerability.
The /fetch endpoint fetches user-supplied URLs without proper validation, allowing SSRF, including local file reads.
This functions.py validates uploaded .gpg or .enc files by decrypting their contents with a fixed Fernet key and checking whether the plaintext exactly matches a predefined secret message. If decryption fails or the message differs even slightly, the file is rejected as invalid.
This file ultimately reveals all the information needed to answer the first questions. The first thing we notice is that Fernet was used as the encryption algorithm. After a quick search, we determine that this is a symmetric encryption algorithm. We can see the key used directly in the sixth line. In the ninth tent, we also see the expected message.
Obtain Flag - The Indented Way
All we have to do now is slightly modify the script to write the message.enc to our system during execution. And then upload the resulting message.enc.
We generate the encrypted message...

... and submit it to the panel. We retrieve the flag.

Last updated
Was this helpful?