## Challenge Introduction
Welcome to this walkthrough of Intigriti's Challenge 1025! We're dealing with a web application called "Shoppix Fashion Importer" that allows users to fetch product images from URLs. Let's see how we can exploit this to read arbitrary files and capture the flag.
**Target:** https://challenge-1025.intigriti.io/challenge.php
## Step 1: Initial Reconnaissance
First, let's visit the challenge page to understand what we're working with. We see a simple form that asks for an image URL and has a "Fetch Resource" button.
Looking at the page source, we can see it's a PHP application that takes a `url` parameter via GET request. The form submits to the same page with the URL parameter.
## Step 2: Understanding the Vulnerability
Now, let's analyze the PHP code to understand how the application works:
```php
if (isset($_GET['url'])) {
$url = $_GET['url'];
if (stripos($url, 'http') === false) {
die("<p style='color:#ff5252'>Invalid URL: must include 'http'</p>");
}
if (stripos($stripos($url, '127.0.0.1') !== false || stripos($url, 'localhost') !== false) {
die("<p style='color:#ff5252'>Invalid URL: access to localhost is not allowed</p>");
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
// ... rest of the code
}
```
Here's what the application does:
1. **First**, it checks if the URL contains the string "http" anywhere
2. **Then**, it checks if the URL contains "127.0.0.1" or "localhost"
3. **Finally**, it uses cURL to fetch the URL and displays the response
## Step 3: Identifying the Flaw
The vulnerability is in the validation logic. The application uses `stripos()` which only checks if a substring exists anywhere in the string. This means:
- It doesn't validate the actual URL scheme
- It doesn't properly parse the URL structure
- URL fragments (`#...`) can contain "http" and bypass the check
## Step 4: Crafting the Bypass
Now, let's think about how to bypass this validation. We need a URL that:
1. Contains "http" somewhere (to pass the first check)
2. Doesn't contain "127.0.0.1" or "localhost" (to pass the second check)
3. Actually reads a local file (to exploit the SSRF)
The solution is to use a `file://` URL with a fragment containing "http":
```
file:///path/to/file#http
```
This works because:
- The URL scheme is `file://` (not `http://`)
- The fragment `#http` satisfies the "must include http" check
- No localhost references in the URL
- cURL processes the `file://` scheme and reads local files
## Step 5: Testing the Bypass
Let's test our bypass with a simple file first. We'll try to read `/etc/passwd`:
```
https://challenge-1025.intigriti.io/challenge.php?url=file%3A%2F%2F%2Fetc%2Fpasswd%23http
```
URL-encoded, this becomes:
- `file://` → `file%3A%2F%2F`
- `#http` → `%23http`
If this works, we should see the contents of `/etc/passwd` in the "Fetched content" section.
## Step 6: Gathering System Information
Once we confirm the bypass works, let's gather more information about the system:
**First**, let's read the challenge.php source code:
```
file:///proc/self/cwd/challenge.php#http
```
**Then**, let's check the environment variables:
```
file:///proc/self/environ#http
```
**Next**, let's see what filesystems are mounted:
```
file:///proc/self/mounts#http
```
This information helps us understand the system structure and locate potential flag files.
## Step 7: Locating the Flag
Now comes the tricky part - finding the flag. Based on the system information we gathered, we need to search common locations where flags might be stored.
**First**, let's try common flag locations:
- `/flag`
- `/flag.txt`
- `/var/www/html/flag`
- `/root/flag`
**Then**, let's check the current working directory:
```
file:///proc/self/cwd/#http
```
**Next**, let's look in the root directory:
```
file:///proc/self/root/#http
```
## Step 8: The Discovery
After trying various paths, we discover the flag is located at:
```
/proc/self/root/93e892fe-c0af-44a1-9308-5a58548abd98.txt
```
This appears to be a randomly named file in the root directory. Let's fetch it:
```
https://challenge-1025.intigriti.io/challenge.php?url=file%3A%2F%2F%2Fproc%2Fself%2Froot%2F93e892fe-c0af-44a1-9308-5a58548abd98.txt%23http
```
## Step 9: Capturing the Flag
When we submit this URL, the application fetches the file content and displays it in the "Fetched content" section. We should see something like:
```
INTIGRITI{...}
```
**Congratulations!** We've successfully captured the flag.
## Step 10: Understanding the Impact
Let's reflect on what we've accomplished:
1. **We bypassed** naive URL validation using URL fragments
2. **We read arbitrary files** on the server using the `file://` scheme
3. **We accessed sensitive information** like system files and environment variables
4. **We located and captured** the challenge flag
This vulnerability could be extended to:
- Reading application source code
- Accessing configuration files
- Reading environment variables with secrets
- Potentially accessing internal services (SSRF)
## Key Learning Points
1. **Never use substring validation** for security checks - always parse and validate properly
2. **URL fragments can be used** to bypass naive validation
3. **Always validate URL schemes** explicitly
4. **Consider all URL components** when implementing validation
5. **Use allowlists instead of blocklists** for better security
## Conclusion
This challenge demonstrated a classic SSRF vulnerability caused by insufficient input validation. The fragment bypass technique (`file://...#http`) is a well-known method to circumvent naive URL validation.
The key takeaway is that security validation must be comprehensive and consider all aspects of the input, including URL components that might be ignored by the application but processed by underlying libraries.
**Challenge completed successfully!** 🎉