Intigriti Challenge 1025 Writeup: From SSRF to RCE
From: @kjcao
Challenge Overview
This challenge presented a web application with an SSRF vulnerability. We could exploit this to read a local file containing the flag. However, the challenge had a deeper layer: we could achieve RCE through a hidden upload script.
Initial Reconnaissance and SSRF Exploitation
We are given the endpoint /challenge.php which accepts a URL via the url parameter and would fetch and display the contents of that URL.
We try to read a file on the server using the file:// protocol but it doesn't work. The webpage tells us that the URL must contain the string "http" in the URL.
We can bypass this check by including the string "http" in a non-significant part of the URL, such as the fragment identifier (the part after the #). We need to percent encode "#" as %23 otherwise the browser interprets the fragment identifier rather than the server. We test our payload:

And we can read files! In fact, by supplying a directory, the server responds with a list of the directory's contents. In general, the payload looks like this:
This URL instructs the server to read from the local filesystem (file:///) and the %23http (percent encoded for #http) satisfies the string check without interfering with the file protocol. Using the payload, we obtain a listing of the root directory. The server's response reveals all top-level directories and, importantly, a text file:
Now, we can simply use our payload again to read this file directly. Final payload for the flag:
The server's response contained the flag:
INTIGRITI{ngks896sdjvsjnv6383utbgn}
Escalation to Remote Code Execution (RCE)
The challenge hinted at a more significant vulnerability. Next, we will achieve RCE on the server. Using the same SSRF technique, we list the contents of the web root /var/www/html/:
The file upload_shoppix_images.php was immediately interesting, as upload functionality is a common vector for RCE. Let's take a look at the source code of upload_shoppix_images.php:
The script validates two things:
- The file's MIME type (using
mime_content_type) must start withimage/. - The filename must contain
.png,.jpg, or.jpeg.
If both checks pass, the file is moved to the uploads/ directory.
Unfortunately, when we try to directly access upload_shoppix_images.php it returns a 403 Forbidden error. I tried to see if there was an .htaccess file, but there wasn't. Further investigation of the Apache configuration (/etc/apache2/sites-enabled/000-default.conf) via SSRF revealed the reason:
Access to the upload script is restricted to requests that contain a custom HTTP header: is-shoppix-admin: true. We can upload files now, so let's create a malicious file that starts with a JPEG signature but contains PHP code.
Now, we upload the malicious file:
curl "https://challenge-1025.intigriti.io/upload_shoppix_images.php" \
-F "[email protected]" \
-H "is-shoppix-admin: true"
|
The server responded successfully, confirming the file was uploaded to /uploads/shell.jpg.php. From here, we can run arbitrary commands, e.g.
Yay!
Conclusion
In conclusion, this is what we did:
- A simple string check was bypassed to read arbitrary files.
- This was used to leak source code & config files.
- An Apache directive was identified and bypassed with a custom header.
- Lax filename and MIME type validation allowed a malicious PHP file to be uploaded and executed, leading to RCE.